######################################################################
ptv = "2.0.0"
######################################################################
# proTyres by Jens Roos (Wergilius at AC forum)
# appWindow = ac.newApp("proTyres")
# appWindow = ac.newApp("proTyres Debug")
#
# 2.0.0 200602  support to read data.acd files, ini & lut files can be forced from cars_extra folder
#               minor code tweaks and structure changes
# 1.5.5 200405  basic support for CSP tyre extension model parameter PRACTICAL_TEMP_RATIO
#       200522  fix for error when performance lut files had unexpected comments
# 1.5.4 191027  relative exit tyre pressure added to extendend pitboard info  
# 1.5.3 190816  fix for rare crash when using CM with certain INI setting and missing the ini & lut pack for selected car
#				        added log entry when EOL is disabled because of zero spline tracks
# 1.5.2 190725  spline property was not always ready during init, function moved to post run
# 1.5.1 190724  fix for error/crash on tracks with zero spline (some variants on LA Canyon),
#               temporary disables EOL and all related functions
#               fix for two errors when EOL was disabled
# 1.5.0 190203  optional support for configuration using Content Manager (CM)
#               note 1! for CM to read the existing INI file correct, proTyres 1.5.0 needs
#               to run once to make the ini file format compatible before using CM!
#               note 2! ini & lut packs still needed for mod cars
#               fix when session was ended, tyre wear stats was wrong
# 1.4.5 181225  fix for estimated interpolation calculation was always done, even if not needed
#               fix for estimated interpolation error when wear lut ended with double grip values
# 1.4.4 181101  fix for estimated interpolation error when wear lut didn't had enough low wear data
#               fix when using different front/rear wear lut tables, single table could sometimes be used anyway
# 1.4.3 180813  optional extend low range wear (red) from 2% to 5%, default is off 
#               optional DIRT overlay color can be set thru INI file
#               current tyre wear setting affect EOL tuning to work better with high wear tyres
#               fix for extended pitboard sometimes show wrong wear info (all % values equal)
#               fix for change of compound mid race, sometimes pitboard displayed wrong name due to timing of shared memory
#               fix for pressure offsets not updated directly on pitboard info if open
#               fix for error when wear lut files had unexpected comments
#               minor eol gain info tuned
#               config_readme.txt updated
# 1.4.2 171219  updated ini&lut pack for all standard AC, DLC and bonus pack cars
#               eol autotune tweaked, could sometimes show strange bar behavaior close to end of life
#               fix for config.ini sometimes saved for no reason when app was closed
# 1.4.1 171116  flexible 'cars_mod' folder structure, any number of folders and name starting with 'cars_' can be used 
#               config.ini is checked that syntax is valid or app will stay static, error message in console
# 1.4.0 171022  optional solid tyre indicator view with rounded corners and simple shades, False (old style) or True (default)
#               fix for app behavior related to in-game HUD preset tabs, would not save or update correct
#               fix for "PitLane" option used for "Other" under "PITBOARD" in INI file, would cause an error on all sessions
#               fix for detection of session restart, could on some rare condition be triggerd faulty
#               fix for app updating strange during replays, will now stay static
#               positions of NewTyre and TempBar indicator in IMO switched and tweaked
#               gpu code tweaked, almost no additional cpu load with new visuals
#               code for internal handling of app and debug window tweaked
#               minor adjustments to backgrounds pictures
#               minor other code tweaks as well
# 1.3.7 171012  fix for some tyre compound behavior that made masked wear and new tyre indicator reset to early
#               fix for tyre and eol data not reset correct when some Challenge sessions was restarted
#               ini & lut pack for all AC 1.15.2 cars (not any known changes from previous)
# 1.3.6 171010  support for different front/rear performance curves, only a few known mod cars have this
#               fix for tyre wear pitboard info was sometimes not reset after changed or restarted session
#               fix for pitboard sometimes not shown during race session
#               log info for session mode/init updated
#               minor code tweaks
# 1.3.5 170918  updated ini&lut pack for all AC and availible DLC cars
#               optional log tyre wear (green-yellow-red scaled 0-100%) writes to py_log.txt after tyre change or enter pitlane, default is off
#               optional show extra tyre wear info on PitBoard as well (if optional logging is used), default is off
#               support for different front/rear wear curves, only a very few cars and specific compounds have this
#               background pictures supplied in different base scales for improved visual when scaling the app
#               fix for some wear tables missing high wear information messing up eol estimation
#               fix for some track/pitbox combinations resulting in 1 extra lap count for 2nd and further tyre changes
#               fix for pitboard sometimes not updated if compound was changed during pitstop
#               changed default brake slip sensitive for "Practice, Qualify, Race" to "High, High, Medium", will not affect existing setups
#               minor visual fix eol high grip marker, notable on few cars/compounds
#               minor code tweaks
#               INI description updated and description of error codes added
# 1.3.4 170720  optional three level brake slip/lockup indicator min/mid/high (yellow/orange/red) with session based sensitive
#               levels (Off/Low/Medium/High) and different modes (Off/Direct/Delayed), update rate 30Hz 
# 1.3.3 170623  updated ini&lut pack for all AC 1.14.4 cars
# 1.3.2 170531  optional PitBoard with compound summary, session display modes None, NewTyre, NewCompound, PitLane, closes when leaving pitlane
#               optional EXTEND display mode without wear bars, when active all dirt levels is shown in eol bar, selection can be set by INI 
#               session specific by adding NoWear in DISPLAY presets or by adding NoWear or Wear to DISABLED to skip selected display modes
#               optional text scale, range 0.5-1.5, font scale is applied after app scale, min size is still standard 10px
#               for non 1.0 it is recomended to use plain or none background
#               standard backgrounds and graphic details updated
#               debugWindow resources not loaded by default, can be enabled again by INI
#               eol gain table tweak
#               fix for instant load of compound data if changed during pitstop (for instant PitBoard details)
#               fix for tyre lap counter when session changes, sometimes it did not reset correct
#               fix for eol marker and labels could sometimes get placed outside app boundaries
#               shared memory reference updated to 1.14.3 and some other minor code tweaks
#               updated ini&lut pack for all AC 1.14.3 cars (not any known changes)
#               index added to INI configuration error information
#               INI description updated and examples added
# 1.3.1 170523  updated ini&lut pack for all AC 1.14.1 cars, some cars was missing compound data in 1.14.0              
#               extended check for Nordschleife Tourist, depending on start pitbox sometimes km fix could be missed
#               very little tweak eol gain calculation
#               fix for app forced visible even if deactivated in app selection bar,
#               even if not activated app will need init to keep correct behavior of pit/track condition
# 1.3.0 170518  updated ini&lut files for all AC cars to version 1.14.0
#               fix for broken lut handling on a few cars (currently known Race Sim Studio)
#               fix for broken session reset when km fix was applied
#               fix when race end to practice session, sometimes current tyre set was not reset correct (not checked)
#               fix for eol color bar & markers, was sometimes not on the correct position & color
#               workaround for URD mod cars wrong parameter configuration, for it to detect tyre compound correct you
#               need to click less than 1 click/second when changing from hard to soft, that due to missing info from
#               the URD cars. change from hard/soft in pitstop will not be detected. compound soft(s) or hard (h) is
#               is not indicated in the app summary (or in the AC pitstop app as well)
# 1.2.2 170511  1 to 7-point dynamic eol, improves estimated end of life (adapts automatically from previous 1-point format)
#               fix if race is started on track and session is restarted, tyre laps/eol data was not reset correct
#               fix for a few high wear compounds, sometimes eol could behave strange on 2nd run
#               fix if km is reset on track messing up eol (example Nordschleife Tourist)
#               fix for info eolgain, not so relevant value was shown, also shown in compact mode as well (if enabled)
#               fix if tyre is overheated and out of lut range, gfx could sometimes freeze until cooldown within lut range
#               pitpos is now not forced set if session changes but jump to pit does, improves new street tyre detection
#               optional non verbose logging in console and pt_log.txt (only errors is shown)
#               add debug info for pit position init
# 1.2.1 170501  fix when standing long time in pit or long countdown before race, newtyre could sometimes be unset
#               fix when session (race) is started on track, position of pitbox is saved if entered later during session
#               (above fixes corrects eol behavior)
# 1.2.0 170424  optional black & white colormode for buttons
#               optionaly bound pressure offsets from ideal target to current track (still temp based)
#               optional fallback is now Temp (False), TempOnly (True), Track, TrackTemp or TrackOnly
#               fix when changing compounds, sometimes wrong offset was selected
#               fix when selecting compound quick back to previous, selection could get stuck on 'n/a'
#               fix for some mod cars compounds not reaching 100% wear, delaying the newtyre overlay behavior (one known)
#               fix for non pitlane track configurations (example Nordschleife Tourist), tyre change not detected correct
#               new init of lib, previous code was sometimes incompatible with some installations (russian confirmed)
#               sim_info updated according to AC 1.13.2, warning if older version loaded by previous app and is not up to date
#               (python seems to share this info and if an older version is loaded before proTyres it could cause app to fail)
#               if app not visible, no background activity (previous EOL calculations was always active)
#				readme INI updated and protyres version added to py_log 
#               fix of non vital app loading error
# 1.1.0 170407  fix for eol range bar not stable even after tuning, buffert had wrong init value
#               newtyre detection flexible sync time, could reduce cpu usage non pit (less than 1%)
#               fix for some mod cars new tyre flicker in pit (currently known Race Sim Studio)
# 1.0.9 170405  fix for zero range optimal temp used by some mod cars (currently known Race Sim Studio)
#               optimal temp range now expands around single entry according to lut file (example RSS +/- 0.2% grip)
#               (ERROR division by zero)
#               added some info for track condition when modifying pressure offsets
# 1.0.8 170404  hotfix - temp range was read correct but not saved correct
# 1.0.7 170403  ini and lut files from AC 1.13.2
#               optional ring average bar showing optimal temp range in IMO boxes
#               optional display mode for pressure bar, Auto or fixed Scale, selectable for Standard and Compact display modes
#               optional display mode for pressure labels, Standard or Compact for each session type
#               optional ideal pressure offset front/rear configurable for each car/compound
#               saved offsets uses track temp as reference for pressure offset, default range +/-2.5 degree/offset
#               optional pressure offset fallback to closest temp range or previous used compound settings
#               optional rolling average for pressure, 0-30s, defaults to 5s
#               debug updated with offset info
#               config menu behavior auto, close and manual
#               disable button option is any of "None, Display, Config, Info, All" (i.e 'DisplayInfo' for only Config button)
#               (disable config button will disable offset functions, use auto for no button mode)
#               optional show IMO ring overheat temp in AVG box 
#               optional start with eol info labels open
#               info and config labels keeps old status when changing display mode
#               minor tweak eol gain and mask during replay/pause/low speed
#               background cleanup, Standard as before, Plain is now AC standard type (very similar to previous), None is no background
#               disable display modes is now "None, Compact, Extend, Standard"
#               new ini structure for better overview and future new options
#               enhanced ini section code and other minor visual and code tweaks
# 1.0.6 170322  ini and lut files from AC 1.13
#               extended error message for folder fallback and tyres.ini not found
#               write console messages to py_log as well
# 1.0.5 170316  wear and eol bar marker has seperate option
#               optional color mode for wear and eol marker
#               new tyre detection tweaked for slicks/street conditions
#               tyre compound with no initial scrub cycle (street) shows as new until wear starts
#               fix for detect new tyres when enter pitbox
#               fix for detect new street tyres during pitstop
#               (one tyre must not be new to detect change of street tyres during pitstop,
#               this is pitstop limits only, pitbox, compound and session change is not affected)
#               pitstop info added to debug window
#               new tyre not shown at all if tyre wear is disabled
#               fix for rare wear curve in some mod cars (1 known)
#               workaround fix for single tyre mod car with missing tyres.ini option (a few known)
#               (tyres.ini needs to edited to make it work as well)
#               tyres.ini checked for valid file format, section and option content before access
#               (will not fix the error but better error handler when loading mod cars with broken ini files)
#               parameters not sensitive to case
#               some minor code and visual tweaks
# 1.0.4 170309  fix for rare double km entry in a few mod cars (1 known)
#               (ERROR Interpolate x_list must be in strictly ascending order!)
#               text adjusted in debug window
# 1.0.3 170307  optional top box mode, rear tyres displayed same as top
#               move pin to standard position (top right)
#               improved pit box and new tyre detection
#               none, plain or standard background
#               new estimated end of life (EOL) indicator for tyres
#               standard, compact or extend display mode
#               info button when eol display mode active
#               display button to toggle thru display modes
#               selectable default displaymodes for practice, qualify and race
#               disable standard or compact display modes
#               minor visual and code tweaks
#               optional new tyre overlay
#               optional font use for VR users
#               optional marker for wear and eol indicator
#               optional folder for mod cars, some cars included
#               final folder fallback to content/car/data folder for open ini mod cars
#               no activity when eol not enabled and not visible 
#               disable button None, Display or All
#               disable new eol mode
#               modified icon for debug
#               ini and lut files from AC 1.12.6
# 1.0.2 170223  Fix for error reading a few curve files
# 1.0.1 170223  New AverageTemperture in center and value, core moved to bottom
#               New tyres is detected and wear indicator is optional masked
#               Optional show dirty level in mid tyre instead of wear bar
#               Show pressure as psi, bar or kPa (abs and diff)
#               ini and lut files from AC 1.12.2
#               Debug, new tire indicated by '|'
# 1.0.0 170220  Initial release
#
# Credits:
# sacredaardvark (Tire PSI Helper), concept idea to use tyres.ini files 
# Rombik - Shared memory Sim Info code
# 
# 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 platform import architecture
from sys import path
from os import environ
from traceback import format_exc

appName = 'proTyres'
#import libraries
try:
    if architecture()[0] == "64bit":
        sysdir = 'apps/python/%s/lib/stdlib64' % appName
    else:
        sysdir = 'apps/python/%s/lib/stdlib' % appName
    path.insert(0, sysdir)
    environ['PATH'] = environ['PATH'] + ";."

    from lib.sim_info import info
    from bisect import bisect_left
    from ctypes import wintypes
    from lib.proTyres_def import *
    from lib.proTyres_util import ptLog
    from lib.proTyres_app import appMainWindow,appDebugWindow
    from lib.proTyres_cls import setupINI,setupApp,setupTyre,tyreIndicator
    from lib.proTyres_dsp import changeModeTyre,changeEolHeight,changeModeIndicator,changeModeApp
    from lib.proTyres_lib import loadCompound,getDeltaPos,setPitPos,setSessionPos,getOrSetEOL,fmtWear
    from lib.proTyres_lib import getOrSetDefaultBoolean,getOrSetDefaultFloat,getOrSetDefaultString,getAndDeleteDefaultString
    
except Exception as e:
    ac.log(appName+": ERROR Importing libraries: %s" % e)
    msg = appName+": ERROR Exception '{}'".format(format_exc())
    ac.log(msg)

#Timers, delay init timer for 2 seconds
timer0 = -2.0
timer1 = -2.0
timer2 = -2.5
timer3 = -2.0
timer4 = 0
timer5 = 0
ntd = 0.0111

#Sourced from the API or shared memory
tyreWearValue = []
tyreTempCoreValue = []
tyreTempValue = []
tyreTempIValue = []
tyreTempMValue = []
tyreTempOValue = []
tyrePressureValue = []
tyreDirtyValue = []
tyreSlipValue = [0,0,0,0]

#MainApp and labels:
appMain = appMainWindow()

#Debug app and labels
appDebug = appDebugWindow()

#setupvariables
setup = setupINI()

#tyre indicators
tyreBox = [0,0,0,0]


#try top open INI if it is valid
try:
  #Initialize configparser
  setup.config.read(setup.configPath)
  setup.configValid =  True
except:
  ptLog("",True)  
  ptLog("proTyres: ERROR "+setup.configPath,True)
  ptLog("proTyres: ERROR ini file has non valid characters or duplicate option name",True)
  ptLog("",True)  

#Check if config file has appropriate content, else add it
key = ["APP","DISPLAY","INDICATOR","PRESSURE","WEAR","DIRT","EOL","PITBOARD"]
for i in range(0,len(key)):
  if setup.config.has_section(key[i]) != True:
    setup.config.add_section(key[i])
    setup.configUpdate = True

#Load or generate customizable parameters

#key="APP"
#scale
s = min(max(getOrSetDefaultFloat(setup, key[0], "SCALE", 1.0),1.0),3.0)
#base font size before scaling, standard is 10
f = min(max(getOrSetDefaultFloat(setup, key[0], "SCALE_FONT", 1.0),0.5),1.5)
cfg = setupApp(s,f)

#use plain background, centerbar will enforce plain
cfg.MainBackground = getOrSetDefaultString(setup, key[0], "BACKGROUND", cfg.MainBackground)
#disable button selection
cfg.configMenu = getOrSetDefaultString(setup, key[0], "CONFIG_MENU_MODE", cfg.configMenu)
#disable button selection
cfg.DisableButton = getOrSetDefaultString(setup, key[0], "DISABLE_BUTTONS", cfg.DisableButton)
#use option black &  white buttons
cfg.bwButton = getOrSetDefaultBoolean(setup, key[0], "BW_BUTTONS", cfg.bwButton)
#use option font
cfg.useOptFont = getOrSetDefaultBoolean(setup, key[0], "USE_OP_FONT", cfg.useOptFont)
#font to be used, check if old syntax is used
if setup.config.has_option(key[0], "OP_FONT"):
    cfg.Font=getAndDeleteDefaultString(setup, key[0], "OP_FONT")
    cfg.Font=cfg.Font.strip("[").strip("]").strip("'")
o=cfg.Font
f=getOrSetDefaultString(setup, key[0], "OP_FONT_NAME", o[:o.find(",")-1])
i=getOrSetDefaultString(setup, key[0], "OP_FONT_ITALIC", o[o.find(",")+1:o.find(",",o.find(",")+1)-len(o)])
b=getOrSetDefaultString(setup, key[0], "OP_FONT_BOLD", o[len(o)-o[::-1].find(",")::])           
cfg.Font='['+"'"+f+"',"+i+','+b+']'
#use of verbose logging
cfg.logVerbose = getOrSetDefaultString(setup, key[0], "VERBOSE_LOG", cfg.logVerbose)
#optional load debug window
appDebug.App = getOrSetDefaultBoolean(setup, key[0], "DEBUG", appDebug.App)

#key="DISPLAY"
#display mode to use during practice
cfg.DisplayMode[2] = getOrSetDefaultString(setup, key[1], "PRACTICE", cfg.DisplayMode[2])
#display mode to use during qualify
cfg.DisplayMode[3] = getOrSetDefaultString(setup, key[1], "QUALIFY", cfg.DisplayMode[3])
#display mode to use during race
cfg.DisplayMode[4] = getOrSetDefaultString(setup, key[1], "RACE", cfg.DisplayMode[4])
#display mode for all other sessions
cfg.DisplayMode[1] = getOrSetDefaultString(setup, key[1], "OTHER", cfg.DisplayMode[1])
#display mode to use during race
cfg.DisplayMode[5] = getOrSetDefaultString(setup, key[1], "DISABLED", cfg.DisplayMode[5])

#key="INDICATOR"
#show new tyre marker in tyre indicator
cfg.showNew = getOrSetDefaultBoolean(setup, key[2], "SHOW_NEW_TYRE", cfg.showNew)
#show new tyre marker in tyre indicator
cfg.showTempBar = getOrSetDefaultString(setup, key[2], "TEMP_BAR_MODE", cfg.showTempBar)
#check for optional top tyre display mode
cfg.TopTyreMode = getOrSetDefaultBoolean(setup, key[2], "TOP_TYRE_MODE", cfg.TopTyreMode)
#check for optional placement of dirty bar
cfg.DirtyBarTyre = getOrSetDefaultBoolean(setup, key[2], "DIRTY_BAR_TYRE", cfg.DirtyBarTyre)
#check for optional avg peak display mode
cfg.AvgPeak = getOrSetDefaultBoolean(setup, key[2], "AVG_PEAK_MODE", cfg.AvgPeak)
#center bar mode
cfg.CenterBar = getOrSetDefaultBoolean(setup, key[2], "CENTER_BAR", cfg.CenterBar)
#use of radius on tyrebox graphics
cfg.useRadius = getOrSetDefaultString(setup, key[2], "USE_RADIUS", cfg.useRadius)
#show brake slip indicator
cfg.showBrake = getOrSetDefaultString(setup, key[2], "SHOW_BRAKE_SLIP", cfg.showBrake)
#layout of pressure during practise
cfg.BrakeLevels[2] = getOrSetDefaultString(setup, key[2], "BRAKE_SENSE_PRACTICE", cfg.BrakeLevels[2])
#layout of pressure during qualifyuv
cfg.BrakeLevels[3] = getOrSetDefaultString(setup, key[2], "BRAKE_SENSE_QUALIFY", cfg.BrakeLevels[3])
#layout of pressure during race
cfg.BrakeLevels[4] = getOrSetDefaultString(setup, key[2], "BRAKE_SENSE_RACE", cfg.BrakeLevels[4])
#layout of pressure during other sessions
cfg.BrakeLevels[1] = getOrSetDefaultString(setup, key[2], "BRAKE_SENSE_OTHER", cfg.BrakeLevels[1])


#key="PRESSURE"
#check and setup custom display of pressure
cfg.PressureTU = getOrSetDefaultString(setup, key[3], "UNITS", cfg.PressureTU)
#pressure average buffert in seconds
cfg.PressureBuff = int(getOrSetDefaultFloat(setup, key[3], "AVERAGE_SECONDS", cfg.PressureBuff))
#pressure fallback to close or previous temp offsets
cfg.PressureFallback = getOrSetDefaultString(setup, key[3], "OFFSET_FALLBACK", cfg.PressureFallback)
#pressure saved offset range, 5 degrees is 7 estimated values per compound
cfg.PressureOffset = getOrSetDefaultFloat(setup, key[3], "OFFSET_TEMP_RANGE", cfg.PressureOffset)
#layout and function of pressure bar for standard display modes
cfg.PressureBar[1] = getOrSetDefaultString(setup, key[3], "BAR_STANDARD", cfg.PressureBar[1])
#layout and function of pressure bar for compact display modes
cfg.PressureBar[2] = getOrSetDefaultString(setup, key[3], "BAR_COMPACT", cfg.PressureBar[2])
#layout of pressure during practise
cfg.PressureLabel[2] = getOrSetDefaultString(setup, key[3], "LABEL_PRACTICE", cfg.PressureLabel[2])
#layout of pressure during qualifyuv
cfg.PressureLabel[3] = getOrSetDefaultString(setup, key[3], "LABEL_QUALIFY", cfg.PressureLabel[3])
#layout of pressure during race
cfg.PressureLabel[4] = getOrSetDefaultString(setup, key[3], "LABEL_RACE", cfg.PressureLabel[4])
#layout of pressure during other sessions
cfg.PressureLabel[1] = getOrSetDefaultString(setup, key[3], "LABEL_OTHER", cfg.PressureLabel[1])

#key="WEAR"
#wear marker to use, none, standard or color
cfg.WearMarker = getOrSetDefaultString(setup, key[4], "MARKER", cfg.WearMarker)
#check for optional masked wear bar
cfg.WearMasked = getOrSetDefaultBoolean(setup, key[4], "MASKED", cfg.WearMasked)
#extend low range to 91% instead of standard 94%
cfg.WearExtLow = getOrSetDefaultBoolean(setup, key[4], "EXTEND_LOW", cfg.WearExtLow)

#key="DIRT"
#color to be used
#font to be used, check if old syntax is used
if setup.config.has_option(key[5], "COLOR"):
    cfg.DirtyColor=getAndDeleteDefaultString(setup, key[5], "COLOR")
    cfg.DirtyColor = [abs(float(i)) for i in cfg.DirtyColor.lstrip("[").rstrip("]").split(",")]
cfg.DirtyColor[0] = getOrSetDefaultString(setup, key[5], "COLOR_RED", str(cfg.DirtyColor[0]))
cfg.DirtyColor[1] = getOrSetDefaultString(setup, key[5], "COLOR_GREEN", str(cfg.DirtyColor[1]))
cfg.DirtyColor[2] = getOrSetDefaultString(setup, key[5], "COLOR_BLUE", str(cfg.DirtyColor[2]))
cfg.DirtyColor[3] = getOrSetDefaultString(setup, key[5], "COLOR_ALPHA", str(cfg.DirtyColor[3]))
cfg.DirtyColor = '['+cfg.DirtyColor[0]+','+cfg.DirtyColor[1]+','+cfg.DirtyColor[2]+','+cfg.DirtyColor[3]+']'

#key="EOL"
#eol marker to use, none, standard or color
cfg.eolMarker = getOrSetDefaultString(setup, key[6], "MARKER", cfg.eolMarker)
#show eol info when any eol display mode selected
cfg.showEOLinfo = getOrSetDefaultBoolean(setup, key[6], "SHOW_INFO", cfg.showEOLinfo)
#show eol gain with info labels
cfg.showEOLgain = getOrSetDefaultBoolean(setup, key[6], "SHOW_GAIN", cfg.showEOLgain)
#disable eol and wear marker functions/display
cfg.useEOL = not getOrSetDefaultBoolean(setup, key[6], "DISABLE", not cfg.useEOL)

#key="PITBOARD"
#display mode to use during practice
cfg.PitBoard[2] = getOrSetDefaultString(setup, key[7], "PRACTICE", cfg.PitBoard[2])
#display mode to use during qualify
cfg.PitBoard[3] = getOrSetDefaultString(setup, key[7], "QUALIFY", cfg.PitBoard[3])
#display mode to use during race
cfg.PitBoard[4] = getOrSetDefaultString(setup, key[7], "RACE", cfg.PitBoard[4])
#display mode for all other sessions
cfg.PitBoard[1] = getOrSetDefaultString(setup, key[7], "OTHER", cfg.PitBoard[1])
#delay when closing pitboard in seconds
cfg.PitBoardClose = min(max(int(getOrSetDefaultFloat(setup, key[7], "CLOSE_SECONDS", cfg.PitBoardClose))-1,0),15)
#check if info button also opens pitboard
cfg.PitBoardOpen = getOrSetDefaultString(setup, key[7], "INFO_OPEN", cfg.PitBoardOpen)
#check if extra wear is shown on pitboard as well
cfg.PitBoardExtra = getOrSetDefaultBoolean(setup, key[7], "EXTEND_INFO", cfg.PitBoardExtra)

#Update config if necessary 
if setup.configUpdate and setup.configValid:
  with open(setup.configPath, 'w') as fileConfig:
    setup.config.write(fileConfig)
    setup.configUpdate = False

def acMain(ac_version):
  global appMain, appName, cfg, tyre, setup, tyreBox,ptv 
  global appDebug

  #Main app window
  appMain.App = ac.newApp(appName)
  ac.setTitle(appMain.App, "")
  ac.drawBorder(appMain.App, 0)
  ac.setIconPosition(appMain.App, 0, -10000)
  
  #configure common indicators, fyi some shared data not availibe during init
  tyre = setupTyre(appMain.App,cfg,setup,ptv)

  #reminder, object positions is also set in function changeDisplayMode
  #set position of LR indicator, this will since it is LEFT, be right bottom corner  
  if cfg.DisplayMode[0]&COMPACT:
    posX = int((cfg.MainCmpWidth)/2 - cfg.SpacingX/2)
    posY = int(cfg.MainCmpHeight/2 - cfg.SpacingY/2)
  else:
    posX = int((cfg.MainWidth)/2 - cfg.SpacingX/2)
    posY = int(cfg.MainHeight/2 - cfg.SpacingY/2)

  #reminder, background is also set in function changeModeApp
  #load background image for main app based on background and display mode
  b=setup.Background
  c=""
  e=""
  w=""
  u=""
  s=""
#  if cfg.MainBackground==PLAIN:
#    b=setup.plainBackground
  if cfg.DisplayMode[0]&COMPACT:
    c="cmp"
  if cfg.DisplayMode[0]&EXTEND:
    e="ext"
  if cfg.DisplayMode[0]&NOWEAR:
    w="nw"
  if c!="" or e!="" or w!="":
    u="_"
  if cfg.Scale>1.5:
    s="20"
  elif cfg.Scale>1.0:
    s="15"
  #set background texture if used
  if cfg.MainBackground==STANDARD:  
    ac.setBackgroundTexture(appMain.App, b+u+c+e+w+s+".png")
  
  #Create tyre Indicators
  tyreBox[0] = tyreIndicator(appMain.App, posX, posY, cfg, LEFT, TOP)
  tyreBox[1] = tyreIndicator(appMain.App, posX+cfg.SpacingX, posY, cfg, RIGHT, TOP)
  tyreBox[2] = tyreIndicator(appMain.App, posX, posY+cfg.Height+cfg.SpacingY, cfg, LEFT, BOTTOM)
  tyreBox[3] = tyreIndicator(appMain.App, posX+cfg.SpacingX, posY+cfg.Height+cfg.SpacingY, cfg, RIGHT, BOTTOM)

  #create buttons if enabled
  appMain.createButtons(cfg,tyre,appName)

  #all listeners must be created in main program
  #create button if enabled
  if not cfg.DisableButton&DISPLAY:
    #display mode button
    ac.addOnClickedListener(appMain.bttnMode,changeDisplayMode)

  #create button if enabled     
  if not cfg.DisableButton&CONFIG:
    #create listener for spinners
    ac.addOnValueChangeListener(tyre.spinCfg[0], spinCfgChanged0)
    ac.addOnValueChangeListener(tyre.spinCfg[1], spinCfgChanged1)
    #if button is used with current settings
    if cfg.configMenu&OPEN==0:
      #config button
      ac.addOnClickedListener(appMain.bttnCfg,showConfigInfo)

  #create button if enabled and eol     
  if not cfg.DisableButton&INFO and cfg.useEOL:
    #info button
    ac.addOnClickedListener(appMain.bttnInfo,showTyreInfo)

  #load optional debug window
  if appDebug.App:

    #Initialize fonts
    ac.initFont(0,"consolas", 0, 0)

    #tyre debug window
    appDebug.createAppLabels(cfg,tyre,appName,ptv)

    #Render callback
    ac.addRenderCallback(appDebug.App, onDebugRender)

    #listener for activating the debug window
    ac.addOnAppActivatedListener(appDebug.App, appDebugActivated)
    #ac.addOnAppDismissedListener(appDebug.App, appDebugDeactivated)

  #Create render callback, only if setup is valid
  if setup.configValid:
    ac.addRenderCallback(appMain.App, onFormRender)

  try:
    x=0
    i=info.physics.localVelocity
    x+=1
    i=info.graphics.windDirection
    x+=2
    i=info.static.pitWindowEnd
    x+=4
  except:
    ptLog(appName+": WARNING sim_info.py conflict (index "+str(x)+") with older version loaded by previous app",True)
    ptLog(appName+": This could cause proTyres to fail, update other apps sim_info.py or untick previous loaded apps",True)    
  return appName

    
def acUpdate(deltaT):
  global appMain, cfg, tyre, setup, tyreBox
  global appDebug
  global tyreWearValue, tyreTempCoreValue,tyreTempValue,tyreTempIValue,tyreTempMValue,tyreTempOValue,tyrePressureValue,tyreDirtyValue,tyreSlipValue
  global timer0, timer1, timer2, timer3, timer4, timer5, ntd

  if (appMain.Visible>0 and info.graphics.status>1) or appMain.Forced: #or appMain.Visible>5):

    #Update the timers
    timer0 += deltaT
    timer1 += deltaT
    timer2 += deltaT
    timer3 += deltaT

    #Display/calculate 10/90 times per second
    if timer3>ntd:
        timer3 = 0
        #check if car is in pitlane
        if not tyre.isNonPitLaneTrack:          
          tyre.isInPitLane = info.graphics.isInPitLane
        else:
          tyre.isInPitLane = getDeltaPos(tyre.PosPit)<8.0
        #set new tyre detection rate 90 or 10 times per second
        if not tyre.isNew and tyre.isInPitLane:
          ntd=0.0111
        else:
          ntd=0.1
        #check if we could be in pit with speed hysteres
        tyre.isInPit = tyre.isInPitLane and (info.physics.speedKmh<0.05 or (info.physics.speedKmh<1.0 and tyre.isInPit))      
        #check if tyres could be changed
        if tyre.isInPit and tyre.isValid and cfg.useEOL:
          #detect when returned to the actual pitbox (within 1cm), this should always set a new tyre
          tyre.isInPitBox = info.graphics.isInPit==1 and info.physics.brake==1.0 and getDeltaPos(tyre.PosPit)<0.01
          #ac.log("proTyres: setNewTyres w{:.5f} d{:.5f} pb{} w{} p{} s{:.3f} b{:.2f}".format(abs(sum(info.physics.tyreWear)-tyre.WearValue*4),getDeltaPos(tyre.Pit),tyre.isInPitBox,abs(sum(info.physics.tyreWear)-tyre.WearValue*4)<0.00125,info.graphics.isInPit,info.physics.speedKmh,info.physics.brake))
          if abs(sum(info.physics.tyreWear)-tyre.WearValue*4)<0.02500: #changed from 0.01
            setNewTyres(tyre,tyreBox,info.physics.tyreWear,2,cfg.logVerbose&STANDARD,cfg.PitBoard[0])
          #if new tyres and compound has change, init change at once for correct pitboard data, wait for info object to sync correct
          if tyre.isNew&DIRECT and not tyre.isInPitBox and tyre.CompoundShort!=ac.getCarTyreCompound(0) and info.graphics.tyreCompound.find('('+ac.getCarTyreCompound(0)+')')>-1:
            changeCompound()
            setNewCompound()
            #ptLog("new compound " + str(cfg.PitBoard[0]&NEWCOMPOUND) + "-" + str(cfg.PitBoard[0]&NEWTYRE),True)
            #update pitboard if needed
            if not cfg.PitBoard[0]&NEWCOMPOUND and cfg.PitBoard[0]&NEWTYRE:
              #ptLog("pitbrd1",True)  
              showPitBoard(True,0,False)

    #Display/calculate 30 times per second
    if timer0>0.0333:
  
        #read all values from AC
        tyrePressureValue = info.physics.wheelsPressure
        tyreDirtyValue = info.physics.tyreDirtyLevel
        tyreWearValue = info.physics.tyreWear
        tyreTempCoreValue = info.physics.tyreCoreTemperature
        tyreTempInValue = info.physics.tyreTempI
        tyreTempMidValue = info.physics.tyreTempM
        tyreTempOutValue = info.physics.tyreTempO

        #set common indicators
        if cfg.useEOL:
          tyre.setCurrentValue(cfg,info.static.aidTireRate,info.graphics.distanceTraveled)

        #check for brake slip
        if cfg.showBrake&DIRECT:
          if info.physics.brake>cfg.BrakePedal:
            tyreSlipValue = info.physics.wheelSlip
            #ac.console("{:.4f} {:.4f} {:.4f} {:.4f}".format(info.physics.wheelSlip[0],info.physics.wheelSlip[1],info.physics.wheelSlip[2],info.physics.wheelSlip[3]))          
          else:
            tyreSlipValue = [0,0,0,0]
          
        #update all indicator values
        #for some reason, temp values in/out is swapped on left side tyres
        tyreBox[0].setCurrentValue(cfg, tyre, FRONT, timer0, tyreTempCoreValue[0],tyreTempOutValue[0],tyreTempMidValue[0], tyreTempInValue[0],tyrePressureValue[0],tyreWearValue[0],tyreDirtyValue[0],tyreSlipValue[0])
        tyreBox[1].setCurrentValue(cfg, tyre, FRONT, timer0, tyreTempCoreValue[1],tyreTempInValue[1],tyreTempMidValue[1], tyreTempOutValue[1],tyrePressureValue[1],tyreWearValue[1],tyreDirtyValue[1],tyreSlipValue[1])
        tyreBox[2].setCurrentValue(cfg, tyre, REAR, timer0, tyreTempCoreValue[2],tyreTempOutValue[2],tyreTempMidValue[2], tyreTempInValue[2],tyrePressureValue[2],tyreWearValue[2],tyreDirtyValue[2],tyreSlipValue[2])
        tyreBox[3].setCurrentValue(cfg, tyre, REAR, timer0, tyreTempCoreValue[3],tyreTempInValue[3],tyreTempMidValue[3], tyreTempOutValue[3],tyrePressureValue[3],tyreWearValue[3],tyreDirtyValue[3],tyreSlipValue[3])

        timer0 = 0
        
    #Display/calculate 10 times per second
    if timer1>0.1:
        timer1 = 0
        appMain.Visible -= 1

        #if app is not visible, stop updating and close any related info/windows
        if appMain.Visible<1:
          #it's in pause menu from visual state, force running in bg
          #ac.console(str(info.graphics.status))
          if info.graphics.status==3:
            appMain.Forced = True
          if appDebug.App:
            #close debug if main in closed
            ac.setVisible(appDebug.App, False)
          #it's forced bu has normal visual state, stop forced mode and set Visible buffert if jumping to pit
          if appMain.Forced and info.graphics.status==2:
            appMain.Forced = False
            appMain.Visible = 50            

        if cfg.useEOL:
          #set common labels
          tyre.setLabelValue(setup,cfg,tyreBox,info.physics.speedKmh,info.static.aidTireRate,info.graphics.completedLaps,tyreWearValue,info.static.trackSPlineLength,appMain.InfoVisible)

        #update all indicator labels
        tyreBox[0].setLabelValue(cfg, tyre, FRONT)
        tyreBox[1].setLabelValue(cfg, tyre, FRONT)
        tyreBox[2].setLabelValue(cfg, tyre, REAR)
        tyreBox[3].setLabelValue(cfg, tyre, REAR)

        #update moving avg pointer
        cfg.PressureBuffIdx += 1
        if cfg.PressureBuffIdx==cfg.PressureBuff:
          cfg.PressureBuffIdx = 0

        #if debug window is visible, update the values on the window
        if appDebug.Visible>0:
          appDebug.Visible -= 1
          appDebug.setLabelValue(cfg,tyre,tyreBox,info.static.aidTireRate,info.static.trackSPlineLength,tyre.isInPitLane,info.physics.roadTemp,info.physics.airTemp)

        #if pit could not be set, check if entering pitbox with a position change bigger than 108km/h (i.e from menu)
        #if tyre.PitIsFuzzy and tyre.isInPitLane:
        if tyre.isInPitLane:
          if info.graphics.isInPit==1 and info.physics.brake==1.0 and (getDeltaPos(tyre.PosOld)>3.0 or timer4>0):
            timer4 += 1
            #tyre.PitIsFuzzy = timer4<10
            if timer4>9:
              #save offset for tyre distance, must be don if jumping from another session
              tyre.eolOffset = info.graphics.distanceTraveled
              tyre.eolLapsOffset = info.graphics.completedLaps
              setPitPos(tyre, info.graphics.isInPit, True, "FuzzyJump",cfg.logVerbose&STANDARD)
              timer4 = 0
          else:
            timer4 = 0
          tyre.PosOld=ac.getCarState(0, CS.WorldPosition)

    #Display/calculate once per second
    if timer2>1:
        timer2 = 0

        #EOL can not be used on track with zero spline length
        #ptLog('sl '+str(info.static.trackSPlineLength),True)
        if cfg.useEOL and info.static.trackSPlineLength == 0:
          cfg.useEOL = False
          disableEOL()

        #Opacity needs to be reset if the app was moved
        ac.setBackgroundOpacity(appMain.App, cfg.MainOpacity)
        if appDebug.App:
          ac.setBackgroundOpacity(appDebug.App, 0.7)
    
        #check if session has started
        if not tyre.startSession and info.graphics.iCurrentTime>3000:
          tyre.startSession = True

        #if new tyres, mark when pit as been left for more than 5m
        if tyre.isNew&DIRECT and getDeltaPos(tyre.PosPit)>5.0 and not tyre.isInPit:
          tyre.isNew-=DIRECT  
        #depending on pitposition and track start, laps offset needs to be set outside pitlane
        if tyre.isNew&DELAYED and not tyre.isInPitLane:
          tyre.isNew-=DELAYED
          tyre.eolLapsOffset = info.graphics.completedLaps

        #if config button and auto open/close when enter/leave pitlane is active
        if not cfg.DisableButton&CONFIG and tyre.startSession and (
            (cfg.configMenu&OPEN and tyre.isInPitLane and not appMain.CfgVisible) or
            (cfg.configMenu&CLOSE and not tyre.isInPitLane and appMain.CfgVisible)):
          showConfigInfo()

        #if session changes, set new tyres, force reset of offsets once tyre data is validated
        if tyre.isValid:
          #show and close pitboard is enabled
          if cfg.PitBoard[0]!=0 and tyre.startSession and ((appMain.PbVisible and not tyre.isInPitLane) or
              (cfg.PitBoard[0]&PITLANE and not appMain.PbVisible and tyre.isInPitLane and not tyre.isNew)):
            if timer5>0:
              timer5-=1
            else:
              #if pitboard and info is open non pitlane and info button activated, close info as well
              if appMain.PbVisible and appMain.InfoVisible and not tyre.isInPitLane and cfg.PitBoardOpen&PITLANE:
                showTyreInfo()
              #toggle pitboard
              showPitBoard(False,not appMain.PbVisible,False)
              #if pb is open when entering pitlane and wear data is availible update wear data even if not shown on pb
              if appMain.PbVisible and tyre.isInPitLane and cfg.logVerbose&INFO+OPEN==INFO:
                setTyreWearInfo(True)

          if tyre.oldSession!=info.graphics.session:
            #close pitboard íf open (will be open within displaymode)
            if sum(cfg.PitBoard)>0:
              #close pitboard if visible
              for i in range(0,tyre.lblPitBoardIdx):
                ac.setVisible(tyre.lblPitBoard[i],False)
              appMain.PbVisible = False
            #change to default display for current session
            changeDisplayMode()
            #save session mode changes
            if tyre.oldSession==-2:
              o="Session init ("+str(info.graphics.session)
            else:
              o="Session changed ("+str(tyre.oldSession)+"->"+str(info.graphics.session)
            #save current session mode
            tyre.oldSession = info.graphics.session
            #check pitlane status
            if info.graphics.isInPit:
              tyre.isNonPitLaneTrack=not info.graphics.isInPitLane
              if tyre.isNonPitLaneTrack or ac.getTrackConfiguration(0).lower()=="touristenfahrten":   
                ptLog("proTyres: None PitLane track configuration",cfg.logVerbose&STANDARD)
            #save the pit position, this should only happen when in pit, overwrite previous pos (was previous forced)
            setPitPos(tyre, info.graphics.isInPit, False, "Session",cfg.logVerbose&STANDARD)
            #session is not started from pit, save position or reset
            if sum(tyre.PosPit)==0:
              setSessionPos(tyre,"Session",cfg.logVerbose&STANDARD)
            else:
              ptLog("proTyres: Session position not used",cfg.logVerbose&STANDARD)
              tyre.PosSession = [0,0,0]
            #force 0 lap offset since it is reset when session changes, km should already be correct
            tyre.eolLapsOffset = 0
            #show pitboard if enabled in new session
            if cfg.PitBoard[0]!=0:
              showPitBoard(True,0,cfg.logVerbose&EXTEND)
            ptLog("proTyres: "+o+")",cfg.logVerbose&STANDARD)

          #ac.console("d"+str(info.graphics.distanceTraveled)+" - l"+str(info.graphics.completedLaps))
          #ac.console(str(getDeltaPos(tyre.PosSession))+" - "+str(info.physics.speedKmh))  
          if tyre.startSession and (info.graphics.iCurrentTime==0 or (info.physics.speedKmh<0.05 and info.graphics.iCurrentTime<3000 and getDeltaPos(tyre.PosSession)<0.01)):
            #set new tyres, forced
            if cfg.useEOL:
                setNewTyres(tyre,tyreBox,tyreWearValue,0,cfg.logVerbose&STANDARD,cfg.PitBoard[0])
            tyre.eolLapsOffset = 0
            tyre.startSession = False
            #show pitboard if enabled
            if cfg.PitBoard[0]!=0:
              showPitBoard(True,0,cfg.logVerbose&EXTEND)
              timer5 = 0
            ptLog("proTyres: Session initiated",cfg.logVerbose&STANDARD)

          #update position of eol markers/labels
          if cfg.useEOL:
            #check for tyre axel with shortest range, if double wear is used and wear has started on front or rear
            if tyre.eolFR and setup.eolUpdate:
              if tyre.eol[0].ActualGrip[tyre.eolBar.raw*info.static.aidTireRate*tyre.eol[0].DynGain[0]]>tyre.eol[1].ActualGrip[tyre.eolBar.raw*info.static.aidTireRate*tyre.eol[1].DynGain[0]]:
                tyre.eolIdx = 1
              else:
                tyre.eolIdx = 0

            changeEolHeight(cfg,tyre,appMain.InfoVisible)
            
            #check and save for tyre session stats if enabled, removed 'not tyre.isNew and'
            if cfg.logVerbose&EXTEND:
              if not tyre.isInPitLane and timer4==0 and info.graphics.status==2 and info.graphics.flag!=5:
                tyre.sessionStats[0] = tyre.eolBar.raw #km
                tyre.sessionStats[1] = info.graphics.completedLaps-tyre.eolLapsOffset
                for i in range(0,4):
                  if tyreBox[i].isNew:
                    tyre.sessionStats[2+i] = 100.0
                  else:
                    tyre.sessionStats[2+i] = tyreBox[i].Wear.raw
                  tyre.sessionStats[6+i] = tyreBox[i].Pressure.raw
                #flag valid data has been set
                if not cfg.logVerbose&INFO:
                  cfg.logVerbose += INFO  
                #reset display flag when new set is started
                if cfg.logVerbose&OPEN:
                  cfg.logVerbose -= OPEN
                
              #if tyre is new, write previous stats if valid and have not been opened
              if tyre.isNew and cfg.logVerbose&INFO+OPEN==INFO:
                setTyreWearInfo(False)

        #delay read of compound change during start of game or changes in pit
        #if compound is changed or not set
        if tyre.CompoundShort!=ac.getCarTyreCompound(0):
          changeCompound()
          timer2 = -1
        #read ideal tyre temperatures and pressures from car tyres.ini and tcurve.lut
        elif tyre.CompoundOld!=tyre.CompoundShort:
          setNewCompound()

def setTyreWearInfo(pl):
  global cfg,tyre,appMain

  #check if info is updated when entering pitlane rather than an new tyre set
  if pl:
    i ="(pitlane) "
  else:
    i = "(new) "
    
  #remove info and add it has been opened
  cfg.logVerbose += OPEN-INFO

  #write to log
  ptLog("proTyres: TYRE Wear %/Pressure "+cfg.PressureTU+" "+i+"LF: "+fmtWear(cfg.WearLevels,tyre.sessionStats[2])+"/"+cfg.PressureFormat.format(tyre.sessionStats[6]*cfg.PressureGain)+", RF: "+fmtWear(cfg.WearLevels,tyre.sessionStats[3])+
        "/"+cfg.PressureFormat.format(tyre.sessionStats[7]*cfg.PressureGain)+", LR: "+fmtWear(cfg.WearLevels,tyre.sessionStats[4])+"/"+cfg.PressureFormat.format(tyre.sessionStats[8]*cfg.PressureGain)+", RR: "+fmtWear(cfg.WearLevels,tyre.sessionStats[5])+
        "/"+cfg.PressureFormat.format(tyre.sessionStats[9]*cfg.PressureGain)+", Laps: {:.0f} ({:.1f}Km)".format(tyre.sessionStats[1]+1,tyre.sessionStats[0]),cfg.logVerbose&STANDARD)

  o = 0
  #if tyre wear should be shown on pitboard as well
  if cfg.PitBoardExtra:
    #set wear info and expand info labels range
    for i in range(0,4):
      pd = tyre.sessionStats[6+i]-tyre.IdealPressure[int(i/2)]-tyre.OffsetPressure[int(i/2)]
      if pd>0:
        s="+"
      else:
        s=""           
      ac.setText(tyre.lblPitBoard[3+i],fmtWear(cfg.WearLevels,tyre.sessionStats[2+i])+'%')
      ac.setText(tyre.lblPitBoard[7+i],s+cfg.PressureFormat.format(pd*cfg.PressureGain)+cfg.PressureTU)
      clr = cfg.WearColor[min(max(bisect_left(cfg.WearLevels,tyre.sessionStats[2+i]/100)-1,0),2)]
      ac.setFontColor(tyre.lblPitBoard[3+i], *(clr))
      clr = cfg.PressureColorPit[bisect_left([-0.75,0.75],pd)]
      ac.setFontColor(tyre.lblPitBoard[7+i], *(clr))
      o+=tyre.sessionStats[2+i]
    ac.setText(tyre.lblPitBoard[2],"{:.0f} Laps".format(tyre.sessionStats[1]+1))
    #only show if any wear or more than 1 lap
    if o<400.0 or tyre.sessionStats[1]>0:
      tyre.lblPitBoardIdx = 11
    else:
      tyre.lblPitBoardIdx = 2
    
    #if pb is already visible, show the extra wear labels or close if non vital wear (first lap return to pit)
    if appMain.PbVisible:
      #setsizePitBoard(cfg,tyre) #not used, main position is not changed
      for i in range(0,11):
        ac.setVisible(tyre.lblPitBoard[i],i<2 or tyre.lblPitBoardIdx>2)

def changeCompound(*args):
  global setup,cfg,tyre  

  ac.setText(tyre.lblTyreCompound, "-")
  tyre.CompoundShort=ac.getCarTyreCompound(0)
  #set and/or read eol parameters 
  if cfg.useEOL:
    getOrSetEOL(setup,cfg,tyre,info.static.aidTireRate,info.static.trackSPlineLength,info.physics.roadTemp,False)
  #reset old tyre data and extend timer delay for checking again
  tyre.CompoundOld = "-"
  tyre.isValid = False

def setNewCompound(*args):
  global setup,cfg,tyre,tyreBox,appMain,appDebug,tyreWearValue 

  tyre.CompoundOld = tyre.CompoundShort
  #try to load and setup all settings for selected tyre
  loadCompound(setup,cfg,tyre,tyreBox,appDebug,info.graphics.tyreCompound[:info.graphics.tyreCompound.find('(')].rstrip().lower())
  #if all settings found and read correct, tyre.isValid is now true
  #set new tyres, forced
  if tyre.isValid:
    if cfg.useEOL:
      setNewTyres(tyre,tyreBox,tyreWearValue,1,cfg.logVerbose&STANDARD,cfg.PitBoard[0])
    #check if pitboard is used, if compund is changed pitboard is always updated
    if cfg.PitBoard[0]:
      #ptLog(info.graphics.tyreCompound + " - " + ac.getCarTyreCompound(0),True)
      #ptLog("pitbrd2",True)
      showPitBoard(True,0,False)
    #if config is open, we need to reload the offsets
    if appMain.CfgVisible:
      appMain.CfgVisible=False
      showConfigInfo()

#GL Drawing		
def onFormRender(*args):
  global cfg, tyre, appMain

  #set visible timer
  appMain.Visible = 5

  #draw common indicators
  if cfg.DisplayMode[0]&EXTEND:
    tyre.drawIndicator(cfg, tyre.eolMarker.h)

  #update all indicator boxes
  for i in range(0,4):
    tyreBox[i].drawIndicator(cfg, tyre)

#GL Drawing		
def onDebugRender(*args):
  global appMain, appDebug

  #set visible timer
  appDebug.Visible = appMain.Visible

#Do on AC shutdown
def acShutdown(*args):
  global setup,cfg,tyre,appMain
  
  #Update config if needed or previous visual state has changed (not used any more)
  #if setup.configUpdate and setup.configValid:
  #  with open(setup.configPath, 'w') as fileConfig:
  #    setup.config.write(fileConfig)
      
  #if eol data has been changed
  if cfg.useEOL:
    getOrSetEOL(setup,cfg,tyre,info.static.aidTireRate,info.static.trackSPlineLength,info.physics.roadTemp,True)   

#debug window, set visibility
def appDebugActivated(*args):
  global appDebug

  #if main not visible, set timer to close it again
  if not appMain.Visible:
    appMain.Visible = 5

def showTyreInfo(*args):
  global appMain,cfg,tyre,timer5

  #labels active or in pitlane
  if cfg.PitBoardOpen&INFO or tyre.isInPitLane:
    #toggle visibility
    appMain.InfoVisible=not appMain.InfoVisible
    #set visibility for info labels, laps on all extend, temp on extend and optionally also eol gain
    for i in range(0,5):
      if i<3 or (cfg.DisplayMode[0]&EXTEND and (i==3 or cfg.showEOLgain)):
        ac.setVisible(tyre.lblEolInfo[i], appMain.InfoVisible)

  #if I bttn should open pitboard
  if not appMain.PbVisible and not tyre.isInPitLane and cfg.PitBoard[0]!=0 and ((appMain.InfoVisible and cfg.PitBoardOpen&PITLANE) or (not appMain.InfoVisible and cfg.PitBoardOpen&PITLANE+INFO==PITLANE)):
    showPitBoard(True,0,False)
    timer5 = cfg.PitBoardClose*2

def showConfigInfo(*args):
  global appMain,cfg,tyre

  #toggle visibility
  appMain.CfgVisible=not appMain.CfgVisible
  #set visibility for config spinners
  for i in range(0,2):
    if appMain.CfgVisible:
      ac.setValue(tyre.spinCfg[i],int(tyre.OffsetPressure[i]))      
    ac.setVisible(tyre.spinCfg[i], appMain.CfgVisible)
  #set text for info label
  if appMain.CfgVisible:
    if tyre.SavedRoadTemp==0:
      #ac.setText(tyre.lblCfgInfo, "{:.0f}Â°C".format(info.physics.roadTemp))
      ac.setText(tyre.lblCfgInfo, "Air {:.0f}Â°C\nRoad {:.0f}Â°C\n(no ref)".format(info.physics.airTemp,info.physics.roadTemp))
    else:
      ac.setText(tyre.lblCfgInfo, "Air {:.0f}Â°C\nRoad {:.0f}Â°C\nRef {:.0f}Â°C".format(info.physics.airTemp,info.physics.roadTemp,tyre.SavedRoadTemp/10.0))
  ac.setVisible(tyre.lblCfgInfo, appMain.CfgVisible)
    
def spinCfgChanged0(*args):
  global tyre
  spinCfgChanged(0,float(args[0]))

def spinCfgChanged1(*args):
  global tyre
  spinCfgChanged(1,float(args[0]))

def spinCfgChanged(i,value):
  global appMain,tyre
  tyre.OffsetPressure[i]=value
  #marked as changed if differ from saved value
  if tyre.SavedOffset[i]!=tyre.OffsetPressure[i]:
    setup.cfgOffsetUpdate=True
  o=cfg.AppTextColor[:]
  if tyre.OffsetPressure[i]<tyre.SavedOffset[i] and tyre.SavedRoadTemp!=0:
    o[0]=cfg.PressureColorR[0]
    o[1]=cfg.PressureColorG[0]
    o[2]=cfg.PressureColorB[0]
    o[3]=cfg.PressureMarkerColor[3]
  elif tyre.OffsetPressure[i]>tyre.SavedOffset[i] and tyre.SavedRoadTemp!=0:
    o[0]=cfg.PressureColorR[4]
    o[1]=cfg.PressureColorG[4]
    o[2]=cfg.PressureColorB[4]
    o[3]=cfg.PressureMarkerColor[3]
  ac.setFontColor(tyre.spinCfg[i],*(o))

  #update pitboard if enabled and visible
  if appMain.PbVisible:
    showPitBoard(True,0,False)

#mark the current tyres as new
def setNewTyres(tyre,tyreBox,ts,s,vl,pb):
  #ac.log("proTyres: setNewTyres({}) {}{}{}{}{} s{:.3f} b{:.2f} w{:.5f}".format(s,tyre.isNew,tyreBox[0].isNew,tyreBox[1].isNew,tyreBox[2].isNew,tyreBox[3].isNew,info.physics.speedKmh,info.physics.brake,sum(ts)))
  #tyre wear active and compound/session change always trigger, otherwise tyres must have some wear, or pitbox is needed
  if info.static.aidTireRate>0.0 and (s<2 or (not tyre.isNew and not (tyreBox[0].isNew and tyreBox[1].isNew and tyreBox[2].isNew and tyreBox[3].isNew) or
      #it is not a street tyre so wear could be used or we are in the actual pitbox 
      (not tyre.isNew and (tyre.WearValue<100 or tyre.isInPitBox)))):
    #ac.log("proTyres: setNewTyres({}) (w{:.4f} d{:.3f}-> 0.0100/0.0075)".format(s,abs(sum(ts)-tyre.WearValue*4),getDeltaPos(tyre.Pit)))
    #check for individual wear only during pitstop
    if s>1:
      for i in range(0,4):
        #ac.log("proTyres: tyre wear["+str(i)+"] {:.4f}".format(ts[i]-tyre.WearValue))
        if abs(ts[i]-tyre.WearValue)>0.0075: #using 0.01 this should be 0.0025
          return
    #mark as new tyre
    for i in range(0,4):
      tyreBox[i].isNew = True
      tyreBox[i].WearOld = 0
      tyreBox[i].TempBar.y = tyreBox[i].TempBar.idx #restore position if it differ
      
    #ac.log("proTyres: tyres is new {}".format(tyre.WearValue))
    #save offset for tyre distance
    tyre.eolOffset = info.graphics.distanceTraveled
    tyre.eolLapsOffset = info.graphics.completedLaps
    #mark tyre set as new, will reset 5m from pitbox
    tyre.isNew = DIRECT+DELAYED
    tyre.eolMarker.h = True #eol high grip marker should be shown
    for i in range(0,abs(tyre.eolFR)+1):
      #reset any non zero eol buffert
      tyre.eol[i].DynGainBuff = [0.0]*100
      #reset gain for new tyre to new (last) index
      tyre.eol[i].DynGain[0] = tyre.eol[i].DynGain[-1]

    #reset eol gain km watch
    tyre.eolIsValid = True
    #if pitpos is 0, race has been started on track, save pitbox position if newtyre is done in pitlane

    if sum(tyre.PosPit)==0:
      setPitPos(tyre, tyre.isInPitLane, False, "NewTyre",vl)
      tyre.PosOld = ac.getCarState(0, CS.WorldPosition)
      #tyre.PitIsFuzzy = cfg.useEOL #if not eol, pit is not used so no need to check fuzzy pit
    #show pitboard
    if pb&NEWTYRE or pb&PITLANE:
      #ptLog("pitbrd3",True)
      showPitBoard(True,0,False)

def showPitBoard(showMe,lo,twc):
  global appMain,cfg,tyre,timer5
    
  if appMain.PbVisible and not showMe:
    for i in range(0,tyre.lblPitBoardIdx):
      ac.setVisible(tyre.lblPitBoard[i],False)
    appMain.PbVisible = False
    timer5 = 0
  else:
    #adjust pressure format
    if cfg.PressureTU=="psi":
      pf = "{:.0f}"
    else:
      pf=cfg.PressureFormat
    p="\n"+pf+cfg.PressureTU+" / "+pf+cfg.PressureTU
    p=p.format(tyre.IdealPressure[0]*cfg.PressureGain, tyre.IdealPressure[1]*cfg.PressureGain)
    if not tyre.GripFR:
      t="\n{:.0f} - {:.0f}Â°C\n{:.0f}Â°C / {:.0f}Â°C".format(
          tyre.Grip[0].MinTemp, tyre.Grip[0].MaxTemp,info.physics.airTemp,info.physics.roadTemp)
    else:
      t="\n{:.0f} - {:.0f}Â°C / {:.0f} - {:.0f}Â°C\n{:.0f}Â°C / {:.0f}Â°C".format(
          tyre.Grip[0].MinTemp, tyre.Grip[0].MaxTemp,tyre.Grip[1].MinTemp, tyre.Grip[1].MaxTemp,info.physics.airTemp,info.physics.roadTemp)
    x=""
    if cfg.useEOL:
      tw = max(info.static.aidTireRate,1.0)
      ld = info.static.trackSPlineLength/1000
      laps = info.graphics.completedLaps-tyre.eolLapsOffset+lo
      #check eol conditions
      if tyre.eol[tyre.eolIdx].DynGain[0]==1.0:
        x="\n(not calibrated)"
      elif laps!=0:
        x="\n(laps to go)"
      elif tyre.eol[tyre.eolIdx].DynGain[1]==tyre.eol[tyre.eolIdx].DynGain[6]:
        x="\n(estimated)"
      else:
        x="\n"
      #set laps text
      for i in range(0,4):
        claps=tyre.eol[tyre.eolIdx].Levels[3-i]/tw/ld/tyre.eol[tyre.eolIdx].DynGain[7-i*2]-laps
        if laps<1:
          x = x+"\n{:.0f} Laps ({:.0f}Km)".format(claps,tyre.eol[tyre.eolIdx].Levels[3-i]/tw/tyre.eol[tyre.eolIdx].DynGain[7-i*2])
        elif claps>=0:
          x = x+"\n{:.0f} Laps".format(claps)
        else:
          x = x+"\n-"
      x="\n"+x
      #check for offsets
      if min(tyre.OffsetPressure)!=0 or max(tyre.OffsetPressure)!=0:
        po=["",""]
        for i in range(0,2):
          if tyre.OffsetPressure[i]>0:
            po[i]="(+{:.0f} ".format(tyre.OffsetPressure[i])+") "
          elif tyre.OffsetPressure[i]<0:
            po[i]="({:.0f} ".format(tyre.OffsetPressure[i])+") "
          p="\n{:.0f}psi "+po[0]+"/ {:.0f}psi "+po[1]
          p=p.format(
          tyre.IdealPressure[0]+tyre.OffsetPressure[0], tyre.IdealPressure[1]+tyre.OffsetPressure[1])
    #set pitboard text and position
    ac.setText(tyre.lblPitBoard[1], info.graphics.tyreCompound[:23]+p+t+x)
    #setsizePitBoard(cfg,tyre) #not used, main position is not changed
    #force tyre wear info labels to be closed
    if twc:
      #if the extra wear labels is shown, close them and rst old stats
      if tyre.lblPitBoardIdx>2:
        tyre.lblPitBoardIdx = 2
        for i in range(0,9):
          ac.setVisible(tyre.lblPitBoard[2+i],False)
      tyre.sessionStats[0] = 0
      tyre.sessionStats[1] = 0
      for i in range(0,4):
        tyre.sessionStats[2+i] = 100.0
        tyre.sessionStats[6+i] = 0.0
      #reset wear flagss      
      cfg.logVerbose = STANDARD+EXTEND
    #show labels
    for i in range(0,tyre.lblPitBoardIdx):
      ac.setVisible(tyre.lblPitBoard[i],True)
    #reset timer if in pitlane
    if tyre.isInPitLane:
      timer5 = cfg.PitBoardClose
    appMain.PbVisible = True

def disableEOL():
  global appMain, cfg, tyre
  for i in range(0,5):
      #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
      #check for conflict with non eol, remove standard or compact disabled of menus 
      if cfg.DisplayMode[i]&STANDARD and cfg.DisplayMode[5]&STANDARD:
        cfg.DisplayMode[5]-=STANDARD
      if cfg.DisplayMode[i]&COMPACT and cfg.DisplayMode[5]&COMPACT:
        cfg.DisplayMode[5]-=COMPACT

  #not using eol, block display mode but wants button (i.e no other display avalible)
  if (cfg.DisplayMode[5]&STANDARD or cfg.DisplayMode[5]&COMPACT) and not cfg.DisableButton&DISPLAY:
    cfg.DisableButton += DISPLAY
    ac.setVisible(appMain.bttnMode,0)

  #new tyre indicator is only possible with eol 
  cfg.WearMasked = False
  #extra pitboard info is only possible with eol
  cfg.PitBoardExtra = False
  #for extend log, eol must be enabled
  if cfg.logVerbose&EXTEND:
    cfg.logVerbose = STANDARD 
  #if pitboard is used, create label
  if sum(cfg.PitBoard)>0:
    o = 5.6
    ac.setText(tyre.lblPitBoard[0]," Compound "+"\n Pressure F/R"+"\n Temp range"+"\n Air / Road")
    ac.setSize(tyre.lblPitBoard[0],cfg.FontSize*7,cfg.FontSize*o)
    ac.setSize(tyre.lblPitBoard[1],cfg.FontSize*11,cfg.FontSize*o)

  #info button, if enabled
  if not cfg.DisableButton&INFO:
    ac.setVisible(appMain.bttnInfo,0)
    cfg.DisableButton += INFO
  #config button if enabled and auto menu is not used
  if not cfg.DisableButton&CONFIG:
    ac.setVisible(appMain.bttnCfg,0)
    cfg.DisableButton += CONFIG

  ptLog("proTyres: Track has zero spline, temporary disabled EOL",cfg.logVerbose&STANDARD)

def changeDisplayMode(*args):
  global appMain,setup,cfg,tyre,tyreBox

  #session changes, set preset mode
  if tyre.oldSession!=info.graphics.session:
    if info.graphics.session>=0 and info.graphics.session<=2:
      snr = 2+info.graphics.session
    else:
      snr = 1
    cfg.DisplayMode[0] = cfg.DisplayMode[snr]
    cfg.PressureLabel[0] = cfg.PressureLabel[snr]
    cfg.PitBoard[0] = cfg.PitBoard[snr]
    cfg.BrakeLevels[0] = cfg.BrakeLevels[snr]
  #if button click
  else:
    #check if eol display and if not disabled, nowear is next
    if cfg.DisplayMode[0]&EXTEND and cfg.DisplayMode[0]&WEAR and not cfg.DisplayMode[5]&NOWEAR:
      cfg.DisplayMode[0] -= WEAR-NOWEAR
    elif cfg.DisplayMode[0]&COMPACT+STANDARD==COMPACT+STANDARD:
      cfg.DisplayMode[0] = COMPACT+EXTEND+WEAR          
    elif cfg.DisplayMode[0]&COMPACT+STANDARD==STANDARD:
      cfg.DisplayMode[0] = EXTEND+WEAR
    elif cfg.DisplayMode[0]&COMPACT+EXTEND==EXTEND:
      cfg.DisplayMode[0] = COMPACT+STANDARD+WEAR   
    else:
      cfg.DisplayMode[0] = STANDARD+WEAR
    #check if new mode is disabled

  #force next mode if eol is not used
  if cfg.DisplayMode[0]&EXTEND and not cfg.useEOL:
    #change to corresponding mode
    if cfg.DisplayMode[0]&COMPACT:
      cfg.DisplayMode[0] = STANDARD+WEAR
    else:
      cfg.DisplayMode[0] = COMPACT+STANDARD+WEAR

  #check for disabled display modes
  if cfg.DisplayMode[0]&COMPACT and cfg.DisplayMode[5]&COMPACT:
    cfg.DisplayMode[0] -= COMPACT
  elif cfg.DisplayMode[0]&EXTEND and cfg.DisplayMode[5]&EXTEND:
    if cfg.DisplayMode[0]&COMPACT:
      cfg.DisplayMode[0] = STANDARD+WEAR
    else:
      cfg.DisplayMode[0] = COMPACT+STANDARD+WEAR
  elif cfg.DisplayMode[0]&STANDARD and cfg.DisplayMode[5]&STANDARD:
    cfg.DisplayMode[0] -= STANDARD-EXTEND

  #check if eol display and if disabled, skip wear to nowear
  if cfg.DisplayMode[0]&EXTEND and cfg.DisplayMode[0]&WEAR and cfg.DisplayMode[5]&WEAR:
    cfg.DisplayMode[0] -= WEAR-NOWEAR  

  #adjust for nowear displaymode
  if cfg.DisplayMode[0]&EXTEND+NOWEAR==EXTEND+NOWEAR:
    cfg.SpacingX=int(-16*cfg.Scale)
    c=int(24*cfg.Scale)
  else:
    cfg.SpacingX=int(8*cfg.Scale)
    c=0

  #set position of LR indicator, this will since it is LEFT, be right bottom corner  
  if cfg.DisplayMode[0]&COMPACT:
    posX = int((cfg.MainCmpWidth-c)/2 - cfg.SpacingX/2)
    posY = int(cfg.MainCmpHeight/2 - cfg.SpacingY/2)
    cfg.PressureBar[0] = cfg.PressureBar[2]
  else:
    posX = int((cfg.MainWidth-c)/2 - cfg.SpacingX/2)
    posY = int(cfg.MainHeight/2 - cfg.SpacingY/2)
    cfg.PressureBar[0] = cfg.PressureBar[1]

  #setup dynamic parameters  
  changeModeApp(appMain,setup,cfg)
  changeModeTyre(appMain.App,cfg,tyre)
  changeModeIndicator(appMain.App,posX, posY,cfg,tyreBox[0],tyre.eolBorder.x,tyre.eolBorder.y)
  changeModeIndicator(appMain.App,posX+cfg.SpacingX,posY,cfg,tyreBox[1],tyre.eolBorder.x,tyre.eolBorder.y)
  changeModeIndicator(appMain.App,posX,posY+cfg.Height+cfg.SpacingY,cfg,tyreBox[2],tyre.eolBorder.x,tyre.eolBorder.y)
  changeModeIndicator(appMain.App,posX+cfg.SpacingX,posY+cfg.Height+cfg.SpacingY,cfg,tyreBox[3],tyre.eolBorder.x,tyre.eolBorder.y)

  #if config was open, reload the offset
  if appMain.CfgVisible:
    appMain.CfgVisible=False
    showConfigInfo()
  else:
    appMain.CfgVisible=False

  #info was open or force on and new display is extend, open it again
  if (appMain.InfoVisible or cfg.showEOLinfo) and cfg.DisplayMode[0]&EXTEND:
    appMain.InfoVisible=False
    showTyreInfo()
  else:
    appMain.InfoVisible=False

