--------------------------------------------------------------------------------
-- ARS-MP safety system
--------------------------------------------------------------------------------
-- Copyright (C) 2013-2018 Metrostroi Team & FoxWorks Aerospace s.r.o.
-- Contains proprietary code. See license.txt for additional information.
--------------------------------------------------------------------------------
Metrostroi.DefineSystem("ARS_MP")
TRAIN_SYSTEM.DontAccelerateSimulation = false

function TRAIN_SYSTEM:Initialize()
    self.Train:LoadSystem("UOS","Relay","Switch", {bass = true})
    self.Train:LoadSystem("ALSFreq","Relay","Switch",{bass=true})
    self.Train:LoadSystem("AB1","Relay","Switch",{bass=true})
    self.Train:LoadSystem("AB2","Relay","Switch",{bass=true})
    self.Train:LoadSystem("EPKContacts","Relay","",{close_time = 3})
    self.Train:LoadSystem("ROT1","Relay","", { bass = true})
    self.Train:LoadSystem("ROT2","Relay","", { bass = true})

    self.Train:LoadSystem("EPKC","Relay")
    -- Internal state
    self.SpeedLimit = 0
    self.NextLimit = 0
    self.AB = 0
    self.AV = 0
    self.AV1 = 0
    self.ARSRing = 0
    self.KRT = 0
    self.KRO = 0
    self.KRH = 0
    --[[ self.Overspeed = false
    self.RUVD = false
    self.PneumaticBrake1 = false
    self.PneumaticBrake2 = true
    self.AttentionPedal = false--]]

    self.KVT = false
    self.LN = 0
    self["2"] = 0
    self["25"] = 0
    self["20"] = 0
    self["33G"] = 0
    self["33"] = 0
    self["17"] = 0
    self["8"] = 0
    self["44"] = 0
    self["48"] = 0

    self.EK = 1

    self.F6 = 0
    self.F5 = 0
    self.PrevF5 = 0
    self.F4 = 0
    self.F3 = 0
    self.F2 = 0
    self.F1 = 0
    self.NoFreq = 0
    self.PrevNoFreq = 0

    self.ARS = 0
    self.AB = 0
    self.AV = 0
    self.AV1 = 0

    self.ABReady = 0

    -- Lamps
    ---self.LKT = false
    self.LVD = 0
    self.Ring = 0
end

function TRAIN_SYSTEM:Outputs()
    return {
        --"2","25","20","33G","33","17","8","44","48",
        "NoFreq","F1","F2","F3","F4","F5","F6","LN","ARS","AB","KT","LVD","ABReady"
    }
end

function TRAIN_SYSTEM:Inputs()
    return { "IgnoreThisARS","AttentionPedal","Ring" }
end

function TRAIN_SYSTEM:TriggerInput(name,value)
end


function TRAIN_SYSTEM:Think(dT)
    local Train = self.Train
    local ALS = Train.ALSCoil
    local speed = math.Round(ALS.Speed or 0,1)

    local power = self.GE > 0
    local TwoToSix = self.Freq  > 0
    -- ALS, ARS state

    if power and not self.EnableARSTimer then
        self.EnableARSTimer = CurTime()+1+math.random()*9
    elseif not power and self.EnableARSTimer then
        self.EnableARSTimer = nil
    end

    if ALS.Enabled~=self.ALS then
        ALS:TriggerInput("Enable",self.ALS)
    end
    local Power = self.EnableARSTimer and CurTime()-self.EnableARSTimer > 0

    local KVT = self.KB > 0

    local F1 = ALS.F1*self.ALS
    local F2 = ALS.F2*self.ALS
    local F3 = ALS.F3*self.ALS
    local F4 = ALS.F4*self.ALS
    local F5 = ALS.F5*self.ALS
    local F6 = ALS.F6*self.ALS
    local FreqCount = F1+F2+F3+F4+F5+F6
    local TwoFreq = FreqCount==2
    if self.AB > 0 then
        if FreqCount>0 then self.AB = 0 end
        F1 = 1
        F2 = 0
        F3 = 0
        F4 = 0
        F5 = 0
        F6 = 0
    elseif TwoToSix and not TwoFreq then
        F1 = 0
        F2 = 0
        F3 = 0
        F4 = math.min(1,F1+F2+F3+F4)
    elseif not TwoToSix and TwoFreq then
        F1 = 0
        F2 = 0
        F3 = 0
        F4 = 0
        F5 = 0
        F6 = 0
    end
    local FreqCode = bit.bor(F1*1,F2*2,F3*4,F4*8,F5*16,F6*32,self.ALS*64,self.GE*128)
    if self.FreqCode ~= FreqCode then
        if not self.FreqCodeTimer then self.FreqCodeTimer = CurTime() end
        if self.FreqCodeTimer and CurTime()-self.FreqCodeTimer>0.8 then
            self.FreqCode = FreqCode
            self.FreqCodeTimer = nil

            self.F1 = F1
            self.F2 = F2
            self.F3 = F3
            self.F4 = F4
            self.F5 = F5
            self.F6 = F6
            self.NoFreq = (1-math.min(1,(self.F1+self.F2+self.F3+self.F4+self.F5+self.F6)))*math.min(1,self.ALS+self.GE)
        end
    elseif self.FreqCodeTimer then
        self.FreqCodeTimer = nil
    end
    if power or self.ALS > 0 --[[ or self.AB > 0--]]  then
        local Vlimit = 20
        if self.F4 > 0 then Vlimit = 40 end
        if self.F3 > 0 then Vlimit = 60 end
        if self.F2 > 0 then Vlimit = 70 end
        if self.F1 > 0 then Vlimit = 80 end
        -- Determine next limit and current limit
        self.SpeedLimit = Vlimit

        self.NextLimit = Vlimit
        if self.F1 > 0 then self.NextLimit = 80 end
        if self.F2 > 0 then self.NextLimit = 70 end
        if self.F3 > 0 then self.NextLimit = 60 end
        if self.F4 > 0 then self.NextLimit = 40 end
        if self.F5 > 0 then self.NextLimit = 20 end

        if self.F4 > 0 and self.F6 > 0 then
            self.NG = true
        end
        if TwoToSix and self.LN==0 and self.AB==0 then self.SpeedLimit=20 end
        if KVT and self.SpeedLimit <= 40 then self.SpeedLimit = 20 end
        if KVT and self.SpeedLimit > 40 then self.SpeedLimit = 40 end
    else
        self.F1 = 0
        self.F2 = 0
        self.F3 = 0
        self.F4 = 0
        self.F5 = 0
        self.F6 = 0
        self.SpeedLimit = 0
        self.NextLimit = 0
        self.NoFreq = 0
        self.FreqCodeTimer = nil
        self.FreqCode = 0
    end
    if Power then
        self.ABReady = (1-self.AB)*(FreqCount*self.ALS == 0 and 1 or 0)
        local ABAccept = self.ABReady > 0 --self.NoFreq--[[ *ALS.Enabled--]]  > 0
        if ABAccept and not self.ABPressed1 and Train.AB1.Value > 0 then self.ABPressed1 = CurTime() end
        if ABAccept and not self.ABPressed2 and Train.AB2.Value > 0 then self.ABPressed2 = CurTime() end
        if not ABAccept or Train.AB1.Value == 0 then self.ABPressed1 = nil end
        if not ABAccept or Train.AB2.Value == 0 then self.ABPressed2 = nil end
        if self.ABPressed1 and self.ABPressed2 and math.abs(self.ABPressed1-self.ABPressed2) < 1 then
            self.AB = 1
        end
        self.ARS = 1-self.AB
        local NoFreq = (self.Freq>0 and self.LN==0) or self.NoFreq>0
        self.BR2 = KVT and NoFreq and not self.BR1
        self.BR1 = KVT and (self.BR1 or not NoFreq) and not self.BR2


        local SpeedLimit = self.SpeedLimit
        if (speed > SpeedLimit) and not self.RUVD or SpeedLimit<=20 and not (self.BR1 or self.BR2) then
            self.RUVD = true
            self.RNT = self.RNT or self.KRT==0
        elseif speed < SpeedLimit-0.5 and self.RUVD and not self.RNT then
            self.RUVD = false
        end
        if (self.BR1 or self.BR2) and self.RNT then
            self.RNT = false
        end
        if self.RO==true and self.KRH > 0 then
            self.RO = CurTime()
        end
        if self.RO and self.RO~=true and CurTime()-self.RO >7 then
            self.RO = false
            if speed<5 and not NoFreq then self.ROBrake = true end
        end
        if self.RO and speed > 5 then self.RO = false end
        if self.RO~=true and speed <= 5 and self.KRH == 0 then self.RO = true end
        if self.RO and self.BR2 then self.RO = false end

        if self.RUVD then
            if not self.PN1Timer then self.PN1Timer = CurTime()+3.5-math.max(0,(speed-20))/60*2.5 end
            if not self.TW8Timer then self.TW8Timer = self.SpeedLimit<=20 and CurTime()-3 or CurTime() end
        end
        if not self.RUVD then
            if self.PN1Timer then self.PN1Timer = false end
            if self.TW8Timer then self.TW8Timer = false end
        end

        local brake = (self.RUVD or self.ROBrake) and 1 or 0
        local pn1 = (self.RO == true or (self.PN1Timer and CurTime()-self.PN1Timer < 0)) and 1 or 0
        local pn2 = (self.TW8Timer and CurTime()-self.TW8Timer > 2.7) and 1 or 0

        self["33"] = (1-self.LVD)
        self["33G"] = brake
        self["17"] = (1-self.LVD)
        self["2"] = brake*self.KRT
        self["20"] = brake
        self["25"] = (1-self.LVD)
        self["44"] = pn1
        self["48"] = pn1
        self["8"] = brake*pn2
        self.Ring = self.RNT and 1 or 0

        local delay = 3.5
        if 10 < speed and speed < 30 then delay = 5.5 end
        if speed < 3 then delay = 10 end
        if (self.RUVD or speed<5 and not NoFreq) and self.KT == 0 then
            if not self.EPKTimer then self.EPKTimer = CurTime() end
        else
            self.EPKTimer = nil
        end
        if self.EPKTimer and CurTime()-self.EPKTimer > delay then
            self.EK = 0
        end
        self.LVD = math.min(1-self.KRO,self.LVD+brake)
        self.LN = self.NG and self.Freq or 0
    else
        self.BR1 = 0
        self.BR2 = 0
        self.EK = 1
        self.AB = 0
        self.ARS = 0
        self.LN = 0
        self.NG = false
        self.ABPressed1 = nil
        self.ABPressed2 = nil
        self.Ring = 0
        self.ROBrake = false
        self.LVD = 0
        self.ABReady = 0

        self.RUVD = true
        self.RNT = true

        self["33"] = 0
        self["33G"] = 0
        self["17"] = 0
        self["2"] = 0
        self["20"] = 0
        self["25"] = 0
        self["44"] = 0
        self["48"] = 0
        self["8"] = 0
    end


    self.EPK = self.GE*self.EK
    --Train:WriteTrainWire(90,self.EPK*(1-Train.ARS.Value))
    --Train.EPKC:TriggerInput("Set",self.EPK+Train:ReadTrainWire(90))
    Train.EPKC:TriggerInput("Set",self.EPK)
end
