# Version:    v1.2
# Date:       19.05.2017
#
# Thanks:     - Rombik for the sim_info module


import ac
import acsys
import sys
import os
import time
import math
import bisect
import configparser
import platform
import traceback
import threading


from math import sin, cos, pi

appName = 'ACRLfuel'
imgPath = ('apps/python/%s/img/' % appName)
dataPath = ('apps/python/%s/data/' % appName)

# import libraries
try:
    if platform.architecture()[0] == "64bit":
        sysdir = 'apps/python/%s/dll/stdlib64' % appName
    else:
        sysdir = 'apps/python/%s/dll/stdlib' % appName
    sys.path.insert(0, sysdir)
    os.environ['PATH'] = os.environ['PATH'] + ";."
    
    import ctypes 

    from ACRLfuelLib.sim_info import info
except Exception as e:
    ac.log(appName + ": Error importing libraries: %s" % e)
    raise

updateTime = 78
lastUpdateTime = 0
trackLength = 0

gui = 0
fuel = 0
laptime = 0
state = 0

def acMain(ac_version):
    try:
        global trackLength, gui, fuel, laptime, state
        
        ac.initFont(0, "Consolas", 0, 1)

        appWindow = ac.newApp(appName)

        ac.setCustomFont(appWindow, "Consolas", 0, 0)

        ac.setSize(appWindow, 332, 190)  # inital value reset later
        ac.drawBorder(appWindow, 0)
        ac.setBackgroundOpacity(appWindow, 0.7)

        # change the number below to alter the size. Bigger=larger App => overridden by ini now!!
        gui = DataView(appWindow, 12)

        fuel = Fuel()
        laptime = LapTime()
        state = State()
        
        ac.log(appName + ": acMain complete")
        return appName
    except Exception as e:
        ac.log(appName + ": Error in acMain: %s" % e)
        # raise

def updateThread():
    try:
        global lastUpdateTime, gui, fuel, laptime, state
        lastUpdateTime = 0
        
        laptime.update(state.crossedSF, state.backToPit)
        
        (session, crossedSF, sessionReset) = state.tick()
        
        fuel.set(info.physics.fuel, gui.rangeCalc)
        ac.setText(gui.fuelx, '%.2fL' % fuel.amount)
        
        if fuel.amount < fuel.lapLast*1.5:
            if fuel.low is False:
                fuel.low = True
                ac.setVisible(gui.fuelIcon, 1)
        elif fuel.low is True:
            fuel.low = False
            ac.setVisible(gui.fuelIcon, 0)
                
        if session != 2:
            if session == 0:
                ac.setText(gui.session, "PRACTICE")
            if session == 1:
                ac.setText(gui.session, "QUALIFY")
            ac.setText(gui.sessionx,
                       MStimetostring(info.graphics.sessionTimeLeft, 'hour'))
                       
            rangeCalc = gui.rangeCalc % 6
            if rangeCalc == 5:
                ac.setText(gui.fuelRangex, "%s" % MStimetostring(fuel.range*laptime.lap80,'hour'))
            else:
                ac.setText(gui.fuelReqx, "---L")
        else:
            ac.setText(gui.session, "RACE")
            lapTime = laptime.lap80/1000.0
            ac.setText(gui.sessionx, "%d laps" % state.lapsRemaining(lapTime))

        if fuel.lapCount > 0 or fuel.amountLast > 0:
            rangeCalc = gui.rangeCalc % 6
            if rangeCalc == 5:
                ac.setText(gui.fuelRangex, "%s" % MStimetostring(fuel.range*laptime.lap80,'hour'))
            else:
                ac.setText(gui.fuelRangex, "%.2f laps" % fuel.range)
            if session == 2:
                if state.timed is True:
                    lapTime = laptime.lap80/1000.0
                    #ac.log("Fuel laptime %f" % laptime.lap80)
                    if lapTime > 0:
                        fuelReq = fuel.required(state.raceTimeLeft/lapTime + 1)
                        if gui.fuelAdd:
                            fuelReq -= fuel.amount
                    if lapTime > 0:
                        ac.setText(gui.fuelReqx, "%.2fL" % fuelReq)
                        # ac.log("lapTime %.3f" % lapTime)
                        # ac.log("timeLeft %.3f" % state.raceTimeLeft)
                        # ac.log("laps: %.2f" % (state.raceTimeLeft/lapTime + 1.0))
                    else:
                        ac.setText(gui.fuelReqx, "-.--L")
                else:
                    fuelReq = fuel.required(info.graphics.numberOfLaps -
                                        state.distance)
                    if gui.fuelAdd:
                        fuelReq -= fuel.amount
                    ac.setText(gui.fuelReqx, "%.2fL" % fuelReq)

        if crossedSF is True:
            fuel.update(state.inPitLane)
            ac.setText(gui.fuelLastx, "%.2fL" % fuel.burnedLast)
            ac.setText(gui.fuelLapx, "%.2fL" % fuel.avg)
            
            rangeCalc = gui.rangeCalc % 6
            if rangeCalc == 5:
                ac.setText(gui.fuelRangex, "%s" % MStimetostring(fuel.range*laptime.lap80,'hour'))
            else:
                ac.setText(gui.fuelRangex, "%.2f laps" % fuel.range)
                    
    except Exception as e:
        ac.log(appName + ": Error in updateThread: %s" % e)
        msg = 'Exception: {}'.format(traceback.format_exc())
        ac.log(msg)
        # raise
    
    
def acUpdate(deltaT):
    try:
        global lastUpdateTime, gui, fuel, splits, state
        
        lastUpdateTime += deltaT

        if lastUpdateTime < float(updateTime)/1000:
            return
        
        t1 = threading.Thread(target=updateThread)
        t1.start()
        #t1.join()
 
    except Exception as e:
        ac.log(appName + ": Error in acUpdate: %s" % e)
        msg = 'Exception: {}'.format(traceback.format_exc())
        ac.log(msg)
        # raise


def acShutdown():
    try:
        gui.saveConf()
        
    except Exception as e:
        ac.log(appName + ": Error in acShutdown: %s" % e)
        msg = 'Exception: {}'.format(traceback.format_exc())
        ac.log(msg)
        raise

class Fuel:
    def __init__(self):
        self.amount = 0
        self.amountLast = 0
        self.burned = 0
        self.burnedLast = 0
        self.lapCount = 0
        self.avg = 0
        self.range = 0
        self.lapLast = 0
        self.userAvg = 0
        self.low = False

    def set(self, amount, rangeCalc):
        if abs(amount-self.amount) > 0.2:  # large change in fuel level
            self.burnedLast = self.amountLast-self.amount
            self.amountLast = amount
            # ac.log('Large fuel change: burnedLast=%.2f' % self.burnedLast)
        self.amount = amount
        
        rangeCalc = rangeCalc % 6
        if rangeCalc == 0 and self.avg > 0:
            self.userAvg = self.avg
        if rangeCalc == 1 and (self.avg > 0 and self.lapLast > 0):
            self.userAvg = min(self.avg, self.lapLast)
        if rangeCalc == 2 and (self.avg > 0 or self.lapLast > 0):
            self.userAvg = max(self.avg, self.lapLast)
        if rangeCalc == 3 and (self.avg > 0 or self.lapLast > 0):
            self.userAvg = (self.avg+self.lapLast)/2.0
        if rangeCalc == 4 and self.lapLast > 0:
            self.userAvg = self.lapLast
        if rangeCalc == 5 and (self.avg > 0 or self.lapLast > 0):
            self.userAvg = max(self.avg, self.lapLast)
            
        if self.userAvg > 0:
            self.range = self.amount/self.userAvg
        
    def required(self, distance):
        return distance * self.userAvg

    def update(self, inPit):
        if inPit == 1:  # car in pit may have refuelled before crossing line
            temp = self.amountLast-self.amount
            if temp < 0.3:
                self.burnedLast += temp
            else:
                self.burnedLast = temp
            # ac.log(
            #    'Pit: burnedLast: %.2f  temp: %.2f' % (self.burnedLast, temp))
        else:
            self.burnedLast = self.amountLast-self.amount

        self.lapLast = self.burnedLast
        self.lapCount += 1
        self.burned += self.burnedLast
        self.avg = self.burned/self.lapCount
        self.amountLast = self.amount


class State:
    def __init__(self):
        self.session = 0
        self.lastLap = 0
        self.lapChange = False
        self.crossedSF = False
        self.atStart = 0
        self.distance = 0
        self.backToPit = False
        self.inPitLane = False
        self.inPitBox = False
        self.timed = False
        self.finished = False
        self.raceTimeLeft = 0
        self.leaderFinished = False
        self.leaderExtraLap = -1
        self.leaderLaptime = 0
        self.leaderDistance = 0
        self.leaderLastLap = 0
        self.leaderLapchange = False
        self.totalDrivers = ac.getCarsCount()
        
    def tick(self):
        try:
            previousSession = self.session
            self.session = info.graphics.session
            if (previousSession != self.session or
                    self.lastLap > info.graphics.completedLaps):
                sessionReset = True
                self.lastLap = info.graphics.completedLaps
                self.reset()
            else:
                sessionReset = False
            
            if self.session == 2 and info.static.isTimedRace == 1:
                self.timed = True
                    
            self.crossedSF = self.lapChange
            if self.crossedSF:
                self.lapChange = False
            
            if self.leaderLapchange:
                self.leaderLapchange = False
                if self.raceTimeLeft < 1:
                    if info.static.hasExtraLap:
                        self.leaderExtraLap += 1
                        if self.leaderExtraLap >= 1:
                            self.leaderFinished = True
                    else:
                        self.leaderFinished = True
                    
            
            if self.lastLap < info.graphics.completedLaps:
                self.lapChange = True
                self.lastLap = info.graphics.completedLaps
                if self.leaderFinished:
                    self.finished = True
                    
            if self.timed:
                pos = 99
                leader = 0
                for driver in range(self.totalDrivers):
                    pos = ac.getCarRealTimeLeaderboardPosition(driver)
                    if pos==0:
                        leader = driver
                        break
                    
                spline = ac.getCarState(leader, acsys.CS.NormalizedSplinePosition)
                laps = ac.getCarState(leader, acsys.CS.LapCount)
                self.leaderLaptime = ac.getCarState(leader, acsys.CS.BestLap)
                self.leaderDistance = laps
                if self.leaderFinished is False:
                    self.leaderDistance += spline
                if laps != self.leaderLastLap:
                    self.leaderLastLap = laps
                    self.leaderLapchange = True
                
            speed = ac.getCarState(0, acsys.CS.SpeedKMH)
            self.inPitBox = bool(ac.isCarInPit(0))
            lastpit = self.inPitLane
            self.inPitLane = bool(ac.isCarInPitline(0))
            self.backToPit = ((self.inPitLane and not lastpit) and
                                 (self.inPitBox or speed < 1 or speed > 500))
            
            spline = ac.getCarState(0, acsys.CS.NormalizedSplinePosition)
            distance = ac.getCarState(0, acsys.CS.LapCount) + spline
            
            if (info.graphics.iCurrentTime < 3*updateTime and
                        info.graphics.completedLaps == 0):
                if spline > 0.5:   # at start behind S/F line
                    self.atStart = -1

            if self.backToPit and spline > 0.5:
                # ac.log("car: %d back to pit" % index)
                self.atStart = -1

            if self.atStart == -1 and spline < 0.5:
                # change behind S/F line to off when distance is low
                self.atStart = 0

            # apply adjust for back to pits and starting
            distance += self.atStart
            
            self.distance = distance
                
            if self.timed is True:
                self.timeRemaining()
                
            return (self.session, self.crossedSF, sessionReset)
        except Exception as e:
            ac.log(appName + ": Error in State.tick: %s" % e)
            msg = 'Exception: {}'.format(traceback.format_exc())
            ac.log(msg)

    def reset(self):
        self.atStart = 0
        self.distance = 0
        self.backToPit = False
        self.inPitLane = False
        self.inPitBox = False
        self.finished = False
        self.raceTimeLeft = 0
        self.leaderFinished = False
        self.leaderExtraLap = -1
        self.leaderLaptime = 0
        self.leaderDistance = 0
        self.leaderLapchange = False

    def timeRemaining(self):
        # ac.log ("Start %.1f Duration %.1f Current %.1f" % (self.raceStartS, self.raceDurationS, time.time()))
        # if self.raceStartS < 1:
            # self.raceTimeLeft = self.raceDurationS
        # else:
            # self.raceTimeLeft = self.raceDurationS - (time.time() - self.raceStartS)
        self.raceTimeLeft = info.graphics.sessionTimeLeft/1000
    
    def lapsRemaining(self, lapTime):
        if self.timed is False:
            laps = info.graphics.numberOfLaps - info.graphics.completedLaps
            if laps < 0:
                laps = 0
            return laps
        else:
            if self.finished or lapTime == 0:
                return 0
                
            lapsInRemaining = self.raceTimeLeft / lapTime
            
            # ac.log("dist in time: %.2f" % lapsInRemaining)
            
            # laps driver can do in remaining time + finish current
            laps = int(math.ceil((self.distance % 1) + lapsInRemaining))
            # ac.log("plus current: %d" % laps)
            
            if self.leaderFinished is False and self.leaderLaptime > 0:
                pos = ac.getCarRealTimeLeaderboardPosition(0)
                
                if pos != 0:
                    leaderLapsInRemaining = self.raceTimeLeft / self.leaderLaptime
                    
                    # check cars location around track when timer ends 
                    posClockEnd = (self.distance + lapsInRemaining) % 1
                    leaderPosClockEnd = (self.leaderDistance + leaderLapsInRemaining) % 1
                    # ac.log("Pos L: %.2f Pos d: %.2f" % (posClockEnd, leaderPosClockEnd))
                    
                    # leader behind driver so driver needs another lap
                    if leaderPosClockEnd < posClockEnd:
                        laps = laps + 1
                        # ac.log("add lap for leader position")
            
            # extra lap
            if info.static.hasExtraLap == 1:
                if self.raceTimeLeft > 0 or self.leaderExtraLap < 0: 
                    laps += 1
                    
            return laps
        
        
class LapTime:
    def __init__(self):
        self.best = 0
        self.lap80 = 0
        self.valid = True
        self.preDirt = [0, 0, 0, 0]
        self.laps = []

    def reset(self):
        self.valid = True
        self.preDirt = [0, 0, 0, 0]

    # did use fastest lap where tyres did not get dirty but thats annoying on long tracks like Nurb
    def update(self, CrossedSF, backToPit):
        try:
            if CrossedSF:
                last = ac.getCarState(0, acsys.CS.LastLap)
                if last > 0:
                    bisect.insort(self.laps, last) 
                self.best = info.graphics.iBestTime
                if len(self.laps) > 0:
                    self.lap80 = self.laps[int(len(self.laps)/5)]
                # ac.log("%s" % str(self.lap80))
                # if self.valid is True and (info.graphics.iBestTime < self.best or self.best < 1) and backToPit is False:
                    # self.best = info.graphics.iBestTime
                # self.valid = True 

            # tyres = info.physics.tyreDirtyLevel
            # dirtDelta = []
            # for index, preTyre in enumerate(self.preDirt):
                # dirtDelta.append(tyres[index] - preTyre)

            # if (all([tDirt > 0 for tDirt in dirtDelta]) and not
                    # ac.isCarInPitline(0)):
                # self.valid = False

            # for ind, preTyre in enumerate(self.preDirt):
                # preTyre = float(tyres[ind])

        except Exception as e:
            ac.log(appName + ": Error in Splits.update: %s" % e)
            raise
        

def TextLabel(app, text, font, fontAlign, fontSize, x, y, width, height):
    self = ac.addLabel(app, text)
    ac.setFontSize(self, fontSize)
    ac.setSize(self, width, height)
    ac.setFontAlignment(self, fontAlign)
    ac.setPosition(self, x, y)
    ac.setCustomFont(self, font, 0, 1)
    return self


class DataView:
    def __init__(self, app, fontSize):
        self.fontSize = fontSize
        self.fuelAdd = True
        self.rangeCalc = 0
        
        self.loadConf()
        fontSize = self.fontSize
        
        # GUI layout
        font = 'Consolas'
        fScale = fontSize/2.0
        top = fontSize/2.0
        line = top
        lineheight = fontSize*1.4
        gap = fontSize * 1.4
        fuel_gap = (gap*3+fScale*11-lineheight)/4.0
        self.apx = app
        height = top*2+7.0*lineheight
        width = gap*2+fScale*18

        self.timer = None

        ac.setIconPosition(app, -9999999999, line)
        ac.setTitle(app, '')
        ac.setSize(app, width, height)

        ac.drawBorder(app, 0)
        ac.setBackgroundColor(app, 0, 0, 0)
        ac.setBackgroundOpacity(app, 0.7)

        self.infoLabel = TextLabel(
            app, '', font, 'center',
            fontSize, 0, -2*lineheight, width, lineheight*2)
        ac.drawBackground(self.infoLabel, 1)
        ac.setBackgroundColor(self.infoLabel, 0, 0, 0)
        ac.setBackgroundOpacity(self.infoLabel, 0.6)
        self.setInfoLabel('', 0.1)
        
        self.fuelIcon = ac.addButton(app, "")
        ac.setSize(self.fuelIcon, lineheight*2, lineheight*2)
        ac.setPosition(self.fuelIcon, width/2.0-lineheight, height/2.0-lineheight)
        ac.setBackgroundTexture(self.fuelIcon, imgPath + "lowFuel.png")
        ac.drawBorder(self.fuelIcon, 0)
        ac.setBackgroundOpacity(self.fuelIcon, 0)
        ac.drawBackground(self.fuelIcon, 0)
        ac.setVisible(self.fuelIcon, 0)
        
        left = fScale

        self.session = TextLabel(
            app, 'SESSION', font, 'center', fontSize, left, line,
            fScale*9.0+gap, lineheight)
            
        left += fScale*9.0+gap
        
        self.fuel = TextLabel(
            app, 'FUEL', font, 'center', fontSize, left, line, fScale*7.0+gap,
            lineheight)

        line += lineheight
        left = fScale
        
        self.sessionx = TextLabel(
            app, '--:--:--', font, 'center', fontSize, left, line,
            fScale*9.0+gap, lineheight)
            
        left += fScale*9.0+gap
        
        self.fuelx = TextLabel(
            app, '---L', font, 'center', fontSize, left,
            line, fScale*7.0+gap, lineheight)
        
        line += lineheight*1.5
        left = fScale
        
        self.fuelLap = TextLabel(
            app, 'L/Lap', font, 'center', fontSize, left, line, fScale*9.0+gap,
            lineheight)
        
        left += fScale*9.0+gap

        self.fuelLast = TextLabel(
            app, 'Last', font, 'center', fontSize, left, line, fScale*7.0+gap,
            lineheight)
            
        line += lineheight
        left = fScale
        
        self.fuelLapx = TextLabel(
            app, '--.--L', font, 'center', fontSize,
            left, line, fScale*9.0+gap, lineheight)
        
        left += fScale*9.0+gap

        self.fuelLastx = TextLabel(
            app, '--.--L', font, 'center', fontSize,
            left, line, fScale*7.0+gap, lineheight)
        
        left += fScale*9.0+gap
        
        line += lineheight*1.5
        left = fScale
        
        self.fuelRange = TextLabel(
            app, 'Range', font, 'center', fontSize, left, line, fScale*9.0+gap,
            lineheight)

        self.rangeLeftBut = ac.addButton(app, "")
        ac.setSize(self.rangeLeftBut, fScale*1.5, fScale*1.5)
        ac.setPosition(self.rangeLeftBut, left+fScale*1.0, line+fScale*0.47)
        ac.setBackgroundTexture(self.rangeLeftBut, imgPath + "left_arrow.png")
        ac.drawBorder(self.rangeLeftBut, 0)
        ac.setBackgroundOpacity(self.rangeLeftBut, 0)
        ac.drawBackground(self.rangeLeftBut, 0)
        ac.addOnClickedListener(self.rangeLeftBut, rangeLeftClick)

        self.rangeRightBut = ac.addButton(app, "")
        ac.setSize(self.rangeRightBut, fScale*1.5, fScale*1.5)
        ac.setPosition(self.rangeRightBut, left+fScale*9.0, line+fScale*0.47)
        ac.setBackgroundTexture(
            self.rangeRightBut, imgPath + "right_arrow.png")
        ac.drawBorder(self.rangeRightBut, 0)
        ac.setBackgroundOpacity(self.rangeRightBut, 0)
        ac.drawBackground(self.rangeRightBut, 0)
        ac.addOnClickedListener(self.rangeRightBut, rangeRightClick)

        left += fScale*9.0+gap
        
        self.fuelReq = TextLabel(
            app, 'L Add', font, 'center', fontSize, left,
            line, fScale*7.0+gap, lineheight)
        if self.fuelAdd is False:
            ac.setText(self.fuelReq, 'L Tot.')
        
        line += lineheight
        left = fScale
        
        self.fuelRangex = TextLabel(
            app, '--.- laps', font, 'center', fontSize,
            left, line, fScale*9.0+gap, lineheight)
        
        left += fScale*9.0+gap

        self.fuelReqx = TextLabel(
            app, '---L', font, 'center', fontSize, left,
            line, fScale*7.0+gap, lineheight)
        
        self.totBut = ac.addButton(app, "")
        ac.setSize(self.totBut, fScale*7.0+gap, lineheight*2)
        ac.setPosition(self.totBut, left, line - lineheight )
        ac.drawBorder(self.totBut, 0)
        ac.setBackgroundOpacity(self.totBut, 0)
        ac.drawBackground(self.totBut, 0)
        ac.addOnClickedListener(self.totBut, fuelClick)
        
    def loadConf(self):
        try:
            fname = dataPath + 'appConf.ini'
            
            config = configparser.ConfigParser()
            config.read(fname)
            
            self.fontSize = int(config.get('interface', 'fontSize'))
            self.fuelAdd = config.getboolean('switches', 'fuelReq')
            # ac.log("loaded fuel add as : %s" % str(self.fuelAdd))
            self.rangeCalc = int(config.get('switches', 'fuelCalc'))
                
        except Exception as e:
            ac.log(appName + ": Error in loadConf: %s" % e)
            raise
            
    def saveConf(self):
        try:
            fname = dataPath + 'appConf.ini'
            
            config = configparser.ConfigParser()
            config.read(fname)
            
            config.set('interface', 'fontSize', str(self.fontSize))
            config.set('switches', 'fuelCalc', str(self.rangeCalc % 6))
            config.set('switches', 'fuelReq', str(self.fuelAdd))
            
            with open(fname, "wt") as config_file:
                config.write(config_file)
                
        except Exception as e:
            ac.log(appName + ": Error in saveConf: %s" % e)
            raise

    def setInfoLabel(self, text, delay):
        ac.setText(self.infoLabel, text)
        ac.setVisible(self.infoLabel, 1)
        # ac.log("timer: %s" % str(self.timer))
        if self.timer is not None:
            self.timer.cancel()
        self.timer = threading.Timer(delay, ac.setVisible, [self.infoLabel, 0])
        self.timer.start()

    def ShowRangeCalcMethod(self):
        rangeCalc = self.rangeCalc % 6
        if rangeCalc == 0:
            text = 'Range calc:\nL/lap'
        if rangeCalc == 1:
            text = 'Range calc:\nmin(L/lap, Last)'
        if rangeCalc == 2:
            text = 'Range calc:\nmax(L/lap, Last)'
        if rangeCalc == 3:
            text = 'Range calc:\navg(L/lap, Last)'
        elif rangeCalc == 4:
            text = 'Range calc:\nLast'
        elif rangeCalc == 5:
            text = 'Range calc:\nTime'
        gui.setInfoLabel(text, 6)

    def redraw(self):
        ac.drawBorder(self.apx, 0)
        ac.setBackgroundOpacity(self.apx, 0.7)
        ac.drawBackground(self.apx, 0.7)


def fuelClick(dummy, var):
    global gui
    gui.fuelAdd = not gui.fuelAdd
    if gui.fuelAdd:
        ac.setText(gui.fuelReq, 'L Add')
        gui.setInfoLabel('L Add: Fuel to add\nto complete distance', 6)
    else:
        ac.setText(gui.fuelReq, 'L Tot.')
        gui.setInfoLabel('L Tot.: Total fuel\nto complete distance', 6)


def rangeLeftClick(dummy, var):
    gui.rangeCalc = gui.rangeCalc - 1
    gui.ShowRangeCalcMethod()
    # ac.log("left clicked")
    return


def rangeRightClick(dummy, var):
    gui.rangeCalc = gui.rangeCalc + 1
    gui.ShowRangeCalcMethod()
    # ac.log("right clicked")
    return


def MStimetostring(time, unit):
    if unit == 'hour':
        if time <= 0 or time > 21747339 or time != time:
            return "--:--:--"
        else:
            return "{:0>2d}:{:0>2d}:{:0>2d}".format(
                int(time/3600000),
                int((time % 3600000)/60000),
                int((time % 60000)/1000))
    elif unit == 'min':
        if time <= 0 or time > 21747339 or time != time:
            return "--:--.---"
        else:
            return "{:2d}:{:0>2d}.{:0>3d}".format(
                int(time/60000),
                int((time % 60000)/1000),
                time % 1000)
    else:
        if time <= 0 or time > 21747339 or time != time:
            return "--.---"
        else:
            return "{:>2d}.{:0>3d}".format(int(time/1000), time % 1000)
