##########################################################
# proTyres Classes Lib by Jens Roos (Wergilius at AC forum)
#
# 2.0.0 200529
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# For GNU General Public License see <http://www.gnu.org/licenses/>.
######################################################################

import ac
from acsys import CS
from os import path,listdir
from configparser import ConfigParser
from bisect import bisect_left
from lib.proTyres_def import *
from lib.proTyres_util import *
from lib.proTyres_dsp import changeModeTyre, changeModeIndicator

#size and layout settings for eol table
class eolTable:
  def __init__(self):
    self.Km = []              #array of km
    self.Grip = []            #array of estimated grip       
    self.ActualGrip =[]       #return teoretical grip from driven km
    self.DynGain = []         #km gain calculated with actual grip as reference
    self.DynGainBuff = []     #buffert, init in newtyre as well
    self.DynGainNew = []      #if the gain settings is new
    self.Levels = []          #arrays of eol levels in km [zero,low,mid,high]
    self.DynGainIdx = 0       #actual position in eol buffert
    self.DynGainP = DYNGAIN_FAST  #gain factor for autotune eol gain

class gripTable:
  def __init__(self):
    Temp = []                 #returns actual grip, with temp as argument
    MaxTemp = 0               #optimal temp, from tcurve.lut
    MinTemp = 0

class gripLevels:
  def __init__(self):
    Temp = []                 #temp levels
    Grip = []                 #grip levels

#define basic paths and parser
class setupINI:
  def __init__(self):
    #config file
    self.configPath = "apps/python/proTyres/config.ini"
    self.config = ConfigParser()
    self.configUpdate = False
    self.configValid = False

    #background images
    self.Background = "apps/python/proTyres/img/background"

    #ini and lut file settings
    self.tyrePath = "apps/python/proTyres/"
    self.tyreThermalFileF = "_not_set_"
    self.tyreThermalFileR = "_not_set_"
    self.tyreEOLFileF = "_not_set_"
    self.tyreEOLFileR = "_not_set_"
    self.tyre = ConfigParser(empty_lines_in_values=False, inline_comment_prefixes=(";",))

    #eol settings
    self.eolPath = "apps/python/proTyres/lib/proTyres.ini"
    self.eol = ConfigParser()
    self.eolUpdate = False
    self.cfgUpdate = False
    self.cfgOffsetUpdate = False
    
#size and layout settings
class setupApp:
  def __init__(self,s,f):
    self.MainBackground = "Standard"              #Background image
    self.DisplayMode = ["Standard","Standard","Extend","Standard","Extend","None"] #display mode, current, other, practice, qualify, race, disabled
    self.TopTyreMode = False                      #Display all temp boxes as Top Tyre
    self.CenterBar = False                        #draw bars from center of app
    self.DirtyBarTyre = False                     #if true, dirty bar is in tyre space instead of wear bar
    self.WearMasked = True                        #mask wear during warmup
    self.WearMarker = "Standard"                  #type of wear markers, standard black or color
    self.showNew = True                           #show new tyre marker in tyre indicator
    self.showTempBar = "StandardRing"             #show temp bar in tyre indicator, None or StandardRing (average)
    self.AvgPeak = False                          #Show peak temp in avg box if IMO overheats
    self.PressureTU = "psi"                       #technical units for pressure
    self.PressureBar = ["Auto","Auto","Auto"]     #Use and scale of pressure bar Auto, Scale6, Scale10, Scale16 for current, standard and Compact display modes
    self.PressureLabel = ["Standard","Standard","Standard","Compact","Compact"]  #Layout of pressure labels, current, other, practice, qualify, race
    self.PressureBuff = 5                         #rolling buffert for pressure diff, in seconds
    self.PressureBuffIdx = 0                      #actual index in buffert
    self.PressureOffset = 5.0                     #range for the saved offset value
    self.PressureFallback = "TempOnly"            #fallback to closest temp if offsets are used, None (False), TempOnly (True), Track, TrackTemp or TrackOnly
    self.useOptFont = False                       #use optionally font
    self.Font = "Roboto',0,0"                      #optionally font string
    self.DisableButton = "None"                   #disable buttons
    self.bwButton = False                         #use black & white buttons
    self.PitBoard = ["None","None","NewTyre","NewTyre","PitLane"]  #pitboard compound summary behavior
    self.PitBoardClose = 5                        #nbr of seconds to wait before close pitboard
    self.PitBoardOpen = "False"                   #info button open pitboard
    self.PitBoardExtra = False                    #show extra tyre info on pitboard
    self.logVerbose = "Standard"                  #use of verbose logging, None (False), Standard (True), Extend
    self.eolMarker = "Color"                      #type of wear markers, standard black or color
    self.showEOLinfo = False                      #default status for eol info
    self.showEOLgain = False                      #show eol dynamic gain with the info labels
    self.configMenu = "Close"                     #behavior of config menu, auto, close or manual
    self.useRadius = "True"                       #radius behavior on tyrebox graphics, None, Fixed or Scaled
    self.sizeRadius = 2                           #radius size, in px
    self.resRadius = 1                            #resultion of radius, in number of vertex arrays
    self.Scale = s                                #overal scaling
    self.MainWidth = int(166*s)
    self.MainHeight = int(144*s)
    self.MainCmpWidth = int(112*s)
    self.MainCmpHeight = int(114*s)
    self.DebugWidth = int(495*s)
    self.DebugHeight = int(171*s)
    self.MainOpacity = 0.7                        #Opacity for no background used, 0 when bg is used
    self.Height = int(52*s)                       #height of each indicator
    self.BarWidth = int(8*s)
    self.DirtyWidth = int(4*s)
    self.TyreIndicatorWidth = int(9*s)            #size if each tyre segment
    self.TyreIndicatorHeight = int(8*s)
    self.AuxIndicatorHeight = int(8*s)
    self.IdealPressureRange = 2.20                #totalrange for marker of target pressure, should result in even px
    self.PressureColorR = [0,0.00,0,0.2,1]        #colors RGBA, psi from optimal [low, target start, optimal, target end, high]
    self.PressureColorG = [0,0.80,1,0.8,0]
    self.PressureColorB = [1,0.20,0,0.0,0]
    self.PressureColorA = [1,1,1,1,1]              
    self.PressureMarkerColor = [0,0,0,0.8]        #color of range marker, pressue, wear and eol
    self.PressureColorPit = [[0,0.5,1,1],[0,1,0,1],[1,0.25,0.25,1]] #pressure color pitboard, low, correct, high, RGBA
    self.PressureGain = 1.0                       #pressure gain, 1.0 for PSI
    self.PressureFormat ="{:.1f}"                 #format for pressure
    self.DirtyStep = 10                           #range of 0-100% of dirt, max value in game is 5.0
    self.DirtyColor = [0.0,0.0,0.0,0.5]           #color of dirt bar, RGBA
    self.WearLevels = [0.94,0.96,0.98,1.0]        #arrays of wear levels [zero,high,mid,low]
    self.WearExtLow = False                       #extend wear levels to [0.91,0.96,0.98,1.0]
    self.WearColor = [[1,0,0,1],[1,1,0,1],[0,1,0,1]]  #wear colors [high, mid, low]
    self.WearMarkerColor = [[1,0,0,0.8],[0.941,0.729,0,1],[0,0,0,0.5]]  #color of wear indicator markers low, mid, standard
    self.TempStep = 2.0                           #grip level from/above max (normaly 100), blue/red above this
    self.TempColorR = [0,0,1]                     #color for temp and grip levels [low temp, optimal temp, high temp]
    self.TempColorG = [0,1,0]                     
    self.TempColorB = [1,0,0]
    self.TempColorA = [1,1,1]
    self.showBrake = "Delayed"                    #use break slip indicator, None, Direct or Delayed
    self.BrakePedal = 0.1                         #brake pedal value to start to check for slip
    self.BrakeTimer = 1.0                        #indicator delay time in s, if used
    self.BrakeLevels = [[2.5,10.0,20000.0],"Off","High","High","Medium"]  #arrays of brake slip levels [low,mid,high]
    self.BrakeColor = [[1,1,0,0.8],[1,0.5,0,0.8],[1,0,0,0.8]]  #brake colors [low, mid, high]
    self.useEOL = True                            #use estimated end of life system
    self.eolWidth = int(10*s)                     #extra width for eol indicator
    self.eolColor = [[1,0,0,1],[1,1,0,1],[0,1,0,1]]  #eol colors [short, mid, long]
    self.eolLevels = [94.5,95.0,96.0,97.0,98.0,99.0,99.5]   #index levels for non linear gain
    self.BorderColor = [1,1,1,0.8]                #color of bar border
    self.AppTextColor = [1,1,1,1]                 #color of all text
    self.BorderSize = 1
    self.FontSize = max(int(10*s*f),10)           #font size
    self.BttnSize = int(10*s)                     #size of button
    self.FontSpace = int(3*s)                     #spacing between indicator and text
    self.SpacingX = int(8*s)                      #spacing of indicators X (is hard set in 2 functions)
    self.SpacingY = int(8*s)                      #spacing of indicators Y
    self.SpacingBar = int(4*s)                    #spacing between temp and bars inside each indicator
    self.Width = self.TyreIndicatorWidth*3 + self.BarWidth*2 + self.SpacingBar*2
  
#parameters for selected tyre, is read from AC, ini and lut files  
class setupTyre:
  def __init__(self,appWindow,cfg,setup,ptv):
    self.Car = ""                       #name of car (same as folder)
    self.Track = ""                     #identification of track type
    self.isValid = False                #values has been validated
    self.isNew = False                  #tyre has been set as new
    self.isInPit = False                #car is in for pitstop (extended detection)
    self.isInPitBox = False             #car is in pitbox (extendend detection)
    self.isInPitLane = False            #car is in pitlane (extended detection)
    self.isNonPitLaneTrack = False      #track has no pitlane  
    self.PosPit = [0,0,0]               #pitbox cordinates, x,y,z
    self.PosSession = [0,0,0]           #session start position
    self.PosOld = [0,0,0]               #previous cordinates, x,y,z
    self.sessionStats = []              #session stats, km, laps and wear
    self.startSession = True            #if session has started (to detect new tyres)
    self.oldSession = -2                #previous session (to detect new tyres)
    self.lblPitBoardIdx = 2             #current number of pitboard labels
    self.lblPitBoardLeft = 0            #left pitboard cordinates
    self.CompoundShort = ""             #compund, short
    self.CompoundOld = "-"
    self.ExtInit = False                #_EXTENSION parameter init done
    self.RatioTempSurface = 0.25        #PRACTICAL_TEMP_RATIO
    self.RatioTempCore = 0.75
    self.IdealPressure = [0.0,0.0]      #ideal pressure, from tyres.ini
    self.OffsetPressure = [0.0,0.0]     #offset from ideal pressure 
    self.SavedOffset = [0.0,0.0]        #saved offset from ideal pressure
    self.SavedRoadTemp = 0              #saved offset road temp
    self.Grip = [gripTable(),0]         #Grip, min and max temp array
    self.GripFR = False                 #differential front/read performance curves
    self.ActualPressureColor = []       #returns actual pressure color, with actual pressure as argument
    self.ActualTempColor = []           #returns actual temp color, with grip as argument
    self.WearValue = 500                #wear value for new tyres
    self.eolOffset = 0                  #travel distance offset, in m
    self.eolLapsOffset = 0              #laps offset
    self.eol = [0,0]                    #array of km, estimated grip and gain
    self.eolFR = False                  #differential front/reat wear curves
    self.eolIdx = 0                     #index of eol wear with the highest wear/km ratio (for eol display)
    self.eolKmOld = 0                   #previous eol km reading                  
    self.eolLvlAdj = [10.0,7.0,5.0,2.0] #arrays of adjusted eol levels in km [zero,low,mid,high]
    self.eolBorder = iBox()             #qlQuad position and max size, value is % of size
    self.eolBar = iBox()           
    self.eolMarker = iBoxMi()

    #init err variable
    err=0

    for i in range(1,6):
      #check for nowear/wear option and adjust string
      nw=cfg.DisplayMode[i].find("nowear")
      if nw!=-1:
        cfg.DisplayMode[i] = cfg.DisplayMode[i][:nw]
        nw = NOWEAR
      else:
        nw = NONE
      w=cfg.DisplayMode[i].find("wear")
      if w!=-1:
        cfg.DisplayMode[i] = cfg.DisplayMode[i][:w]
        w = WEAR
      else:
        w = NONE
      #wear is default on if nothing is selected for display modes
      if nw==NONE and w==NONE and i<5:
        w = WEAR
      #make sure we don't set both
      if nw==NOWEAR and w==WEAR:
        nw = NONE
        err=11+i*100
        setup.configUpdate = True

      #check display mode and size
      if (cfg.DisplayMode[i]=="compact" or cfg.DisplayMode[i]=="compactstandard") and i<5:
        cfg.DisplayMode[i] = COMPACT+STANDARD+w
        if nw:
          setup.configUpdate = True
          err=1+i*100
      elif cfg.DisplayMode[i]=="extend" and i<5:
        cfg.DisplayMode[i] = EXTEND+w+nw
      elif cfg.DisplayMode[i]=="compactextend" and i<5:
        cfg.DisplayMode[i] = COMPACT+EXTEND+w+nw
      elif cfg.DisplayMode[i]=="standard" and i==5:
        cfg.DisplayMode[i] = STANDARD+w+nw
      elif cfg.DisplayMode[i]=="extend" and i==5:
        cfg.DisplayMode[i] = EXTEND+w+nw
      elif cfg.DisplayMode[i]=="compact" and i==5:
        cfg.DisplayMode[i] = COMPACT+w+nw
      elif i<5:
        cfg.DisplayMode[i] = STANDARD+w
        if nw:
          setup.configUpdate = True
          err=2+i*100
      else:
        cfg.DisplayMode[i] = NONE+w+nw

      #force standard if eol is not used
      if cfg.DisplayMode[i]&EXTEND and not cfg.useEOL and i<5:
        #change to corresponding mode
        if not cfg.DisplayMode[i]&COMPACT:
          cfg.DisplayMode[i] = STANDARD+WEAR
        else:
          cfg.DisplayMode[i] = COMPACT+STANDARD+WEAR
        setup.configUpdate = True
        err=3+i*100

    for i in range(1,5):
      #check for conflict with non eol, remove standard or compact disabled of menus 
      if not cfg.useEOL and cfg.DisplayMode[i]&STANDARD and cfg.DisplayMode[5]&STANDARD:
        cfg.DisplayMode[5]-=STANDARD
        setup.configUpdate = True
        err=4+i*100
      if not cfg.useEOL and cfg.DisplayMode[i]&COMPACT and cfg.DisplayMode[5]&COMPACT:
        cfg.DisplayMode[5]-=COMPACT
        setup.configUpdate = True
        err=5+i*100
      #check for disabled display modes
      if cfg.DisplayMode[i]&COMPACT and cfg.DisplayMode[5]&COMPACT:
        cfg.DisplayMode[i] -= COMPACT
        setup.configUpdate = True
        err=6+i*100
      elif cfg.DisplayMode[i]&EXTEND and cfg.DisplayMode[5]&EXTEND:
        cfg.DisplayMode[i] -= EXTEND-STANDARD
        if cfg.DisplayMode[i]&NOWEAR:
          cfg.DisplayMode[i] -= NOWEAR-WEAR
        setup.configUpdate = True
        err=7+i*100
      elif cfg.DisplayMode[i]&STANDARD and cfg.DisplayMode[5]&STANDARD:
        cfg.DisplayMode[i] -= STANDARD-EXTEND
        setup.configUpdate = True
        err=8+i*100
      #check for any wear conflict
      if cfg.DisplayMode[i]&WEAR+EXTEND==WEAR+EXTEND and cfg.DisplayMode[5]&WEAR:
        cfg.DisplayMode[i] -= WEAR-NOWEAR
        setup.configUpdate = True
        err=9+i*100
      elif cfg.DisplayMode[i]&NOWEAR+EXTEND==NOWEAR+EXTEND and cfg.DisplayMode[5]&NOWEAR:
        cfg.DisplayMode[i] -= NOWEAR-WEAR
        setup.configUpdate = True
        err=10+i*100

    #set default display mode
    cfg.DisplayMode[0] = cfg.DisplayMode[1]

    #check background used
    if cfg.MainBackground=="plain":
      cfg.MainBackground = PLAIN
    elif cfg.MainBackground=="none":
      cfg.MainBackground = NONE      
    else:
      cfg.MainBackground = STANDARD

    #check wear marker
    if cfg.WearMarker=="none":
      cfg.WearMarker = NONE
    elif cfg.WearMarker=="color":
      cfg.WearMarker = COLOR
    else:
      cfg.WearMarker = STANDARD

    #check if extended low range is used
    if cfg.WearExtLow:
      cfg.WearLevels[0] = 0.91        

    #check eol marker
    if cfg.eolMarker=="none":
      cfg.eolMarker = NONE
    elif cfg.eolMarker=="color":
      cfg.eolMarker = COLOR
    else:
      cfg.eolMarker = STANDARD

    #check no button selection
    i=0
    if cfg.DisableButton.find("display")!=-1:
      i += DISPLAY
    if cfg.DisableButton.find("config")!=-1:
      i += CONFIG    
    if cfg.DisableButton.find("info")!=-1:
      i += INFO      
    if cfg.DisableButton.find("all")!=-1:
      i = DISPLAY+CONFIG+INFO
    cfg.DisableButton = i
    
    #not using eol but wants config button
    if not cfg.useEOL and not cfg.DisableButton&CONFIG:
      cfg.DisableButton += CONFIG
      setup.configUpdate = True
      err=11
      
    #check config menu behavior
    if cfg.configMenu=="auto":
      cfg.configMenu = OPEN+CLOSE
    elif cfg.configMenu=="close":
      cfg.configMenu = CLOSE
    else:
      cfg.configMenu = NONE

    #auto or close is enabled but all buttons i forced closed
    if (cfg.configMenu&OPEN or cfg.configMenu&CLOSE) and cfg.DisableButton&CONFIG:
      cfg.configMenu=NONE
      setup.configUpdate = True
      err=12

    #not using eol, block display mode but wants button (i.e no other display avalible)
    if not cfg.useEOL and (cfg.DisplayMode[5]&STANDARD or cfg.DisplayMode[5]&COMPACT) and not cfg.DisableButton&DISPLAY:
      cfg.DisableButton += DISPLAY
      setup.configUpdate = True
      err=13

    #check pitboard behavior
    for i in range(1,5):
      if cfg.PitBoard[i]=="pitlane":
        cfg.PitBoard[i] = PITLANE
      elif cfg.PitBoard[i]=="newcompound":
        cfg.PitBoard[i] = NEWCOMPOUND
      elif cfg.PitBoard[i]=="newtyre":
        cfg.PitBoard[i] = NEWTYRE
      else:
        cfg.PitBoard[i] = NONE

    #set default session pitboard
    cfg.PitBoard[0] = cfg.PitBoard[1]

    #check if info show pitboard
    if cfg.PitBoardOpen=="true":
      cfg.PitBoardOpen = PITLANE+INFO
    elif cfg.PitBoardOpen=="only":
      cfg.PitBoardOpen = PITLANE
    else:
      cfg.PitBoardOpen = INFO

    #check if true and info and no pitboard is active
    if sum(cfg.PitBoard)==0 and cfg.PitBoardOpen&PITLANE:
      cfg.PitBoardOpen = INFO
      setup.configUpdate = True
      err=16

    #check if show brake indicator 
    if cfg.showBrake=="direct":
      cfg.showBrake = DIRECT
    elif cfg.showBrake=="delayed":
      cfg.showBrake = DIRECT+DELAYED
    else:
      cfg.showBrake = NONE


    #check brake indicator sens
    for i in range(1,5):
      if cfg.BrakeLevels[i]=="low":
        cfg.BrakeLevels[i] = [10.0,50.0,200.0]
      elif cfg.BrakeLevels[i]=="medium":
        cfg.BrakeLevels[i] = [2.5,10.0,200.0]
      elif cfg.BrakeLevels[i]=="high":
        cfg.BrakeLevels[i] = [1.0,5.0,200.0]
      else:
        cfg.BrakeLevels[i] = [99999999.7,99999999.8,99999999.9]

    #default brake sens
    cfg.BrakeLevels[0] = cfg.BrakeLevels[1]

    #check for pressure scaling
    if cfg.PressureTU=="bar":
      cfg.PressureGain = 0.06894757     #pressure gain for psi to bar
      cfg.PressureTU = "bar"            #technical units for pressure
      cfg.PressureFormat ="{:.2f}"      #format for pressure
    elif cfg.PressureTU =="kpa":
      cfg.PressureGain = 6.894757       #pressure gain for psi to kPa
      cfg.PressureTU = "kPa"            #technical units for pressure
      cfg.PressureFormat ="{:.0f}"      #format for pressure 
    else:
      cfg.PressureTU = "psi" 

    #display mode of the pressure bar
    for i in range(1,3):
      if cfg.PressureBar[i]=="scale6":
        cfg.PressureBar[i] = SCALE6          
      elif cfg.PressureBar[i]=="scale10":
        cfg.PressureBar[i] = SCALE10          
      elif cfg.PressureBar[i]=="scale16":
        cfg.PressureBar[i] = SCALE16          
      else:
        cfg.PressureBar[i] = AUTO

    #set default bar mode
    if not cfg.DisplayMode[0]&COMPACT:
      cfg.PressureBar[0] = cfg.PressureBar[1]
    else:
      cfg.PressureBar[0] = cfg.PressureBar[2]

    for i in range(1,5):
      #displaymode of the pressure bar
      if cfg.PressureLabel[i]=="compact":
        cfg.PressureLabel[i] = COMPACT          
      else:
        cfg.PressureLabel[i] = STANDARD          

    #set default label mode
    cfg.PressureLabel[0] = cfg.PressureLabel[1]

    #calculate buffert, each second has 30 values index is updated each x10/s, if disabled (0s), use 1 buffert
    cfg.PressureBuff = min(max(cfg.PressureBuff,0),30)*10
    if cfg.PressureBuff==0:
      cfg.PressureBuff = 1

    #check config menu behavior
    if cfg.PressureFallback=="true" or cfg.PressureFallback=="temponly" :
      cfg.PressureFallback = TEMP
    elif cfg.PressureFallback=="track":
      cfg.PressureFallback = TRACK
    elif cfg.PressureFallback=="tracktemp":
      cfg.PressureFallback = TEMP+TRACK
    elif cfg.PressureFallback=="trackonly":
      cfg.PressureFallback = TEMP+TRACK+TRACK_ONLY      
    else:
      cfg.PressureFallback = NONE
    
    #Check if custom font settings is used
    if not cfg.useOptFont:
      cfg.Font = ["segoe ui",0,0] 
    else:
      #cfg.Font = [int(i) for i in cfg.Font.lstrip("[").rstrip("]").split(",")] for later use, do not work on mixed strings
      #convert mixed string with integers to array with name and settings
      o = cfg.Font.strip("[").strip("]").strip("'")
      cfg.Font = []
      cfg.Font.append(o[:o.find(",")-1])
      cfg.Font.append(int(o[o.find(",")+1:o.find(",",o.find(",")+1)-len(o)]))
      cfg.Font.append(int(o[len(o)-o[::-1].find(",")::]))

    #centerbar or non std font size will enforce plain background or none
    i=int(10*cfg.Scale-cfg.FontSize)
    if i!=0 and cfg.MainBackground==STANDARD:
      ptLog("proTyres: SCALE_FONT other than 1.0 is best used with PLAIN or NONE background",True)  
      setup.configUpdate = True
      err=14
    elif i!=0:
      cfg.MainWidth -= i*4
      cfg.MainHeight -= i*2
      cfg.DebugWidth -= i*48
      cfg.DebugHeight -= i*13
      if i>0:
        cfg.MainCmpHeight -=  i*2

    #check log behavior
    if cfg.logVerbose=="true" or cfg.logVerbose=="standard" :
      cfg.logVerbose = STANDARD
    elif cfg.logVerbose=="extend":
      cfg.logVerbose = STANDARD+EXTEND
    else:
      cfg.logVerbose = NONE

    #if not using eol, wearmasked is not possible
    if cfg.WearMasked and not cfg.useEOL:
      cfg.WearMasked = False
      setup.configUpdate = True
      err=15

    #new tyre indicator is only possible with eol      
    if cfg.showNew and not cfg.useEOL:
      cfg.showNew = False
      setup.configUpdate = True
      err=17

    #extra pitboard info is only possible with eol
    if cfg.PitBoardExtra and not cfg.useEOL:
      cfg.PitBoardExtra = False
      setup.configUpdate = True
      err=18

    #pitboard extra only possible with extend logging
    if cfg.PitBoardExtra and cfg.logVerbose&EXTEND!=EXTEND:
      cfg.PitBoardExtra = False
      setup.configUpdate = True
      err=19

    #for extend log, eol must be enabled
    if cfg.logVerbose&EXTEND and not cfg.useEOL:
      cfg.logVerbose = STANDARD  
      setup.configUpdate = True
      err=20
     
    #check temp bar
    if cfg.showTempBar=="standardring":
      cfg.showTempBar = True
    else:
      cfg.showTempBar = False

    #check dirtlevel color
    cfg.DirtyColor = [abs(float(i)) for i in cfg.DirtyColor.lstrip("[").rstrip("]").split(",")]
    if len(cfg.DirtyColor) != 4 or cfg.DirtyColor[0]>1.0 or cfg.DirtyColor[1]>1.0 or cfg.DirtyColor[2]>1.0 or cfg.DirtyColor[3]>1.0:
      cfg.DirtyColor = [0,0,0,0.5]
      setup.configUpdate = True
      err=21

    #check radious behavior and scaled if selected
    if cfg.useRadius=="true":
#      cfg.useRadius = True
#    elif cfg.useRadius=="scaled":
      cfg.useRadius = True
      cfg.sizeRadius = int(float(cfg.sizeRadius)*min(1.5,cfg.Scale))
      cfg.resRadius = int(float(cfg.resRadius)*min(1.5,cfg.Scale))
      cfg.TyreIndicatorHeight = int(10*cfg.Scale)
    else:
      cfg.useRadius = False

    #Displaysettings has changed, set correct in file as well
    if setup.configUpdate and setup.configValid:
      ptLog("",True)
      ptLog("proTyres: INFORMATION One or more ini settings are in conflict and was changed (ERROR "+str(err)+")",True)
      ptLog("",True)
      setup.configUpdate = False

    #reset opacity if non plain bg is used
    if cfg.MainBackground!=PLAIN:
      cfg.MainOpacity = 0
      
    #DirtyBar in tyre, enforce full size indicator
    if cfg.DirtyBarTyre:
      cfg.DirtyStep = 5.0 

    #get car used (equals folder name as well)
    self.Car = ac.getCarName(0)

    #check if car_extra folder is existing, will force use of ini & lut files
    if path.isdir(setup.tyrePath+"cars_extra/"+self.Car):
      setup.tyrePath += "cars_extra/"+self.Car+"/"
    else:
      #check if data.acd is availible in car folder, if not assume data folder exist
      if path.exists("content/cars/"+self.Car+"/data.acd"):
        setup.tyrePath = "content/cars/"+self.Car+"/"
      else:
        setup.tyrePath = "content/cars/"+self.Car+"/data/"

    #Initialize fonts
    ac.initFont(0,cfg.Font[0],cfg.Font[1],cfg.Font[2])

    #size of outer border and reduction of width/height
    bs = cfg.BorderSize
    x = 0
    y = 0

    #tyre compound short label
    self.lblTyreCompound = ac.addLabel(appWindow,"press home key!")
    ac.setCustomFont(self.lblTyreCompound, cfg.Font[0],cfg.Font[1],cfg.Font[2])
    ac.setFontSize(self.lblTyreCompound, cfg.FontSize)
    ac.setFontAlignment(self.lblTyreCompound, "center")
    ac.setFontColor(self.lblTyreCompound, *(cfg.AppTextColor))

    #create config elements if button enabled     
    if not cfg.DisableButton&CONFIG:
      #eol info range labels
      self.spinCfg = [0,0]
      #o=["front","rear"]
      for i in range(0,2):
        self.spinCfg[i]=ac.addSpinner(appWindow,"")
        ac.setCustomFont(self.spinCfg[i], cfg.Font[0],cfg.Font[1],cfg.Font[2])
        ac.setFontSize(self.spinCfg[i], cfg.FontSize)
        ac.setFontAlignment(self.spinCfg[i], "center")
        ac.setFontColor(self.spinCfg[i], *(cfg.AppTextColor))
        ac.setSize(self.spinCfg[i],cfg.FontSize*5,cfg.FontSize*1.5)
        ac.setStep(self.spinCfg[i],1)
        ac.setVisible(self.spinCfg[i], 1)
        if cfg.MainBackground!=NONE:
          ac.setBackgroundOpacity(self.spinCfg[i], 0.7)
        else:
          ac.setBackgroundOpacity(self.spinCfg[i], 0)
      #create info label for configuration
      self.lblCfgInfo=ac.addLabel(appWindow," n/a")
      ac.setCustomFont(self.lblCfgInfo, cfg.Font[0],cfg.Font[1],cfg.Font[2])
      ac.setFontSize(self.lblCfgInfo, cfg.FontSize)
      ac.setFontAlignment(self.lblCfgInfo, "center")
      ac.setFontColor(self.lblCfgInfo, *(cfg.AppTextColor))
      ac.setSize(self.lblCfgInfo,cfg.FontSize*5,cfg.FontSize*4.5)
      ac.setVisible(self.lblCfgInfo, 0)
      if cfg.MainBackground!=NONE:
        ac.setBackgroundOpacity(self.lblCfgInfo, 0.7)
      else:
        ac.setBackgroundOpacity(self.lblCfgInfo, 0)

    #if pitboard is used, create label
    if sum(cfg.PitBoard)>0:
      self.lblPitBoard = [0,0,0,0,0,0,0,0,0,0,0]
      for i in range(0,11):
        if i<2 or cfg.PitBoardExtra:
          #create info label for configuration
          self.lblPitBoard[i]=ac.addLabel(appWindow,"n/a")
          ac.setCustomFont(self.lblPitBoard[i], cfg.Font[0],cfg.Font[1],cfg.Font[2])
          ac.setFontSize(self.lblPitBoard[i], cfg.FontSize)
          if i<2:
            ac.setFontAlignment(self.lblPitBoard[i], "left")
          else:
            ac.setFontAlignment(self.lblPitBoard[i], "center")
          ac.setFontColor(self.lblPitBoard[i], *(cfg.AppTextColor))
          ac.setVisible(self.lblPitBoard[i], 0)
          if cfg.MainBackground!=NONE:
            ac.setBackgroundOpacity(self.lblPitBoard[i], 0.7)
          else:
            ac.setBackgroundOpacity(self.lblPitBoard[i], 0)
      o = 13.6
      ac.setText(self.lblPitBoard[0]," Compound "+"\n Pressure F/R"+"\n Temp range"+"\n Air / Road"+"\n\n Tyre life\n High grip"+"\n Medium grip"+"\n Low grip"+"\n End of life")
      if not cfg.useEOL:
        o = 5.6
        ac.setText(self.lblPitBoard[0]," Compound "+"\n Pressure F/R"+"\n Temp range"+"\n Air / Road")
      ac.setSize(self.lblPitBoard[0],cfg.FontSize*7,cfg.FontSize*o)
      ac.setSize(self.lblPitBoard[1],cfg.FontSize*11,cfg.FontSize*o)
      if cfg.PitBoardExtra:
        ac.setSize(self.lblPitBoard[2],cfg.FontSize*8,cfg.FontSize*1.4)
        ac.setSize(self.lblPitBoard[3],cfg.FontSize*4,cfg.FontSize*1.3)
        ac.setSize(self.lblPitBoard[4],cfg.FontSize*4,cfg.FontSize*1.3)
        ac.setSize(self.lblPitBoard[5],cfg.FontSize*4,cfg.FontSize*1.3)
        ac.setSize(self.lblPitBoard[6],cfg.FontSize*4,cfg.FontSize*1.3)
        ac.setSize(self.lblPitBoard[7],cfg.FontSize*4,cfg.FontSize*1.8)
        ac.setSize(self.lblPitBoard[8],cfg.FontSize*4,cfg.FontSize*1.8)
        ac.setSize(self.lblPitBoard[9],cfg.FontSize*4,cfg.FontSize*1.4)
        ac.setSize(self.lblPitBoard[10],cfg.FontSize*4,cfg.FontSize*1.4)
        ac.setText(self.lblPitBoard[2],"x Laps wear")
  
    if cfg.useEOL:

      #setup tables for km, grip and gain
      self.eol[0] = eolTable()
      self.eol[0].Km = []                     #array of km and estimated grip
      self.eol[0].Grip = []                   
      self.eol[0].ActualGrip =[]              #return teoretical grip from driven km
      self.eol[0].DynGain = [1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0] #km gain calculated with actual grip as reference
      self.eol[0].DynGainBuff = [0.0]*100     #buffert, init in newtyre as well
      self.eol[0].DynGainNew = [True]*7       #if the gain settings is new
      self.eol[0].Levels = [8.0,4.0,2.0,1.0]  #arrays of eol levels in km [zero,low,mid,high]
      

      if cfg.logVerbose&EXTEND:
        self.sessionStats = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] #session stats, km, laps and wear
      
      #eol border
      self.eolBorder.h = cfg.Height*2+cfg.SpacingY
      self.eolBorder.w = cfg.BarWidth
      self.eolBorder.Color = cfg.BorderColor
      
      #eol
      self.eolBar.w = self.eolBorder.w-bs*2
      self.eolBar.h = self.eolBorder.h-bs*2
      self.eolBar.Color = cfg.eolColor[0]

      #eol markers
      self.eolMarker.h = 0
      self.eolMarker.w = self.eolBar.w

      #create color for the marker
      if cfg.eolMarker!=COLOR:
        self.eolMarker.Color = cfg.WearMarkerColor[2]
      else:
        #this creates an copy so the original stays intact
        self.eolMarker.Color = cfg.WearMarkerColor    

      #eol laps, if not compact display mode
      self.lblEolLaps=ac.addLabel(appWindow,"n/a")
      ac.setCustomFont(self.lblEolLaps, cfg.Font[0],cfg.Font[1],cfg.Font[2])
      ac.setFontSize(self.lblEolLaps, cfg.FontSize)
      ac.setFontAlignment(self.lblEolLaps, "right")
      ac.setFontColor(self.lblEolLaps, *(cfg.AppTextColor))

      #eol info range labels
      self.lblEolInfo = [0,0,0,0,0]
      for i in range(0,5):
        #create label, last only if display of eol gain is active
        self.lblEolInfo[i]=ac.addLabel(appWindow," n/a")
        ac.setCustomFont(self.lblEolInfo[i], cfg.Font[0],cfg.Font[1],cfg.Font[2])
        ac.setFontSize(self.lblEolInfo[i], cfg.FontSize)
        ac.setFontAlignment(self.lblEolInfo[i], "left")
        ac.setFontColor(self.lblEolInfo[i], *(cfg.AppTextColor))
        ac.setSize(self.lblEolInfo[i],cfg.FontSize*8,cfg.FontSize*1.5)
        ac.setVisible(self.lblEolInfo[i], 0)
        if cfg.MainBackground!=NONE:
          ac.setBackgroundOpacity(self.lblEolInfo[i], 0.7)
        else:
          ac.setBackgroundOpacity(self.lblEolInfo[i], 0)

    #setup dynamic parameters  
    changeModeTyre(appWindow,cfg,self)  

    ptLog("proTyres: Current version "+ptv+", init done",cfg.logVerbose&STANDARD)
    ptLog("proTyres: Using folder '"+setup.tyrePath+"'",cfg.logVerbose&STANDARD)


  #update current values to all object
  def setCurrentValue(self,cfg,tw,distance):
 
   #set traveled km if tyre wear is active and tyre valid
    if tw>0 and self.isValid:
      self.eolBar.raw = (distance-self.eolOffset)/1000
      #ac.console(str(self.startSession))
      #check if Km is reset for no reason
      if self.eolKmOld>self.eolBar.raw and not self.isInPit and self.isNonPitLaneTrack:
        ptLog("proTyres: KM reset on track, eol offset compensated",cfg.logVerbose&STANDARD)
        self.eolOffset = distance-(self.eolKmOld*1000)
        self.eolBar.raw = (distance-self.eolOffset)/1000
      self.eolKmOld = self.eolBar.raw
      #set level and color for wear indicator, index 0-2, level 0-1
      #self.eolBar.Value = min(max((self.eolBar.raw*tw*self.eol[0].DynGain[0]-self.eolLevels[0])/-self.eolLevels[0],0.0),1.0)
      #self.eolMarker.idx = min(max(3-bisect_left(self.eolLevels[::-1],self.eolBar.raw*tw*self.eol[0].DynGain[0]),0),2)
      self.eolBar.Value = min(max((self.eolBar.raw*tw-self.eolLvlAdj[0])/-self.eolLvlAdj[0],0.0),1.0)
      self.eolMarker.idx = min(max(3-bisect_left(self.eolLvlAdj[::-1],self.eolBar.raw*tw),0),2)
      self.eolBar.Color = cfg.eolColor[self.eolMarker.idx]
    else:
      self.eolBar.Value = 1.0
      self.eolBar.raw = (distance-self.eolOffset)/1000
      self.eolMarker.idx = 2
      self.eolBar.Color = cfg.WearColor[self.eolMarker.idx]


  #update current values to all object
  def setLabelValue(self,setup,cfg,tyreBox,speed,tw,laps,wear,ld,info):

    #this function is only called when eol is active

    #update number of laps to label depending on display mode, skip when in pitlane unless new tyres
    if self.isInPit or not self.isInPitLane:
      if cfg.DisplayMode[0]&COMPACT+EXTEND+NOWEAR==EXTEND:
        ac.setText(self.lblEolLaps, "Laps {}".format(laps-self.eolLapsOffset))
      elif cfg.DisplayMode[0]&EXTEND:
        ac.setText(self.lblTyreCompound, self.CompoundShort[0:3]+"/{}".format(laps-self.eolLapsOffset))

    #tyre wear and valid tyre, only during live and not replay/pause
    if tw>0 and self.isValid and speed>75:
      #only check for none new tyres, index or find do not work, enumerate, round no good either
      x=[0,2]
      y=[101.0,101.0]

      #find index with lowest wear
      for i in range(0,4):
        #check highest wear for front and rear, if double curves is not used
        if wear[i]<y[0] and tyreBox[i].isNew==False and (not self.eolFR or i<2):
          y[0]=wear[i]
          x[0]=i
        #check highest wear for rear, if double curves is used  
        elif self.eolFR and i>1 and wear[i]<y[1] and tyreBox[i].isNew==False:
          y[1]=wear[i]
          x[1]=i

      #loop thru eol arrays, if different front/rear it loops a second time
      for idx in range(0,abs(self.eolFR)+1):
        #not new, wear has started but km is less than max and wear more than min
        if not tyreBox[x[idx]].isNew and not self.isInPit and wear[x[idx]]<99.95 and wear[x[idx]]>(cfg.WearLevels[0]*100) and (self.eolBar.raw*tw*self.eol[idx].DynGain[0])<self.eol[idx].Km[-1]:
          #calculate dynamic eol gain of actual distance, based on tyre with the most wear
          self.eol[idx].DynGainBuff[self.eol[idx].DynGainIdx]=self.eol[idx].ActualGrip[self.eolBar.raw*tw*self.eol[idx].DynGain[0]]/wear[x[idx]]-1
          #average 50 values, update moving avg pointer
          self.eol[idx].DynGainIdx += 1
          if self.eol[idx].DynGainIdx==100:
            self.eol[idx].DynGainIdx = 0
          #average
          i = sum(self.eol[idx].DynGainBuff)/self.eol[idx].DynGainP/tw
          #limit between 10 to 175%
          self.eol[idx].DynGain[0] = min(max(self.eol[idx].DynGain[0]+i,0.10),1.75)
          #validate this value to be saved
          setup.eolUpdate = True
          #save non linear gain if at current index
          i=max(bisect_left(cfg.eolLevels,wear[x[idx]])-1,0)
          #if abs(cfg.eolLevels[i]-int(wear[x]*100))<25:
          #set the gain for all levels behind
          if self.eol[idx].DynGainNew[i]:
            #autotune other values
            for o in range(0,i+1):
              if self.eol[idx].DynGainNew[i-o]:
                self.eol[idx].DynGain[i+1-o]=self.eol[idx].DynGain[0]
            #reset autotunig
            if i<6:
              self.eol[idx].DynGainNew[i+1]=False
            if i==0 and wear[x[idx]]<cfg.eolLevels[1]:
              self.eol[idx].DynGainNew[i]=False
              self.eol[idx].DynGainP = DYNGAIN_SLOW
          else:
            self.eol[idx].DynGain[i+1]=self.eol[idx].DynGain[0]

    if info:
      ld /= 1000
      for i in range(0,3):
        if tw>0 and i<3:
          ac.setText(self.lblEolInfo[i]," {:.0f} Laps ({:.0f}Km)".format(
          self.eol[self.eolIdx].Levels[i+1]/tw/ld/self.eol[self.eolIdx].DynGain[i*2+3],self.eol[self.eolIdx].Levels[i+1]/tw/self.eol[self.eolIdx].DynGain[i*2+3]))
        else:
          ac.setText(self.lblEolInfo[i], "-")
      #show optional eol gain if extend display mode (not availible if compact extend)
      if cfg.showEOLgain and cfg.DisplayMode[0]&EXTEND:
        o=cfg.AppTextColor[:]
        if (sum(self.eol[0].DynGainBuff)/100)>0.001:
          o[0]=cfg.PressureColorR[0]
          o[1]=cfg.PressureColorG[0]
          o[2]=cfg.PressureColorB[0]          
        elif (sum(self.eol[0].DynGainBuff)/100)<-0.001:
          o[0]=cfg.PressureColorR[4]
          o[1]=cfg.PressureColorG[4]
          o[2]=cfg.PressureColorB[4]
        ac.setFontColor(self.lblEolInfo[4],*(o))
        if not self.eolFR:
          #single wear curve is used
          ac.setText(self.lblEolInfo[4]," x{:.4f} ({:.4f})".format(self.eol[0].DynGain[0],sum(self.eol[0].DynGainBuff)/100))
        else:
          #double wear curve is used
          ac.setText(self.lblEolInfo[4]," {:.0f} x{:.4f} ({:.4f}) / x{:.4f} ({:.4f})".format(self.eolIdx, self.eol[0].DynGain[0],
            sum(self.eol[0].DynGainBuff)/100, self.eol[1].DynGain[0], sum(self.eol[1].DynGainBuff)/100))
        
  #update graphics
  def drawIndicator(self,cfg,hg):

    #this function is only called when extend is used

    #draw borders around bars
    drawRectangle((self.eolBorder.Color), self.eolBorder.x, self.eolBorder.y, self.eolBorder.w, self.eolBorder.h)

    #for bar range style indicators, set current height 0-1
    ld = int(round(self.eolBar.h*self.eolBar.Value))

    #eolbox based on km
    drawBar((self.eolBar.Color), self.eolBar.x, self.eolBar.y, self.eolBar.w, ld)
    
    #wear range marker if enabled
    if cfg.eolMarker==STANDARD:
      drawLine((self.eolMarker.Color), self.eolMarker.x, self.eolMarker.y[0], self.eolMarker.w, 0)
      drawLine((self.eolMarker.Color), self.eolMarker.x, self.eolMarker.y[1], self.eolMarker.w, 0)
      drawLine((self.eolMarker.Color), self.eolMarker.x, self.eolMarker.y[2], self.eolMarker.w, 0)
    elif cfg.eolMarker==COLOR:
      #show yellow or red marker if wear is above that level
      for i in range(0,2):
        if self.eolMarker.idx>i:
          drawBar((self.eolMarker.Color[i]), self.eolMarker.x, self.eolMarker.y[i]+1, self.eolMarker.w, 2)
      if self.eolMarker.idx==2 and hg:
        drawLine((self.eolMarker.Color[2]), self.eolMarker.x, self.eolMarker.y[2], self.eolMarker.w, 0)


#indicator for pressure, temp/grip and wear with dirt indicator
class tyreIndicator:
  def __init__(self,appWindow,x,y,cfg,hAlign,vAlign):
    self.Pressure = iBox()              #qlQuad position and max size, value is % of size
    self.PressureMarker = iBoxM()
    self.PressureDiffBuff=[0]*100       #rolling buffert for pressure diff, as diff*10 stored as int, std 10s
    self.PressureStep=SCALE16           #current pressure step
    self.Wear = iBox()
    self.WearMarker = iBoxMi()
    self.WearOld = 0                    #previous wear value *1000 
    self.Dirty = iBox()
    self.TempCore = iBox()              #qlQuad position and max size, value is actual temp
    self.TempIn = iBox()
    self.TempMid = iBox()
    self.TempOut = iBox()
    self.TempAvg = iBox()
    self.TempBorder = iBoxM()
    self.TempBar = iBox()
    self.Brake = iBoxMi()               #brake slip indicator box
    self.BrakeTimer = 0.0
    self.GripIdx = 0                    #grip table index, could be seperate for front/rear
    self.GripCore = 0                   #actual grip
    self.GripIn = 0
    self.GripMid = 0
    self.GripOut = 0
    self.GripAvg = 0
    self.Border = iBoxM()               #qlQuad position and size of indicator in total 
    self.posX = x                       #position, bottom left
    self.posY = y
    self.hAlign = hAlign                #position left of right
    self.vAlign = vAlign                #position top or bottom
    self.isNew = False                  #tyre is new, mark with no wear until 100 wear is read

    #setting up all labels and graphic element
    #cordinates is X,Y, x is going to the right, y is going down
    #indicator setup is based on bottom left corner and is built upwards to the right
    #the indictor can then be set LEFT or (default) RIGHT
    #tyre contact indicator and text is TOP or (default) BOTTOM
    #barmode indicators is default BOTTOM and optional CENTER based 

    #actual temp of core
    self.lblTempCore=ac.addLabel(appWindow,"----")
    ac.setCustomFont(self.lblTempCore, cfg.Font[0],cfg.Font[1],cfg.Font[2])
    ac.setFontSize(self.lblTempCore, cfg.FontSize)
    ac.setFontAlignment(self.lblTempCore, "center")
    ac.setFontColor(self.lblTempCore, *(cfg.AppTextColor))


    #display label and absolut pressure
    #difference from optimal pressure
    self.lblPressDiff=ac.addLabel(appWindow,"-.-")
    ac.setCustomFont(self.lblPressDiff, cfg.Font[0],cfg.Font[1],cfg.Font[2])
    ac.setFontSize(self.lblPressDiff, cfg.FontSize)
    ac.setFontAlignment(self.lblPressDiff, "right")
    ac.setFontColor(self.lblPressDiff, *(cfg.AppTextColor))

    #actual pressure
    self.lblPressure=ac.addLabel(appWindow,"-.-")
    ac.setCustomFont(self.lblPressure, cfg.Font[0],cfg.Font[1],cfg.Font[2])
    ac.setFontSize(self.lblPressure, cfg.FontSize)
    ac.setFontAlignment(self.lblPressure, "right")
    ac.setFontColor(self.lblPressure, *(cfg.AppTextColor))

    #psi label indicator
    self.lblPressInfo=ac.addLabel(appWindow,cfg.PressureTU)
    ac.setCustomFont(self.lblPressInfo, cfg.Font[0],cfg.Font[1],cfg.Font[2])
    ac.setFontSize(self.lblPressInfo, cfg.FontSize)
    ac.setFontAlignment(self.lblPressInfo, "right")
    ac.setFontColor(self.lblPressInfo, *(cfg.AppTextColor))

    #size of outer border and reduction of width/height
    bs = cfg.BorderSize
    
    #create indicator objects, colors will be overwritten if tyre is validated
    #border outline of indicator bars
    self.Border.w = cfg.Width
    self.Border.h = cfg.Height-cfg.AuxIndicatorHeight
    self.Border.Color = cfg.BorderColor

    #temp core
    self.TempCore.w = cfg.Width-cfg.BarWidth*2-cfg.SpacingBar*2
    self.TempCore.h = cfg.TyreIndicatorHeight
    self.TempCore.Color = [0,1,0,1]

    #temp avg
    self.TempAvg.w = cfg.Width-cfg.BarWidth*2-cfg.SpacingBar*2
    self.TempAvg.h = cfg.Height-cfg.TyreIndicatorHeight*2-bs*2
    self.TempAvg.Color = [1,0,0,1]

    #temp inner
    self.TempIn.w = cfg.TyreIndicatorWidth
    self.TempIn.h = cfg.TyreIndicatorHeight
    self.TempIn.Color = [0,1,0,1]   

    #temp middle
    self.TempMid.w = cfg.TyreIndicatorWidth
    self.TempMid.h = cfg.TyreIndicatorHeight
    self.TempMid.Color = [0,0,1,1]

    #temp outer
    self.TempOut.w = cfg.TyreIndicatorWidth
    self.TempOut.h = cfg.TyreIndicatorHeight
    self.TempOut.Color = [1,1,0,1]  

    #border indicator for new tyre
    self.TempBorder.w = cfg.TyreIndicatorWidth*2-1
    self.TempBorder.h = int(cfg.AuxIndicatorHeight/2)-1
    self.TempBorder.Color = cfg.WearMarkerColor[2][:]
    self.TempBorder.Color[3] *= 0.75
    
    #temp bar to indicate where in optimal range
    self.TempBar.w = cfg.TyreIndicatorWidth*3
    self.TempBar.h = int(cfg.AuxIndicatorHeight/4)
    self.TempBar.Color = self.TempBorder.Color

    #brake indicator to indicate slip
    self.Brake.w = int(cfg.TyreIndicatorWidth*3*.8)
    self.Brake.h = cfg.AuxIndicatorHeight
    self.Brake.Color = cfg.BrakeColor[0]
    
    #wear
    self.Wear.w = cfg.BarWidth-bs*2
    self.Wear.h = self.Border.h-bs*2
    self.Wear.Color = cfg.WearColor[0]

    #wear markers
    self.WearMarker.w = self.Wear.w
    self.WearMarker.h = 0
    #create color for the marker
    if cfg.WearMarker!=COLOR:
      self.WearMarker.Color = cfg.WearMarkerColor[2]
    else:
      #this creates an copy so the original stays intact
      self.WearMarker.Color = cfg.WearMarkerColor   

    #pressure
    self.Pressure.w = cfg.BarWidth-bs*2
    self.Pressure.h = self.Border.h-bs*2
    self.Pressure.Color = [0,1,0,1]

    #pressure optimal range marker
    self.PressureMarker.w = cfg.BarWidth-bs*2
    self.PressureMarker.Color = cfg.PressureMarkerColor

    #create pressure diff buffert
    self.PressureDiffBuff = [0]*cfg.PressureBuff

    #placement of the dirty bar
    if not cfg.DirtyBarTyre:
      #dirty in wear bar
      self.Dirty.w = cfg.DirtyWidth
      self.Dirty.h = self.Border.h-bs*2
      self.Dirty.Color = cfg.DirtyColor
    else:
      #dirty in tyre box
      self.Dirty.w = cfg.TyreIndicatorWidth*2
      self.Dirty.h = self.TempAvg.h
      self.Dirty.Color = cfg.DirtyColor

    #setup dynamic parameters
    changeModeIndicator(appWindow,x,y,cfg,self,0,0)
    
   
  #update current values to all object
  def setCurrentValue(self,cfg,tyre,pos,tm,tempCore,tempIn,tempMid,tempOut,pressure,wear,dirty,slip):
    #set actual temp values and respective raw values, if they should be availible
    self.TempCore.Value = tempCore
    self.TempIn.Value = tempIn
    self.TempMid.Value = tempMid
    self.TempOut.Value = tempOut
    self.TempAvg.Value = tempAvg = tempCore*tyre.RatioTempCore+(tempIn+tempMid+tempOut)/3*tyre.RatioTempSurface
    self.Wear.raw = wear
    self.Dirty.raw = dirty

    #add diff in rolling avergae (with one decimal as int)
    self.PressureDiffBuff[cfg.PressureBuffIdx]=int(round(pressure*10))
    self.Pressure.raw = sum(self.PressureDiffBuff)/cfg.PressureBuff/10.0

    #set level for dirty indicator, 0-1
    self.Dirty.Value = min(max(dirty/cfg.DirtyStep,0.0),1.0)

    #if tyre is new, set no wear level untill 100 or less than new, with non street tyres, sometimes they do not reach 100, so check if the start to drop
    if self.isNew:
      if (((wear==100.0 or int(wear*1000)<self.WearOld) and tyre.WearValue!=100.0) or (wear<tyre.WearValue-0.000025 and tyre.WearValue==100)) and not tyre.isInPitLane and ac.getCarState(0,CS.SpeedKMH)>75:
        self.isNew = False
        #move temp bar
        self.TempBar.y = self.TempIn.y-int((self.TempIn.h-self.TempBar.h)/2)
        
      #some compounds lower wear before it rise above the start value
      if wear>tyre.WearValue:
        self.WearOld = int(wear*1000)
      else:
        self.WearOld = 0
      #if masked, show 100%
      if cfg.WearMasked:
        wear = 100

    #set level and color for wear indicator, index 0-2, level 0-1
    self.Wear.Value = min(max(((wear/100)-cfg.WearLevels[0])/(cfg.WearLevels[3]-cfg.WearLevels[0]),0.0),1.0)
    self.WearMarker.idx = min(max(bisect_left(cfg.WearLevels,wear/100)-1,0),2)
    self.Wear.Color = cfg.WearColor[self.WearMarker.idx]

    #set sliplevel and color for brake indicator, index 0-2
    if cfg.showBrake&DIRECT:
      if slip>cfg.BrakeLevels[0][0] and cfg.showBrake&DELAYED:
        self.Brake.idx = max(self.Brake.idx,slip)
        self.BrakeTimer = cfg.BrakeTimer
      elif self.BrakeTimer<0.0 or not cfg.showBrake&DELAYED:
        self.Brake.idx = slip
      else:
        self.BrakeTimer -= tm
      self.Brake.Color = cfg.BrakeColor[min(max(bisect_left(cfg.BrakeLevels[0],self.Brake.idx)-1,0),2)]


    #if selected tyre has been validated and all related paramters found
    if tyre.isValid:
      #if peak IMO for AVG is used and temp is over max
      if cfg.AvgPeak and max(tempIn,tempMid,tempOut)>tyre.Grip[self.GripIdx].MaxTemp:
        tempAvg = max(tempIn,tempMid,tempOut)
      if cfg.showTempBar:
        #save value 0 to range of temp for avgtyre and value 0-1
        self.TempBar.raw = (tempIn+tempMid+tempOut)/3
        self.TempBar.Value = min(max((self.TempBar.raw-tyre.Grip[self.GripIdx].MinTemp)/(tyre.Grip[self.GripIdx].MaxTemp-tyre.Grip[self.GripIdx].MinTemp),0.0),1.0)
   
      #set current grip from actual temp
      self.GripCore = tyre.Grip[self.GripIdx].Temp[tempCore]
      self.GripIn = tyre.Grip[self.GripIdx].Temp[tempIn]
      self.GripMid = tyre.Grip[self.GripIdx].Temp[tempMid]
      self.GripOut = tyre.Grip[self.GripIdx].Temp[tempOut]
      self.GripAvg = tyre.Grip[self.GripIdx].Temp[tempAvg]

      #set temp related colors for each indicator
      #if tyre is overheating, add range offset
      if tempCore>tyre.Grip[self.GripIdx].MaxTemp:
        self.TempCore.Color = tyre.ActualTempColor[-self.GripCore+200]
      else:
        self.TempCore.Color = tyre.ActualTempColor[self.GripCore]
      
      if tempIn>tyre.Grip[self.GripIdx].MaxTemp:
        self.TempIn.Color = tyre.ActualTempColor[-self.GripIn+200]
      else:
       self.TempIn.Color = tyre.ActualTempColor[self.GripIn]
      
      if tempMid>tyre.Grip[self.GripIdx].MaxTemp:
       self.TempMid.Color = tyre.ActualTempColor[-self.GripMid+200]
      else:
        self.TempMid.Color = tyre.ActualTempColor[self.GripMid]
      
      if tempOut>tyre.Grip[self.GripIdx].MaxTemp:
        self.TempOut.Color = tyre.ActualTempColor[-self.GripOut+200]
      else:
        self.TempOut.Color = tyre.ActualTempColor[self.GripOut]

      if tempAvg>tyre.Grip[self.GripIdx].MaxTemp:
        self.TempAvg.Color = tyre.ActualTempColor[-self.GripAvg+200]
      else:
        self.TempAvg.Color = tyre.ActualTempColor[self.GripAvg]     


  #update current values to all labels
  def setLabelValue(self,cfg,tyre,pos):
    #if selected tyre has been validated and all related paramters found
    if tyre.isValid:
      #check if high grip marker should be valid in eol bar
      if tyre.eolMarker.h and not self.isNew and self.Wear.raw<99.95:
        tyre.eolMarker.h = False

      #if rear tire, adapt, if any, an offset for difference in optimal pressure
      if pos==REAR:
        dp = self.Pressure.raw + (tyre.IdealPressure[0]+tyre.OffsetPressure[0]-tyre.IdealPressure[1]-tyre.OffsetPressure[1]) 
      else:
        dp = self.Pressure.raw

      #set difference from optimal pressure
      pd = dp-tyre.IdealPressure[0]-tyre.OffsetPressure[0]

      #set difference from optimal pressure
      if pd>0:
        s="+"
      else:
        s=""

      #auto, adjust scale for markers and range
      #reminder, y pos and h is also set in setLabelValue
      if cfg.PressureBar[0]==AUTO:
        i = 0
        o=abs(pd)
        #pressure within min scale
        if o<2:
          i = SCALE6
        #pressure mid scale, or going back from closeset with 1 psi threshold
        elif o<4 and (self.PressureStep!=SCALE6 or o>2.5):
          i = SCALE10
        #pressure max scale or going back from mid scale with 1 psi threshold
        elif o>4.5 or (self.PressureStep!=SCALE10 and o>4):
          i = SCALE16
        #scale for bar has changed
        if i!=0 and i!=self.PressureStep:
          self.PressureStep = i
          self.PressureMarker.h = int(round(cfg.IdealPressureRange/(self.PressureStep*4.0)*(cfg.Height-cfg.AuxIndicatorHeight-cfg.BorderSize*2)))
          self.PressureMarker.y = int(self.Pressure.y-self.Pressure.h/2+self.PressureMarker.h/2)
      #if visible, update label with value        
      if not cfg.DisplayMode[0]&COMPACT:
        #update temp and pressure labels
        ac.setText(self.lblTempCore, "{:.0f}°C".format(self.TempAvg.Value))
        ac.setText(self.lblPressure, cfg.PressureFormat.format(self.Pressure.raw*cfg.PressureGain))
        ac.setText(self.lblPressDiff, s+cfg.PressureFormat.format(pd*cfg.PressureGain))
      #scale pressure bar 0-1 and set actual color
      self.Pressure.Value = max(min(((dp-tyre.OffsetPressure[0])-(tyre.IdealPressure[0]-self.PressureStep*2))/((tyre.IdealPressure[0]+self.PressureStep*2)-(tyre.IdealPressure[0]-self.PressureStep*2)),1.0),0.0)
      self.Pressure.Color = tyre.ActualPressureColor[dp-tyre.OffsetPressure[0]]
    
  #update graphics
  def drawIndicator(self,cfg,tyre):

    if cfg.DisplayMode[0]&WEAR or self.hAlign==LEFT:
      #draw borders around pressure bar
      drawRectangle((self.Border.Color), self.Border.x, self.Border.y, cfg.BarWidth, self.Border.h)
    if cfg.DisplayMode[0]&WEAR or self.hAlign==RIGHT:
      #draw borders around 
      drawRectangle((self.Border.Color), self.Border.x+self.Border.w-cfg.BarWidth, self.Border.y, cfg.BarWidth, self.Border.h)  

    #ac.glBegin(3)
    #core temp
    drawBarR(self.TempCore)
    #inner temp
    drawBarR(self.TempIn)
    #middel temp
    drawBarR(self.TempMid)
    #outer temp
    drawBarR(self.TempOut)
    #avg temp
    drawBarR(self.TempAvg)
    #ac.glEnd()

    #border for tyres, if new and enabled
    if self.isNew and cfg.showNew:
      drawBar((self.TempBorder.Color), self.TempBorder.x, self.TempBorder.y, self.TempBorder.w, self.TempBorder.h)
    #temp bar, when not new and active
    if cfg.showTempBar:# and (not self.isNew or not cfg.showNew):
      tb = int(round(self.TempBar.w*self.TempBar.Value))
      drawBar((self.TempBar.Color), self.TempBar.x+int((self.TempBar.w-tb)/2), self.TempBar.y, tb, self.TempBar.h)
    #brake slip indicator
    if cfg.showBrake&DIRECT and self.Brake.idx>cfg.BrakeLevels[0][0]:
      drawBar((self.Brake.Color), self.Brake.x, self.Brake.y, self.Brake.w, self.Brake.h)
      
    #for bar range style indicators, set current height 0-1
    lw = int(round(self.Wear.h*self.Wear.Value))
    lp = int(round(self.Pressure.h*self.Pressure.Value))
    ld = int(round(self.Dirty.h*self.Dirty.Value))

    #if indicator is at bottom and center bar is used
    if self.vAlign==BOTTOM and cfg.CenterBar:
      ow = -self.Wear.h+lw
      op = -self.Pressure.h+lp
      od = -self.Dirty.h+ld
      co = 1
    else:
      ow = 0
      op = 0
      od = 0
      co = 0

    if cfg.DisplayMode[0]&WEAR:
      #wearbox
      drawBar((self.Wear.Color), self.Wear.x, self.Wear.y+ow, self.Wear.w, lw)
      #wear range marker if enabled
      if cfg.WearMarker==STANDARD:
        drawLine((self.WearMarker.Color), self.WearMarker.x, self.WearMarker.y[0], self.WearMarker.w, 0)
        drawLine((self.WearMarker.Color), self.WearMarker.x, self.WearMarker.y[1], self.WearMarker.w, 0)
      #use color marker
      elif cfg.WearMarker==COLOR:
        #show red marker if wear is above that level
        if self.WearMarker.idx>0:
          drawBar((self.WearMarker.Color[0+co]), self.WearMarker.x, self.WearMarker.y[0]+1, self.WearMarker.w, 2)
        #show yellow marker if wear is above that level
        if self.WearMarker.idx>1:
          drawBar((self.WearMarker.Color[1-co]), self.WearMarker.x, self.WearMarker.y[1]+1, self.WearMarker.w, 2)

    #pressurebox
    drawBar((self.Pressure.Color), self.Pressure.x, self.Pressure.y+op, self.Pressure.w, lp)
    #dirt
    drawBar((self.Dirty.Color), self.Dirty.x, self.Dirty.y+od, self.Dirty.w, ld)
    #optimal pressure target range marker
    drawLine((self.PressureMarker.Color), self.PressureMarker.x, self.PressureMarker.y, self.PressureMarker.w, 0)
    drawLine((self.PressureMarker.Color), self.PressureMarker.x, self.PressureMarker.y-self.PressureMarker.h+1, self.PressureMarker.w, 0)
    #drawRectangle(self.PressureMarker.Color, self.PressureMarker.x, self.PressureMarker.y, self.PressureMarker.w, self.PressureMarker.h)


