--------------------------------------------------------------------------------
-- ПУАВ - Поездное Устройство АвтоВедения
--------------------------------------------------------------------------------
Metrostroi.DefineSystem("PUAV")
TRAIN_SYSTEM.DontAccelerateSimulation = true

function TRAIN_SYSTEM:Initialize()
    self.Train:LoadSystem("IPAV")

    self.Train:LoadSystem("KH","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("VAV","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("KSZD","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("VZP","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("VAU","Relay","Switch",{ bass = true, normally_closed = true })
    self.Train:LoadSystem("RC2","Relay","Switch",{ bass = true, normally_closed = true })

    self.Train:LoadSystem("P1","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("P2","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("P3","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("P4","Relay","Switch",{ bass = true })
    self.Train:LoadSystem("P5","Relay","Switch",{ bass = true })

    self.Selected = true


    self.LK16 = false
    self.LAVT = false
    self.LOS = false
    self.ARS = false
    self.LKI1 = false
    self.LKI2 = false

    self.LRSTimer = false

    self["2"] = 0 --Вращение РК
    self["3"] = 0 --Ход 3
    self["8"] = 0 --Замещение электрического торможения
    self["16"] = 0 --Закрытие дверей
    self["17"] = 0  -- Разрешение восстановления реле перегрузки
    self["19"] = 0 -- Разрешение замещения электрического торможения
    self["20"] = 0 -- Включение двигателей
    self["20X"] = 0 -- Разрешение включения двигателей в ходовые режимы
    self["025"] = 0 -- Разрешение ручного торможения
    self["25"] = 0 -- Ручное торможение
    self["31"] = 0 --Разрешение открытия левых дверей
    self["31O"] = 0 --Открытие левых дверей
    self["32"] = 0 --Разрешение открытия правых дверей
    self["32O"] = 0 --Открытие правых дверей
    self["33"] = 0 --Включение ходового режима
    self["033"] = 0 --Разрешение включения ходового режима
    self["33G"] = 0 --Включение режима торможения
    self["39"] = 0 --Включение вентиля замещения № 2
    self["48"] = 0 --Включение вентиля замещения № 1


    self.KRH = 0
    self.KRT = 0
    self.KGR = 0
    self.KRR1 = 0
    self.KRR2 = 0
    self.KRR3 = 0
    self.KD = 0
    self.KPRK = 0
    self.KOAT = 0
    self.KET = 0
    self.KSOT = 0
    self.KDL = 0
    self.KDP = 0
    self.RK1 = 0
    self.KRU = 0

    self.KVARS = 0
    self.KTARS = 0
    self.VAB = 0
    self.KH3 = 0
    self.VZP = 0
    self.KSZD = 0
    self.KB = 0
    self.RD = 0


    self.Ring = false

    self.State = -1

    self.Stopped = false
    self.DoorsOpen = false
    self.Drive = 0
end


if TURBOSTROI then return end
function TRAIN_SYSTEM:Inputs()
    return {  "IPAVCommand" }
end
function TRAIN_SYSTEM:Outputs()
    return { "31","32","31C","32C","16C"}
end

if CLIENT then
    local function createFont(name,font,size)
        surface.CreateFont("Metrostroi_"..name, {
            font = font,
            size = size,
            weight = 500,
            blursize = false,
            antialias = true,
            underline = false,
            italic = false,
            strikeout = false,
            symbol = false,
            rotary = false,
            shadow = false,
            additive = false,
            outline = false,
            extended = true,
            scanlines = false,
        })
    end
    createFont("PUAV","Liquid Crystal Display",35,400)
    function TRAIN_SYSTEM:ClientThink()
        --RunConsoleCommand("say","президент!!!")
        if not self.DrawTimer then
        render.PushRenderTarget(self.Train.PUAV,0,0,512, 128)
            render.Clear(0, 0, 0, 0)
            render.PopRenderTarget()
        end
        if self.DrawTimer and CurTime()-self.DrawTimer < 0.1 then return end
        self.DrawTimer = CurTime()
        render.PushRenderTarget(self.Train.PUAV,0,0,512, 128)
        --render.Clear(0, 0, 0, 0)
        cam.Start2D()
            self:PUAVScreen(self.Train)
        cam.End2D()
        render.PopRenderTarget()
    end
    function TRAIN_SYSTEM:PrintText(x,y,text,inverse)
        local str = {utf8.codepoint(text,1,-1)}
        for i=1,#str do
            local char = utf8.char(str[i])
            if inverse then
                draw.SimpleText(string.char(0x7f),"Metrostroi_PUAV",(x+i)*27+15,y*50+30,Color(0,0,0),TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
                draw.SimpleText(char,"Metrostroi_PUAV",(x+i)*27+15,y*50+30,Color(140,190,0,150),TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
            else
                draw.SimpleText(char,"Metrostroi_PUAV",(x+i)*27+15,y*50+30,Color(0,0,0),TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
            end
        end
    end

    function TRAIN_SYSTEM:PUAVScreen(Train)
        local State = Train:GetNW2Int("PUAV:State",-1)
        if State ~= 0 then
            surface.SetDrawColor(140*0.7,190*0.7,0,self.Warm and 130 or 255)
            self.Warm = true
        else
            surface.SetDrawColor(20,50,0,230)
            self.Warm = false
        end
        surface.DrawRect(0,0,512,128)
        if State == 0 then
            return
        end
        for i=1,6 do
            self:PrintText(-2+i*2,0,Format("%02d",i))
            self:PrintText(-2+i*2,1,Format("%02d",math.max(0,Train:GetNW2Int("PUAV:RK"..i,0))))
        end
        --self:PrintText(0,0,"010101010101")
        --self:PrintText(1,1,Format("РК:%02d",Train:GetNW2Int("PUAV:RK",0)))
        --self:PrintText(1,1,"(точнее криво)")
    end
end

function TRAIN_SYSTEM:Trigger(name,nosnd)
end

function TRAIN_SYSTEM:SetAVPosition(pos)
    if self.Train.KV.AutodrivePosition ~= pos then
        self.Train.KV:TriggerInput("SetAutodrive",pos)
    end
end

local IgnoreDoors = false
function TRAIN_SYSTEM:Think(dT)
    if not self.Selected then return end

    local Train = self.Train
    local ALS = Train.ALSCoil
    local ARS = Train.ALS_ARS
    local EnableARS = ARS.EnableARS
    local UOS = not EnableARS and Train.RC1.Value == 0
    local LeftCoil,RightCoil = Train.LeftAutoCoil,Train.RightAutoCoil

    local power = Train.VB.Value > 0.5 and Train.VAU.Value > 0.5 and Train.Panel["V1"] > 0.5
    local KRUEnabled =  Train.KRU and Train.KRU["14/1-B3"] > 0
    local RVForward = power and (Train.KV["D4-15"] > 0 or KRUEnabled)
    local KVT = Train.PB.Value > 0 or Train.KVT and Train.KVT.Value > 0

    local speed = math.Round(ALS.Speed or 0,1)


    if not power and self.State > -1 then self.State = -1 self.State2Timer = nil end
    if not RVForward and self.State > 0 then self.State = 0 end
    if RVForward and self.State == 0 then self.State = 2 end
    if RVForward and self.State == -1 and not self.State2Timer then self.State = 1 self.State2Timer = CurTime() end
    if RVForward and self.State == 1 and self.State2Timer and CurTime()-self.State2Timer > 2 then self.State = 2 self.State2Timer = nil end
    if self.State > 0 then
        self.NoFreq = ALS.NoFreq
        self.F1 = ALS.F1*(1-self.NoFreq)
        self.F2 = ALS.F2*(1-self.NoFreq)
        self.F3 = ALS.F3*(1-self.NoFreq)
        self.F4 = ALS.F4*(1-self.NoFreq)
        self.F5 = ALS.F5*(1-self.NoFreq)
        self.F6 = ALS.F6*(1-self.NoFreq)
        self.RealF5 = self.F5*(1-(self.F4+self.F3+self.F2+self.F1))
        self.NoFreq = self.NoFreq + (1-math.min(1,self.F5+self.F4+self.F3+self.F2+self.F1))
    else
        self.NoFreq = 0
        self.F1 = 0
        self.F2 = 0
        self.F3 = 0
        self.F4 = 0
        self.F5 = 0
        self.F6 = 0
        self.NoFreq = 0
    end
    if self.State == 2 then
        --Speed limit
        local speedLimit = 20
        if EnableARS and self.F4 > 0 then speedLimit = 40 end
        if EnableARS and self.F3 > 0 then speedLimit = 60 end
        if EnableARS and self.F2 > 0 then speedLimit = 70 end
        if EnableARS and self.F1 > 0 then speedLimit = 80 end

        local ProgrammX = false
        local ProgramDoorLeft = IgnoreDoors
        local ProgramDoorRight = IgnoreDoors
        local ProgrammSBrake = false
        self.LastBrakeProgramTimer =  self.LastBrakeProgramTimer or CurTime()
        self.LastBrakeProgram =  self.LastBrakeProgram or false

        local haveCommand = 0
        for k,v in ipairs(LeftCoil.Commands) do
            v.Power = true
            local command = v.PlateType
            ProgramDoorLeft = ProgramDoorLeft or command == METROSTROI_ACOIL_DOOR
            ProgrammX = ProgrammX or command == METROSTROI_ACOIL_DRIVE and v.Power and v.Mode
            ProgrammSBrake = ProgrammSBrake or command == METROSTROI_ACOIL_SBRAKE and LeftCoil
            haveCommand = haveCommand + 1
        end
        for k,v in ipairs(RightCoil.Commands) do
            v.Power = true
            local command = v.PlateType
            ProgramDoorRight = ProgramDoorRight or command == METROSTROI_ACOIL_DOOR
            ProgrammX = ProgrammX or command == METROSTROI_ACOIL_DRIVE and v.Power and v.Mode
            ProgrammSBrake = ProgrammSBrake or command == METROSTROI_ACOIL_SBRAKE and RightCoil

            haveCommand = haveCommand + 1
        end
        if self.HaveCommand ~= haveCommand then
            self.LRSTimer = CurTime()
            self.HaveCommand = haveCommand
        end
        if self.DoorsOpen == true and (Train.Panel["SD"] <= 0 or Train:ReadTrainWire(16) > 0) then self.DoorsOpen = false end
        if Train.VAV.Value > 0 and self.Stopped then
            if Train.Panel["SD"] > 0 and not self.SDTimer then self.SDTimer = CurTime() end
            if Train.Panel["SD"] <= 0 and self.SDTimer then
                self.SDTimer = false
                self.Autodrive = false
            end
            if self.SDTimer and (self.SDTimer == true or CurTime()-self.SDTimer > 0.2) then
                self.SDTimer = true
                self.Autodrive = self.Autodrive or Train.VZP.Value > 0
            end
        else
            self.Autodrive = false
            self.Stopped = false
        end
        if Train.VAV.Value > 0 and Train.Panel["SD"] <= 0 and not self.Stopped then self.Stopped = CurTime() end
        if Train.VAV.Value > 0 and Train.Panel["SD"] > 0 and (self.Stopped and self.Stopped ~= true and CurTime()-self.Stopped >= 2) then self.Stopped = true end
        if self.Stopped and (Train.VAV.Value == 0 or Train.Panel["SD"] > 0 and (self.Stopped ~= true and CurTime()-self.Stopped < 2)) then self.Stopped = false end
        if not self.Autodrive then
            self.Drive = 0
            self.Brake = false
        end
        if ProgrammSBrake then
            if self.RingArmed == nil then self.RingArmed = CurTime() + math.max(1,math.min(4,5-speed/15)) end
        elseif not ProgrammSBrake and self.RingArmed ~= nil then self.RingArmed = nil end
        if self.RingArmed and CurTime()-self.RingArmed <= 0 and Train:ReadTrainWire(6) > 0 then self.RingArmed = false end
        if self.RingArmed and CurTime()-self.RingArmed > 4  then
            self.RingArmed = false
        end
        local position = 0
        if self.Autodrive and ProgrammSBrake then
            if not self.Brake and ProgrammSBrake then self.Brake = 0 end
            if self.Brake then position = speed > 55 and -2 or -1 end

            local passed = ProgrammSBrake.BrakeProgrammPassed
            local passednow = CurTime()-ProgrammSBrake.LastBrakeProgrammPassed
            if self.Brake == 0 and passed < 1e9 then self.Brake = 1 end
            if self.Brake == 1 and (passed < METROSTROI_ABRAKE_DIST or (passednow > self.Passed*4 and passednow > METROSTROI_ABRAKE_DIST*4)) then self.Brake = 2 end
            if self.Brake == 2 and METROSTROI_ABRAKE_DIST*3 < passed and passed < 1e9 and speed<=25 then self.Brake = 3 end
            if self.Brake == 3 and passed < METROSTROI_ABRAKE_DIST then self.Brake = 4 end
            --if self.Brake == 2 and speed > 25 or self.Brake == 4 then position = -3 end
            if self.Brake == 2 and Train.RheostatController.Position < 12 or self.Brake == 4 then position = -3 end
            self.Passed = passed
        elseif self.Autodrive then
            --print(-1,Train,ProgrammSBrake)
            if self.Brake then
                position = -3
            elseif ProgrammX and ProgrammX then
                --X3
                if ProgrammX == 2 or ProgrammX == 4 then self.Drive = 3 end --X3
                if ProgrammX == 1 or ProgrammX == 3 then self.Drive = 2 end --X2
                if ProgrammX == 5 or ProgrammX == 6 then self.Drive = 0 end --0
                if ProgrammX == 7 then self.Drive = -1 end --T1
                if ProgrammX == 8 then self.Drive = -2 end --T1a
            end
            if self.Brake and (self.Brake <= 1 or speed < 0.1) then
                self.Brake = false
                self.Stopped = false
                if ProgramDoorLeft or ProgramDoorRight then
                    self.DoorsOpen = true
                end
            end
            if position == 0 then position = self.Drive or 0 end
        end
        if position ~= 0 and not self.Starting then self.Starting = CurTime() end
        if position == 0 and     self.Starting then self.Starting = false end
        if (position < -1 or position > 1) and self.Starting and CurTime()-self.Starting < 0.5 then
            position = math.Clamp(position,-1,1)
        end
        if ARS["33D"]*self["33D"] == 0 and position > 0 then position = 0 end
        --if Train.RheostatController.Position < 17.2 then print(Train.Electric.I13,Train.RheostatController.Position,speed,Train.Electric.ThyristorState,Train.Electric.ThyristorResistance,Train.ThyristorBU5_6.Value) end

        self:SetAVPosition(position)

        if ProgrammSBrake then ProgrammSBrake.BrakeProgrammPassed = 1e9 end
        local zero = (self.NoFreq*(Train.ALS.Value+Train.VRD.Value)) > 0

        --PUAV ARS logic
        if not EnableARS then
            self.Ringing = true
            self.EBrake = true
        end
        if self.Ringing and KVT then self.Ringing = false end
        if self.EBrake and KVT then self.EBrake = false end
        if speed >= speedLimit and not ARS.ElectricBrake and not self.Pneumo then
            self.Ringing = true
            self.Pneumo = speed == speedLimit
            self.EBrake = speed > speedLimit
        end
        if (speed < speedLimit or ARS.ElectricBrake) and EnableARS then
            self.Pneumo = false
            self.EBrake = false
        end

        local CloseDoors = (not ProgramDoorLeft and not ProgramDoorRight and not zero) or speed > 0.2 or Train.VAV.Value > 0 and Train.KSZD.Value > 0
        local DoorLeftWork = Train:ReadTrainWire(16)  == 0 and (ProgramDoorLeft or zero)
        local DoorRightWork = Train:ReadTrainWire(16)  == 0 and (ProgramDoorRight or zero)
        self.LK16 = Train:ReadTrainWire(16) > 0
        self.LAVT = Train.VAV.Value > 0
        self.LOS = UOS
        self.LRS =
            not ProgramDoorLeft and not ProgramDoorRight and EnableARS
            and Train.EK.Value > 0 and Train.EK1.Value > 0 and Train.SOT.Value > 0 and (not self.LRSTimer or CurTime()-self.LRSTimer > 0.2)
        self.LKI1 = false
        self.LKI2 = false

        self["31"] = DoorLeftWork and self.DoorsOpen and 1 or 0
        self["32"] = DoorRightWork and self.DoorsOpen and 1 or 0
        self["31C"] = DoorLeftWork and 1 or 0
        self["32C"] = DoorRightWork and 1 or 0
        self["16C"] = CloseDoors and 1 or 0

        --local zbrake = speedLimit <= 20 and not KVT
        self["33D"] = (self.EBrake or zbrake) and 0 or 1
        self["33G"] = (self.EBrake or zbrake) and 1 or 0
        self["2"] = self["33G"]
        self["20"] = self["33G"]
        self["48"] = self.Pneumo and 1 or 0
        self["8"] = self["33G"]
        if Train.KV["U2-25"] > 0 or KVT and speedLimit == 20 then
            self["8"] = -1
        end
        if (Train.KV["7G-33Yu"] > 0 or Train.KSZD.Value > 0) and self.KSZDRing then self.KSZDRing = false end
        for i,train in ipairs(Train.WagonList) do
            if train.RheostatController then
                Train:SetNW2Int("PUAV:RK"..i,math.floor(train.RheostatController.Position+0.5))
            end
        end
        self.Ring = (self.EBrake or self.Ringing) and CurTime()%2 > 1 or self.KSZDRing
    elseif self.State == 1 then
        self.LK16 = true
        self.LAVT = true
        self.LOS = true
        self.LRS = true
        self.LKI1 = true
        self.LKI2 = true
        self.Ring = CurTime()-self.State2Timer < 1

        self.EBrake = true
        self.Ringing = true
    else
        self.LK16 = false
        self.LAVT = false
        self.LOS = false
        self.LRS = false
        self.LKI1 = false
        self.LKI2 = false

        self["31"] = 0
        self["32"] = 0
        self["31C"] = 1
        self["32C"] = 1
        self["16C"] = 0

        self["33D"] = 1
        self["33G"] = 0
        self["2"] = 0
        self["20"] = 0
        self["48"] = 0
        self["8"] = 0


        self.LRSTimer = false
        self.HaveCommand = false

        self.Ring = false

        self.EBrake = true
        self.Ringing = true
        self.Pneumo = false
        self.RingArmed = nil

        self:SetAVPosition(0)
    end
    if EnableARS then
        if (ARS.F5 > 0 or ARS.F6 > 0) then
            if not self.ZeroTimer then
                self.ZeroTimer = CurTime()+30
                self.ZeroTimerState = true
            end
            if self.ZeroTimerState and self.ZeroTimer and CurTime()-self.ZeroTimer > 7.5 and Train.ALSCoil.Speed < 0.25 then
                self.ZeroTimerState = false
                self.ZeroTimer = CurTime()+30
            end
        elseif self.ZeroTimer ~= nil then
            self.ZeroTimer = nil
        end
    else
        self.ZeroTimer = nil
    end
    --[[
    if self.Train.VZP.Value > 0.5 then
        Train.Autodrive:Enable()
    end
    if Train.VAV.Value < 0.5 then
        Train.Autodrive:Disable()
    end
    if self.Timer and CurTime() - self.Timer > 0 then
        self.Timer = nil
        self.Choose = 0
    end
    --self.FirstStation = Metrostroi.EndStations[Train.Announcer.AnnMap][self.Line] and Metrostroi.EndStations[Train.Announcer.AnnMap][self.Line][self.ChoosedFStation or 1] or 0
    --self.LastStation = Metrostroi.EndStations[Train.Announcer.AnnMap][self.Line] and Metrostroi.EndStations[Train.Announcer.AnnMap][self.Line][self.ChoosedLStation or 1] or 0
    if self.State ~= self.RealState then
        self.RealState = self.State
        self.TimeOverride = true
    end

    self.Time = self.Time or CurTime()
    if (CurTime() - self.Time) > 0.1 or self.TimeOverride then
        self.TimeOverride = nil
        --print(1)
        self.Time = CurTime()
        Train:SetNW2Int("PUAV:Choose",self.Choose)
        Train:SetNW2Int("PUAV:LastStation",self.LastStation or 1)
        Train:SetNW2Int("PUAV:FirstStation",self.FirstStation or 1)
        Train:SetNW2Int("PUAV:Line",self.Line or 1)
    end
    self.RouteNumber = string.gsub(Train.RouteNumber or "","^(0+)","")
    self.Line = Train.UPO.Line
    self.FirstStation = tostring(Train.UPO.FirstStation or "")
    self.LastStation = tostring(Train.UPO.LastStation or "")
    local pathID
    if (Metrostroi.TrainPositions[self.Train] and Metrostroi.TrainPositions[self.Train][1]) then
        PathID = Metrostroi.TrainPositions[self.Train][1].path.id
    end
    Train:SetPackedBool("LKI",self.Train.Autodrive.Commands[PathID] and self.Train.Autodrive.Commands[PathID][self.Train.UPO.Station] ~= nil)]]
end
