include("shared.lua")

--------------------------------------------------------------------------------
function ENT:Initialize()
    self.Sig = ""
    self.OldName = ""
    self.Models = {{},{},{}}
    self.Signals = {}
    self.Anims = {}
end

function ENT:Animate(clientProp, value, min, max, speed, damping, stickyness)
    local id = clientProp
    if not self.Anims[id] then
        self.Anims[id] = {}
        self.Anims[id].val = value
        self.Anims[id].V = 0.0
    end

    if damping == false then
        local dX = speed * self.DeltaTime
        if value > self.Anims[id].val then
            self.Anims[id].val = self.Anims[id].val + dX
        end
        if value < self.Anims[id].val then
            self.Anims[id].val = self.Anims[id].val - dX
        end
        if math.abs(value - self.Anims[id].val) < dX then
            self.Anims[id].val = value
        end
    else
        -- Prepare speed limiting
        local delta = math.abs(value - self.Anims[id].val)
        local max_speed = 1.5*delta / self.DeltaTime
        local max_accel = 0.5 / self.DeltaTime

        -- Simulate
        local dX2dT = (speed or 128)*(value - self.Anims[id].val) - self.Anims[id].V * (damping or 8.0)
        if dX2dT >  max_accel then dX2dT =  max_accel end
        if dX2dT < -max_accel then dX2dT = -max_accel end

        self.Anims[id].V = self.Anims[id].V + dX2dT * self.DeltaTime
        if self.Anims[id].V >  max_speed then self.Anims[id].V =  max_speed end
        if self.Anims[id].V < -max_speed then self.Anims[id].V = -max_speed end

        self.Anims[id].val = math.max(0,math.min(1,self.Anims[id].val + self.Anims[id].V * self.DeltaTime))

        -- Check if value got stuck
        if (math.abs(dX2dT) < 0.001) and stickyness and (self.DeltaTime > 0) then
            self.Anims[id].stuck = true
        end
    end
    return min + (max-min)*self.Anims[id].val
end
--------------------------
-- MAIN SPAWN FUNCTIONS --
--------------------------
function ENT:SpawnMainModels(pos,ang,LenseNum,add)
    local TLM = self.TrafficLightModels[self.LightType]
    for k,v in pairs(TLM) do
        if type(v) == "string" and not k:find("long") then
            local idx = add and v..add or v
            if IsValid(self.Models[1][idx]) then break else
                local k_long = k.."_long"
                if TLM[k_long] and LenseNum >= 7 then
                    self.Models[1][idx] = ClientsideModel(TLM[k_long],RENDERGROUP_OPAQUE)
                    self.LongOffset = Vector(0,0,TLM[k.."_long_pos"])
                else
                    self.Models[1][idx] = ClientsideModel(v,RENDERGROUP_OPAQUE)
                end
                self.Models[1][idx]:SetPos(self:LocalToWorld(pos))
                self.Models[1][idx]:SetAngles(self:LocalToWorldAngles(ang))
                self.Models[1][idx]:SetParent(self)
            end
        end
    end
end

function ENT:SpawnHeads(ID,model,pos,ang,glass,notM,add)
    if not IsValid(self.Models[1][ID]) then
        self.Models[1][ID] = ClientsideModel(model,RENDERGROUP_OPAQUE)
        self.Models[1][ID]:SetPos(self:LocalToWorld(pos))
        self.Models[1][ID]:SetAngles(self:LocalToWorldAngles(ang))
        self.Models[1][ID]:SetParent(self)
    end
    if self.RN and self.RN == self.RouteNumbers.sep then
        self.RN = self.RN + 1
    end
    local id = self.RN
    local rouid = id and "rou"..id
    if rouid and not IsValid(self.Models[1][rouid]) then
        local rnadd = ((self.RouteNumbers[id] and self.RouteNumbers[id][1] ~= "X") and (self.RouteNumbers[id][3] and not self.RouteNumbers[id][2] and "2" or "") or "5")
        local LampIndicator = self.TrafficLightModels[self.LightType].LampIndicator
        self.Models[1][rouid] = ClientsideModel(LampIndicator.model..rnadd..".mdl",RENDERGROUP_OPAQUE)
        self.Models[1][rouid]:SetPos(self:LocalToWorld(pos-self.RouteNumberOffset*(self.Left and LampIndicator[1] or LampIndicator[2])))
        self.Models[1][rouid]:SetAngles(self:GetAngles())
        self.Models[1][rouid]:SetParent(self)
        if self.RouteNumbers[id] then self.RouteNumbers[id].pos = pos-self.RouteNumberOffset*(self.Left and LampIndicator[1] or LampIndicator[2]) end
        self.RN = self.RN + 1
    end
    if notM then
        if glass then
            local ID_glass = tostring(ID).."_glass"
            for i,tbl in pairs(glass) do
                local ID_glassi = ID_glass..i
                if not IsValid(self.Models[1][ID_glassi]) then  --NEWLENSES
                    self.Models[1][ID_glassi] = ClientsideModel(tbl[1],RENDERGROUP_OPAQUE)
                    self.Models[1][ID_glassi]:SetPos(self:LocalToWorld(pos+tbl[2]*(add and Vector(-1,1,1) or 1)))
                    self.Models[1][ID_glassi]:SetAngles(self:LocalToWorldAngles(ang))
                    self.Models[1][ID_glassi]:SetParent(self)
                end
            end
        end
    end
end

function ENT:SetLight(ID,ID2,pos,ang,skin,State,Change)
    local IsStateAboveZero = State > 0
    local IDID2 = ID..ID2
    local IsModelValid = IsValid(self.Models[3][IDID2])
    if IsModelValid then
        if IsStateAboveZero then 
            if Change then 
                self.Models[3][IDID2]:SetColor(Color(255,255,255,State*255))
            end
        else
            self.Models[3][IDID2]:Remove()
        end
    elseif IsStateAboveZero then
        self.Models[3][IDID2] = ClientsideModel(self.TrafficLightModels[self.LightType].LampBase.model,RENDERGROUP_OPAQUE)
        self.Models[3][IDID2]:SetPos(self:LocalToWorld(pos))
        self.Models[3][IDID2]:SetAngles(self:LocalToWorldAngles(ang))
        self.Models[3][IDID2]:SetSkin(skin)
        self.Models[3][IDID2]:SetParent(self)
        self.Models[3][IDID2]:SetRenderMode(RENDERMODE_TRANSCOLOR)
        -- self.Models[3][IDID2]:SetColor(Color(255, 255, 255, 0))
        self.Models[3][IDID2]:SetColor(Color(255,255,255,State*255))
    end
end

function ENT:SpawnLetter(i,model,pos,letter,double)
    local LetMaterials = self.TrafficLightModels[self.LightType].LetMaterials.str
    local LetMaterialsStart = LetMaterials.."let_start"
    local LetMaterialsletter = LetMaterials..letter
    if double ~= false and not IsValid(self.Models[2][i]) and (self.Double or not self.Left) and (not letter:match("s[1-3]") or letter == "s3" or self.Double and self.Left) then
        self.Models[2][i] = ClientsideModel(model,RENDERGROUP_OPAQUE)
        self.Models[2][i]:SetAngles(self:LocalToWorldAngles(Angle(0,180,0)))
        self.Models[2][i]:SetPos(self:LocalToWorld(self.BasePosition+pos))
        self.Models[2][i]:SetParent(self)
        for k,v in pairs(self.Models[2][i]:GetMaterials()) do
            if v:find(LetMaterialsStart) then
                self.Models[2][i]:SetSubMaterial(k-1,LetMaterialsletter)
            end
        end
    end
    local id = i.."d"
    if not double and not IsValid(self.Models[2][id]) and (self.Double or self.Left) and (not letter:match("s[1-3]") or letter == "s3" or self.Double and not self.Left) then
        self.Models[2][id] = ClientsideModel(model,RENDERGROUP_OPAQUE)
        self.Models[2][id]:SetAngles(self:LocalToWorldAngles(Angle(0,180,0)))
        self.Models[2][id]:SetPos(self:LocalToWorld((self.BasePosition+pos)*Vector(-1,1,1)))
        self.Models[2][id]:SetParent(self)
        for k,v in pairs(self.Models[2][id]:GetMaterials()) do
            if v:find(LetMaterialsStart) then
                self.Models[2][id]:SetSubMaterial(k-1,LetMaterialsletter)
            end
        end
    end
end

function ENT:OnRemove()
    self:RemoveModels()
end

function ENT:RemoveModels(final)
    if self.Models and  self.Models.have then
        for _,v in pairs(self.Models) do if type(v) == "table" then for _,v1 in pairs(v) do v1:Remove() end end end
    end
    self.Models = {{},{},{}}
    self.ModelsCreated = false
end

net.Receive("metrostroi-signal", function()
    local ent = net.ReadEntity()
    if not IsValid(ent) then return end
    ent.LightType = net.ReadInt(3)
    ent.Name = net.ReadString()
    --ent.Name = " BUDAPEiT"..string.gsub(ent.Name,"[A-Za-z]*","")
    ent.Lenses = net.ReadString()
    ent.ARSOnly = ent.Lenses == "ARSOnly"
    ent.RouteNumberSetup = net.ReadString()
    ent.Left = net.ReadBool()
    ent.Double = net.ReadBool()
    ent.DoubleL = net.ReadBool()
    ent.AutostopPresent = net.ReadBool()
    if not ent.ARSOnly then
        ent.LensesTBL = string.Explode("-",ent.Lenses)
    end
    if ent.RemoveModels then ent:RemoveModels() end
end)

function ENT:Think()
    local CurTime = CurTime()
    self:SetNextClientThink(CurTime + 0.0333)
    self.PrevTime = self.PrevTime or RealTime()
    self.DeltaTime = (RealTime() - self.PrevTime)
    self.PrevTime = RealTime()
    if self:IsDormant() or Metrostroi and Metrostroi.ReloadClientside then
        if not self.ReloadModels and self.ModelsCreated then
            self:RemoveModels()
        end
        return true
    end

    if self.ReloadModels then
        self.ReloadModels = false
        self:RemoveModels()
    end

    if not self.Name then
        if self.sended and (CurTime - self.sended) > 0 then
            self.sended = nil
        end
        if not self.sended then
            net.Start("metrostroi-signal")
                net.WriteEntity(self)
            net.SendToServer()
            self.sended = CurTime + 1.5
        end
        return true
    end
    local TLM = self.TrafficLightModels[self.LightType]

    if not self.ModelsCreated then
        local ID = 0
        local ID2 = 0
        -- Create new clientside models
        if not self.ARSOnly then
            --SPAWN A OLD ROUTE Numbers
            --оператор # съедает больше производительности, чем исопльзование своей переменной с хранением количества элементов в таблице
            --поэтому добавляю каунтеры
            --TODO вообще сравнить бы это здесь xD
            local rn1 = {}
            local rn1N = 0
            local rn2 = {}
            self.RouteNumbers = {}
            self.SpecRouteNumbers = {}
            for i=1,#self.RouteNumberSetup do
                local CurRN = self.RouteNumberSetup[i]
                if self.OldRouteNumberSetup[1]:find(CurRN) then
                    rn1N = table.insert(rn1,CurRN)
                elseif self.OldRouteNumberSetup[2]:find(CurRN) then
                    table.insert(rn2,CurRN)
                elseif self.OldRouteNumberSetup[3]:find(CurRN) then
                    table.insert(self.SpecRouteNumbers,{CurRN,CurRN == "F"})
                end
            end
            for i=1,rn1N,2 do
                table.insert(self.RouteNumbers,{rn1[i],rn1[i+1],true})
            end
            for k,v in pairs(rn2) do
                table.insert(self.RouteNumbers,{v})
            end
            self.Arrow = nil

            for k,v in pairs(self.SpecRouteNumbers) do
                if not v[2] then
                    self.Arrow = k
                    self.SpecRouteNumbers = v
                    break
                end
            end
            local LenseNum = self.Arrow and 1 or 0
            local OneLense = self.Arrow == nil
            for k,v in ipairs(self.LensesTBL) do
                if k > 1 and v:find("[RGBWYM]+") then
                    OneLense = false
                end
                for i=1,#v do
                    if v[i]:find("[RGBWYM]") then
                        LenseNum = LenseNum+1
                    end
                end
            end
            if LenseNum == 0 then OneLense = false end
            local offset = self.RenderOffset[self.LightType] or Vector(0, 0, 0)
            self.LongOffset = self.LongOffset or Vector(0, 0, 0)
            if not self.Left or self.Double then self:SpawnMainModels(self.BasePosition,Angle(0, 0, 0),LenseNum) end
            if self.Left or self.Double then self:SpawnMainModels(self.BasePosition*Vector(-1,1,1),Angle(0,180,0),LenseNum,self.Double and "d" or nil) end


            if not self.RouteNumbers.sep and #self.RouteNumbers > 1 then
                self.RouteNumbers.sep = 2
            elseif not self.RouteNumbers.sep and #self.RouteNumbers > 0 then
                self.RouteNumbers.sep = 1
            end
            if self.RouteNumbers.sep and self.RouteNumbers[self.RouteNumbers.sep][1] ~= "X" then
                local id = self.RouteNumbers.sep
                local rnadd = self.RouteNumbers[id][3] and not self.RouteNumbers[id][2] and "3" or "4"
                self.Models[1]["rous"] = ClientsideModel(TLM.LampIndicator.model..rnadd..".mdl",RENDERGROUP_OPAQUE)
                self.RouteNumbers[id].pos = (self.BasePosition+offset+self.LongOffset-TLM.LampIndicator[3])
                if self.Left then self.RouteNumbers[id].pos = self.RouteNumbers[id].pos*TLM.LampIndicator[4] end
                self.Models[1]["rous"]:SetPos(self:LocalToWorld(self.RouteNumbers[id].pos))
                self.Models[1]["rous"]:SetAngles(self:GetAngles())
                self.Models[1]["rous"]:SetParent(self)
            end
            if #self.RouteNumbers > 0 and (#self.RouteNumbers ~= 1 or not self.RouteNumbers.sep) then
                self.RN = 1
                self.RouteNumberOffset = TLM.RouteNumberOffset
                offset = offset + self.RouteNumberOffset*(self.Left and Vector(-1,1,1) or 1)
            else
                self.RouteNumberOffset = nil
                self.RN = nil
            end
            if self.AutostopPresent then
                if not IsValid(self.Models[1]["autostop"]) then
                    self.Models[1]["autostop"] = ClientsideModel(self.AutostopModel[1],RENDERGROUP_OPAQUE)
                    self.Models[1]["autostop"]:SetPos(self:LocalToWorld(self.BasePosition+self.AutostopModel[2]))
                    self.Models[1]["autostop"]:SetAngles(self:GetAngles())
                    self.Models[1]["autostop"]:SetParent(self)
                end
            end
            self.NamesOffset = Vector(0, 0, 0)
            -- Create traffic light models
            --if self.LightType > 2 then self.LightType = 2 end
            --if self.LightType < 0 then self.LightType = 0 end
            local first = true
            for _,v in ipairs(self.LensesTBL) do
                local data
                if not self.TrafficLightModels[self.LightType][v] then
                    data = self.TrafficLightModels[self.LightType][#v-1]
                else
                    if v == "M" then
                        self.RouteNumber = ID
                    end
                    data = self.TrafficLightModels[self.LightType][v]
                end
                if not data then continue end
                local vec = Vector(0,0,data[1])
                if first then
                    first = false
                else
                    offset = offset - vec
                end

                self.NamesOffset = self.NamesOffset + vec
                local offsetAndLongOffset = offset + self.LongOffset
                if not self.Left or self.Double then    self:SpawnHeads(ID,data[2],self.BasePosition + offsetAndLongOffset,Angle(0, 0, 0),data[3] and data[3].glass,v~="M") end
                if self.Left or self.Double then self:SpawnHeads((self.Double and ID.."d" or ID),(not TLM.noleft) and data[2]:Replace(".mdl","_mirror.mdl") or data[2],self.BasePosition*Vector(-1,1,1) + offsetAndLongOffset,Angle(0, 0, 0),data[3] and data[3].glass,v~="M",true) end
                if v ~= "M" then
                    for i = 1,#v do
                        ID2 = ID2 + 1
                        if not self.Signals[ID2] then self.Signals[ID2] = {} end
                        if not self.DoubleL then
                            self:SetLight(ID,ID2,self.BasePosition*(self.Left and Vector(-1,1,1) or 1) + offsetAndLongOffset + data[3][i-1]*(self.Left and Vector(-1,1,1) or 1),Angle(0, 0, 0),self.SignalConverter[v[i]]-1,0    )
                        else
                            self:SetLight(ID,ID2,self.BasePosition + offsetAndLongOffset + data[3][i-1],Angle(0, 0, 0),self.SignalConverter[v[i]]-1,0)
                            self:SetLight(ID,ID2.."x",self.BasePosition*Vector(-1,1,1) + offsetAndLongOffset + data[3][i-1]*Vector(-1,1,1),Angle(0, 0, 0),self.SignalConverter[v[i]]-1,0)
                        end
                    end
                end

                ID = ID + 1
            end
            if self.Arrow then
                local id = self.Arrow
                self.Models[1]["roua"] = ClientsideModel(TLM.LampIndicator.model.."4.mdl",RENDERGROUP_OPAQUE)
                self.SpecRouteNumbers.pos = (self.BasePosition+offset+self.LongOffset-TLM.LampIndicator[5])*(self.Left and TLM.LampIndicator[6] or 1) - (self.RouteNumberOffset or Vector(0, 0, 0))
                self.Models[1]["roua"]:SetPos(self:LocalToWorld(self.SpecRouteNumbers.pos))
                self.Models[1]["roua"]:SetAngles(self:LocalToWorldAngles(self.Left and Angle(-90,0,0) or Angle(90,0,0)))
                self.Models[1]["roua"]:SetParent(self)
            end
            offset = self.RenderOffset[self.LightType]+(OneLense and TLM.name_one or TLM.name)+(OneLense and self.RouteNumberOffset or Vector(0, 0, 0))
            if self.LightType == 1 then
                offset = offset - self.NamesOffset
            end
            local double = self.LightType ~= 1 and string.find(self.Name,"^[A-Z][A-Z]")
            if double then
                    if not self.Left or self.Double then
                        self:SpawnLetter(0,TLM.SignLetterSmall.model,offset - TLM.SignLetterSmall[2],(Metrostroi.LiterWarper[self.Name[0+1]] or self.Name[0+1]),true)
                        self:SpawnLetter(1,TLM.SignLetterSmall.model,offset - TLM.SignLetterSmall[1],(Metrostroi.LiterWarper[self.Name[1+1]] or self.Name[1+1]),true)
                    end
                    if self.Left or self.Double then
                        self:SpawnLetter(0,TLM.SignLetterSmall.model,offset - TLM.SignLetterSmall[1],(Metrostroi.LiterWarper[self.Name[0+1]] or self.Name[0+1]),false)
                        self:SpawnLetter(1,TLM.SignLetterSmall.model,offset - TLM.SignLetterSmall[2],(Metrostroi.LiterWarper[self.Name[1+1]] or self.Name[1+1]),false)
                    end
            end
            local min = 0
            for i = double and 2 or 0,#self.Name-1 do
                local id = (double and i-1 or i) - min
                if double and i == 2 then offset = offset + TLM.DoubleOffset end
                if self.Name[i+1] == " " then continue end
                if self.Name[i+1] == "/" then min = min + 1; continue end
                --if not IsValid(self.Models[2][i]) then
                self:SpawnLetter(i,TLM.SignLetter.model,offset - Vector(0,0,id*TLM.SignLetter.z),(Metrostroi.LiterWarper[self.Name[i+1]] or self.Name[i+1]))
                --end
            end
            if self.Name and self.Name:match("(/+)$") then
                local i = #self.Name
                local id = (double and i-1 or i) - min
                self:SpawnLetter(i,TLM.SignLetter.model,offset - Vector(0,0,id*TLM.SignLetter.z),Format("s%d",math.min(3,#self.Name:match("(/+)$"))))
            end
        else
            local k = "m1"

            if not IsValid(self.Models[1][k]) then
                local v = TLM["m1"]
                self.Models[1][k] = ClientsideModel(v,RENDERGROUP_OPAQUE)
                self.Models[1][k]:SetPos(self:LocalToWorld(self.BasePosition*(self.Left and Vector(-1,1,1) or 1)))
                self.Models[1][k]:SetAngles(self:LocalToWorldAngles(self.Left and Angle(-1,1,1) or Angle(1,1,1)))
                self.Models[1][k]:SetParent(self)
            end
        end
        self.Models.have = true
        self.ModelsCreated = true
    else
        --TODO
        if self.AutostopPresent then
            if IsValid(self.Models[1]["autostop"]) then
                self.Models[1]["autostop"]:SetPoseParameter("position",self:Animate("Autostop", self:GetNW2Bool("Autostop") and 1 or 0,     0,1, 0.4,false))
            end
        end


        self.Sig = self:GetNW2String("Signal","")
        self.Num = self:GetNW2String("Number",nil)
        if self.OldNum ~= self.Num then
            self.NextNumWork = CurTime + 1
        end
        self.OldNum = self.Num

        if (self.NextNumWork or CurTime) - CurTime >= 0 then
            self.Num = ""
        end
        if self.ARSOnly then return true end
        local offset = (self.RenderOffset[self.LightType] or Vector(0, 0, 0))
        if self.RouteNumberOffset then offset = offset + self.RouteNumberOffset*(self.Left and Vector(-1,1) or Vector(1,1)) end
        local ID = 0
        local ID2 = 0
        local first = true
        for _,v in ipairs(self.LensesTBL) do
            local data
            if not self.TrafficLightModels[self.LightType][v] then
                data = self.TrafficLightModels[self.LightType][#v-1]
            else
                data = self.TrafficLightModels[self.LightType][v]
            end
            if not data then continue end
            if first then
                first = false
            else
                offset = offset - Vector(0,0,data[1])
            end

            --self.NamesOffset = self.NamesOffset + Vector(0,0,data[1])
            if v ~= "M" then
                for i = 1,#v do
                    ID2 = ID2 + 1
                    local n = tonumber(self.Sig[ID2])
                    if n and self.Signals[ID2].RealState ~= (n > 0) then
                        self.Signals[ID2].RealState = n > 0
                        self.Signals[ID2].Stop = CurTime + 0.5
                    end
                    if self.Signals[ID2].Stop and CurTime-self.Signals[ID2].Stop > 0 then
                        self.Signals[ID2].Stop = nil
                    end
                    local State = self:Animate(ID.."/"..i,  ((n == 1 or (n == 2 and (RealTime() % 1.2 > 0.4))) and not self.Signals[ID2].Stop) and 1 or 0,  0,1, 128)
                    if not IsValid(self.Models[3][ID..ID2]) and State > 0 then self.Signals[ID2].State = nil end
                    local offsetAndLongOffset = offset + self.LongOffset
                    if not self.DoubleL then
                        self:SetLight(ID,ID2,self.BasePosition*(self.Left and Vector(-1,1,1) or 1) + offsetAndLongOffset + data[3][i-1]*(self.Left and Vector(-1,1,1) or 1),Angle(0, 0, 0),self.SignalConverter[v[i]]-1,State,self.Signals[ID2].State ~= State)
                    else
                        self:SetLight(ID,ID2,self.BasePosition + offsetAndLongOffset + data[3][i-1],Angle(0, 0, 0),self.SignalConverter[v[i]]-1,State,self.Signals[ID2].State ~= State)
                        self:SetLight(ID,ID2.."x",self.BasePosition*Vector(-1,1,1) + offsetAndLongOffset + data[3][i-1]*Vector(-1,1,1),Angle(0, 0, 0),self.SignalConverter[v[i]]-1,State,self.Signals[ID2].State ~= State)
                    end
                    self.Signals[ID2].State = State
                end
            else
                if Metrostroi.RoutePointer[self.Num[1]] and IsValid(self.Models[1][self.RouteNumber]) then self.Models[1][self.RouteNumber]:SetSkin(Metrostroi.RoutePointer[self.Num[1]]) end
            end

            ID = ID + 1
        end

        local LampIndicatorModels_numb_mdl = TLM.LampIndicator.model.."_numb.mdl"
        local LampIndicatorModels_lamp_mdl = TLM.LampIndicator.model.."_lamp.mdl"
        for k,v in pairs(self.RouteNumbers) do
            if k == "sep" then continue end
            local rou1k = "rou1"..k
            local State1 = self:Animate(rou1k,self.Num:find(v[1]) and 1 or 0,   0,1, 256)
            local State2
            --if v[3] then
            local rou2k = "rou2"..k
            if v[2] then State2 = self:Animate(rou2k,self.Num:find(v[2])and 1 or 0,     0,1, 256) end
            if not IsValid(self.Models[3][rou1k]) and State1 > 0 then
                self.Models[3][rou1k] = ClientsideModel(v[3] and LampIndicatorModels_numb_mdl or LampIndicatorModels_lamp_mdl,RENDERGROUP_OPAQUE)
                self.Models[3][rou1k]:SetPos(self:LocalToWorld(v.pos + self.OldRouteNumberSetup[4]))
                self.Models[3][rou1k]:SetAngles(self:GetAngles())
                self.Models[3][rou1k]:SetParent(self)
                self.Models[3][rou1k]:SetSkin(v[3] and self.OldRouteNumberSetup[5][v[1]] or self.OldRouteNumberSetup[6][v[1]] or tonumber(v[1])-1)
                self.Models[3][rou1k]:SetRenderMode(RENDERMODE_TRANSCOLOR)
                self.Models[3][rou1k]:SetColor(Color(255, 255, 255, 0))
            end
            if IsValid(self.Models[3][rou1k]) then
                if State1 > 0 then
                    self.Models[3][rou1k]:SetColor(Color(255,255,255,State1*255))
                elseif State1 == 0 then
                    self.Models[3][rou1k]:Remove()
                end
            end
            if not IsValid(self.Models[3][rou2k]) and v[3] and v[2] and State2 > 0 then
                self.Models[3][rou2k] = ClientsideModel(LampIndicatorModels_numb_mdl,RENDERGROUP_OPAQUE)
                self.Models[3][rou2k]:SetPos(self:LocalToWorld(v.pos + self.OldRouteNumberSetup[4] + TLM.RouteNumberOffset2))
                self.Models[3][rou2k]:SetAngles(self:GetAngles())
                self.Models[3][rou2k]:SetParent(self)
                self.Models[3][rou2k]:SetSkin(self.OldRouteNumberSetup[5][v[2]] or tonumber(v[2])-1)
                self.Models[3][rou2k]:SetRenderMode(RENDERMODE_TRANSCOLOR)
                self.Models[3][rou2k]:SetColor(Color(255, 255, 255, 0))
            end
            if IsValid(self.Models[3][rou2k]) then
                if State2 > 0 then
                    self.Models[3][rou2k]:SetColor(Color(255,255,255,State2*255))
                elseif State2 == 0 then
                    self.Models[3][rou2k]:Remove()
                end
            end
        end
        if self.Arrow then
            local State = self:Animate("roua",self.Num:find(self.SpecRouteNumbers[1]) and 1 or 0,   0,1, 256)
            if not IsValid(self.Models[3]["roua"]) and State > 0 then
                self.Models[3]["roua"] = ClientsideModel(LampIndicatorModels_lamp_mdl,RENDERGROUP_OPAQUE)
                self.SpecRouteNumbers.pos = (self.BasePosition+offset-TLM.SpecRouteNumberOffset)-(self.RouteNumberOffset or Vector(0, 0, 0))+TLM.RouteNumberOffset3
                if self.Left then self.SpecRouteNumbers.pos = self.SpecRouteNumbers.pos*TLM.SpecRouteNumberOffset2 end
                self.Models[3]["roua"]:SetPos(self.Models[1]["roua"]:LocalToWorld(TLM.RouaOffset))
                self.Models[3]["roua"]:SetAngles(self.Models[1]["roua"]:LocalToWorldAngles(Angle(180,0,0)))
                self.Models[3]["roua"]:SetParent(self)
                if self.Left then
                    if self.Num[1] == "L" then
                        self.Models[3]["roua"]:SetSkin(self.OldRouteNumberSetup[6]["R"] or 0)
                    else
                        self.Models[3]["roua"]:SetSkin(self.OldRouteNumberSetup[6]["L"] or 0)
                    end
                else
                    self.Models[3]["roua"]:SetSkin(self.OldRouteNumberSetup[6][self.Num[1]] or 0)
                end
                self.Models[3]["roua"]:SetRenderMode(RENDERMODE_TRANSCOLOR)
                self.Models[3]["roua"]:SetColor(Color(255, 255, 255, 0))
            end
            if IsValid(self.Models[3]["roua"]) then
                if State > 0 then
                    self.Models[3]["roua"]:SetColor(Color(255,255,255,State*255))
                elseif State == 0 then
                    self.Models[3]["roua"]:Remove()
                end
            end
        end
        --self.SpecRouteNumbers
    end
    
    return true
end

function ENT:Draw()
    -- Draw model
    self:DrawModel()
end
local debug = GetConVar("metrostroi_drawsignaldebug")

local ars = {
    {"275 Hz", "0 KM/H"},
    {"N/A Hz", "No frequency"},
    {"275-N/A", "Absolute stop"},
    nil,
    {"225 Hz", "40 KM/H"},
    nil,
    {"175 Hz", "60 KM/H"},
    {"125 Hz", "70 KM/H"},
    {"75  Hz", "80 KM/H"},
}


local cols = {
    R = Color(200,0,0),
    Y = Color(200,200,0),
    G = Color(0,200,0),
    W = Color(200,200,200),
    B = Color(0,0,200),
}
local function enableDebug()
    if debug:GetBool() then
        hook.Add("PreDrawEffects","MetrostroiSignalDebug",function()
            for _,sig in pairs(ents.FindByClass("gmod_track_signal")) do
                if IsValid(sig) and LocalPlayer():GetPos():Distance(sig:GetPos()) < 384 then
                    local pos = sig:LocalToWorld(Vector(48,0,150))
                    local ang = sig:LocalToWorldAngles(Angle(0,180,90))
                    cam.Start3D2D(pos, ang, 0.25)

                        if sig:GetNW2Bool("Debug",false) then
                            surface.SetDrawColor(sig.ARSOnly and 255 or 125, 125, 0, 255)
                            surface.DrawRect(0, -60, 364, 210)
                            if not sig.ARSOnly then
                                surface.DrawRect(0, 155, 240, 170)
                                surface.DrawRect(0, 330, 240, 190)
                                surface.SetDrawColor(0,0,0, 255)
                                surface.DrawRect(245, 155, 119, 365)
                            else
                                surface.DrawRect(0, 155, 364, 150)
                                surface.DrawRect(0, 310, 364, 190)
                            end

                            if sig.Name then
                                draw.DrawText(Format("Joint main info (%d)",sig:EntIndex()),"Trebuchet24",5,-60,Color(200,0,0,255))
                                draw.DrawText("Signal name: "..sig.Name,"Trebuchet24",          15, -40,Color(0, 0, 0, 255))
                                draw.DrawText("TrackID: "..sig:GetNW2Int("PosID",0),"Trebuchet24",  25, -20,Color(0, 0, 0, 255))
                                    draw.DrawText(Format("PosX: %.02f",sig:GetNW2Float("Pos",0)),"Trebuchet24", 135, -20,Color(0, 0, 0, 255))
                                draw.DrawText(Format("NextSignalName: %s",sig:GetNW2String("NextSignalName","N/A")),"Trebuchet24",  15, 0,Color(0, 0, 0, 255))
                                draw.DrawText(Format("TrackID: %s",sig:GetNW2Int("NextPosID",0)),"Trebuchet24", 25, 20,Color(0, 0, 0, 255))
                                    draw.DrawText(Format("PosX: %.02f",sig:GetNW2Float("NextPos",0)),"Trebuchet24", 135, 20,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Dist: %.02f",sig:GetNW2Float("DistanceToNext",0)),"Trebuchet24",  15, 40,Color(0, 0, 0, 255))
                                draw.DrawText(Format("PrevSignalName: %s",sig:GetNW2String("PrevSignalName","N/A")),"Trebuchet24",  15, 60,Color(0, 0, 0, 255))
                                draw.DrawText(Format("TrackID: %s",sig:GetNW2Int("PrevPosID",0)),"Trebuchet24", 25, 80,Color(0, 0, 0, 255))
                                    draw.DrawText(Format("PosX: %.02f",sig:GetNW2Float("PrevPos",0)),"Trebuchet24", 135, 80,Color(0, 0, 0, 255))
                                draw.DrawText(Format("DistPrev: %.02f",sig:GetNW2Float("DistanceToPrev",0)),"Trebuchet24",  15, 100,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Current route: %d",sig:GetNW2Int("CurrentRoute",-1)),"Trebuchet24",   15, 120,Color(0, 0, 0, 255))

                                draw.DrawText("AB info","Trebuchet24",5,160,Color(200,0,0,255))
                                draw.DrawText(Format("Occupied: %s",sig:GetNW2Bool("Occupied",false) and "Y" or "N"),"Trebuchet24",5,180,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Linked to controller: %s",sig:GetNW2Bool("LinkedToController",false) and "Y" or "N"),"Trebuchet24",5,200,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Num: %d",sig:GetNW2Int("ControllersNumber",0)),"Trebuchet24",10,220,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Controller logic: %s",sig:GetNW2Bool("BlockedByController",false) and "Y" or "N"),"Trebuchet24",5,240,Color(0, 0, 0, 255))
                                draw.DrawText(Format("Autostop: %s",not sig.ARSOnly and sig.AutostopPresent and (sig:GetNW2Bool("Autostop") and "Up" or "Down") or "No present"),"Trebuchet24",5,260,Color(0, 0, 0, 255))
                                draw.DrawText(Format("2/6: %s",sig:GetNW2Bool("2/6",false) and "Y" or "N"),"Trebuchet24",5,280,Color(0, 0, 0, 255))
                                draw.DrawText(Format("FreeBS: %d",sig:GetNW2Int("FreeBS")),"Trebuchet24",5,300,Color(0, 0, 0, 255))

                                draw.DrawText("ARS info","Trebuchet24",5,335,Color(200,0,0,255))
                                local num = 0
                                for i,tbl in pairs(ars) do
                                    if not tbl then continue end
                                    if sig:GetNW2Bool("CurrentARS"..(i-1),false) then
                                        draw.DrawText(Format("(% s)",tbl[1]),"Trebuchet24",5,355+num*20,Color(0,100,0,255))
                                        draw.DrawText(Format("%s",tbl[2]),"Trebuchet24",105,355+num*20,Color(0,100,0,255))
                                    else
                                        draw.DrawText(Format("(% s)",tbl[1]),"Trebuchet24",5,355+num*20,Color(0, 0, 0, 255))
                                        draw.DrawText(Format("%s",tbl[2]),"Trebuchet24",105,355+num*20,Color(0, 0, 0, 255))
                                    end
                                    num = num+1
                                end
                                if sig:GetNW2Bool("CurrentARS325",false) or sig:GetNW2Bool("CurrentARS325_2",false) then
                                    draw.DrawText("(325 Hz)","Trebuchet24",5,355+num*20,Color(0,100,0,255))
                                    draw.DrawText(Format("LN:%s Apr0:%s",sig:GetNW2Bool("CurrentARS325",false) and "Y" or "N",sig:GetNW2Bool("CurrentARS325_2",false) and "Y" or "N"),"Trebuchet24",105,355+num*20,Color(0,100,0,255))
                                else
                                    draw.DrawText("(325 Hz)","Trebuchet24",5,355+num*20,Color(0, 0, 0, 255))
                                    draw.DrawText(Format("LN:%s Apr0:%s",sig:GetNW2Bool("CurrentARS325",false) and "Y" or "N",sig:GetNW2Bool("CurrentARS325_2",false) and "Y" or "N"),"Trebuchet24",105,355+num*20,Color(0, 0, 0, 255))
                                end

                                if not sig.ARSOnly then
                                    draw.DrawText("Signal info","Trebuchet24",250,160,Color(200,0,0,255))
                                    local ID = 0
                                    local ID2 = 0
                                    local first = true
                                    for _,v in ipairs(sig.LensesTBL) do
                                        local data
                                        if not sig.TrafficLightModels[sig.LightType][v] then
                                            data = sig.TrafficLightModels[sig.LightType][#v-1]
                                        else
                                            data = sig.TrafficLightModels[sig.LightType][v]
                                        end
                                        if not data then continue end

                                        --sig.NamesOffset = sig.NamesOffset + Vector(0,0,data[1])
                                        if v ~= "M" then
                                            for i = 1,#v do
                                                ID2 = ID2 + 1
                                                local n = tonumber(sig.Sig[ID2])
                                                local State = n == 1 and "X" or (n == 2 and (RealTime() % 1.2 > 0.4)) and "B" or false
                                                draw.DrawText(Format(v[i],sig:EntIndex()),"Trebuchet24",250,160 + ID*20 + ID2*20,cols[v[i]])
                                                if State then
                                                    draw.DrawText(State,"Trebuchet24",280,160 + ID*20 + ID2*20,cols[v[i]])
                                                end
                                            end
                                        else
                                            ID2 = ID2 + 1
                                            draw.DrawText("M","Trebuchet24",250,160 + ID*20 + ID2*20,Color(200,200,200))
                                            draw.DrawText(sig.Num or "none","Trebuchet24",280,160 + ID*20 + ID2*20,Color(200,200,200))

                                            --if Metrostroi.RoutePointer[sig.Num[1]] then sig.Models[1][sig.RouteNumber]:SetSkin(Metrostroi.RoutePointer[sig.Num[1]]) end
                                        end

                                        ID = ID + 1
                                    end
                                end
                            else
                                draw.DrawText("No data...","Trebuchet24",5,0,Color(0, 0, 0, 255))
                            end
                        else
                            surface.SetDrawColor(sig.ARSOnly and 255 or 125, 125, 0, 255)
                            surface.DrawRect(0, 0, 364, 25)
                            draw.DrawText("Debug disabled...","Trebuchet24",5,0,Color(0, 0, 0, 255))
                        end
                    cam.End3D2D()
                end
            end
        end)
    else
        hook.Remove("PreDrawEffects","MetrostroiSignalDebug")
    end
end
hook.Remove("PreDrawEffects","MetrostroiSignalDebug")
cvars.AddChangeCallback( "metrostroi_drawsignaldebug", enableDebug)
enableDebug()

Metrostroi.OptimisationPatch()