// COPYRIGHT 2020 by the Open Rails project.
// 
// This file is part of Open Rails.
// 
// Open Rails is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Open Rails is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Open Rails.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orts.Common;
using ORTS.Common;
// using Orts.Formats.Msts;
using ORTS.Scripting.Api;
using ORTS.Scripting.Api.ETCS;
using Orts.Simulation.Physics;
using Orts.Simulation.Signalling;
using Orts.Simulation.RollingStocks;
using Orts.Simulation.RollingStocks.SubSystems;


namespace ORTS.Scripting.Script
{
    public class TCS_SCMTETCS_IT_ETR400 : TrainControlSystem
    {
        enum TCS
        {
            SCMT,        // I SCMT
            ETCSL2
        }

        enum SSBSTATE
        {
            Off,
            ReadyToOn,
            On
        }


        enum MonitorState
        {
            Disabled,
            StandBy,
            Alarm,
            Emergency,
            ReadyToReset
        }

        enum TCSCommandEvent
        {
            None,
            AccendiSpegniSSB,
            Free1,
            Conferma_Infobox,
            Pulsante_SCMT,
            Pulsante_Man,
            Pulsante_RSC,
            Soppr,
            Visualizz_RF,
            Splash,
            Vigilante,
            Visualizz_Pulsante_RSC,
            Cancella_Pulsante_Manovra,
            Ripetizioni_estese,
            Bottone_Start,
            Visualizz_Timestamp,
            Luce_blu_tacho,
            Luce_rossa_tacho,
            SR,
            Attivazione_SR,
            Ripetizioni_estese_spente_lampeggio,
            Ripetizioni_estese_accese_lampeggio,
            Pulsante_Pre,
            // Le prossime 7 righe devono rimanere consecutive
            Pulsante_Ric270S,
            Pulsante_Ric270,
            Pulsante_Ric180S,
            Pulsante_Ric180,
            Pulsante_Ric120S,
            Pulsante_Ric120,
            Pulsante_Ric75,
            SopprConfirmation,
            SRDialog,
            TCSID,
            RadioLink,
            RequestSwitchToETCS,
            MessaggiInfobox,
            FullSupervision,
            TargetDistance,
            MascheraBarraFrenata,
            BarraDistanzaFrenata,
            CommutatoreTensione,
            CampoB3,
            CampoB4,
            Visualizz_CommutatoreTensione,
            TensioneAiPanto,
            Visualizz_ReleaseSpeed,
            MascheraReleaseSpeed,
			Approccio10,
            //
        }

        enum MESSAGGIINFOBOX
        {
            Void,
            Liv2Annunciato,
            ConfermareTransizioneLiv2,
            LivSTMAnnunciato,
            ConfermareLivSTMNazionale,
            ConfermareTransizioneLivSTM,
            NuovaTensione25kV,
            NuovaTensione3kV,
            Void1,
            ConfermareLiv2,
        }

        enum TCSTRANSITIONSTATE
        {
            SCMT,
            ToETCSRadiolink,
            ToETCSRequestSwitch,
            ToETCSSegnaleConfinePassed,
            ToETCSTransitionTimeoutBeforeSegnaleConfine,
            ToETCSTransitionTimeoutAfterSegnaleConfine,
            ToETCSAckSwitchWaitSegnaleConfine,
            ETCS,
            ToSCMTAnnunciato,
            ToSCMTRequestSwitch,
            ToSCMTSegnaleConfinePassed,
            ToSCMTTransitionTimeoutBeforeSegnaleConfine,
            ToSCMTTransitionTimeoutAfterSegnaleConfine,
            ToSCMTAckSwitchWaitSegnaleConfine,
        }

        enum RSCSTATE
        {
            Inactive,
            WaitingAck,
            Active,
            AckMissed,
            WaitingShutdown,
            ActivationRequested,
        }

        enum PRERICONOSCIMENTOSTATE
        {
            Invalid,
            Inactive,
            WaitingRic,
            WaitingRicEnd,
            WaitingPre,
            Timer12secOn,
            PreActive,
            Active,
            AckMissed,
            Timer12secExpired
        }

        enum RSCCODE
        {
            C270SS,
            C270S,
            C270,
            C180S,
            C180,
            C120S,
            C120,
            C75,
            SR,
            AC,
            None
        }

        enum SCMTSTATE
        {
            Off,
            Autotest,
            Manovra,
            Start,
            ConfermaStart,
            Predisposizione,
            Active,
            ActiveAndRSC
        }

        enum ATTIVAZIONESR
        {
            Inactive,
            WaitingConfirmation,
            InternalChecks,
            Active,
            SignalPassed,
        }

        float[] SRTimerDelayS = new float[] { 0.0f, 10.0f, 5.0f, 12.0f, 12f };

        enum ETCSL2STATE
        {
            Off,
            Autotest,
            Void1,
            Start,
            ConfermaStart,
            AttesaFullSupervision,
            Void2,
            FullSupervision
        }

        enum POCSTATE
        {
            Zone3kV,
            POCTo25Announced,
            POCTo25PantosDown,
            POCTo25Start,
            POCTo25End,
            POCTo25WaitPowerSwitchMoved,
            POCTo25WaitPantosUp,
            POCTo25WaitCircuitBreakerClosed,
            Zone25kV,
            POCTo3Announced,
            POCTo3PantosDown,
            POCTo3Start,
            POCTo3End,
            POCTo3WaitPowerSwitchMoved,
            POCTo3WaitPantosUp,
            POCTo3WaitCircuitBreakerClosed,
        }

        enum PCFSTATE
        {
            OutOfPCF,
            PCFAnnounced,
            PCFStaccoCarichi,
            PCFCircuitBreakerOpen,
            PCFStart,
            PCFEnd,
            PCFWaitCircuitBreakerClosed,
            PCFCircuitBreakerClosed,
            PCFWaitAttaccoCarichi,
        }

        enum POWERSUPPLYSTATE
        {
            Pantos3Up,
            Pantos3Down,
            CircuitBreakerOpen,
            Pantos25Rising,
            Pantos25Up,
            CircuitBreakerClosed,
            Pantos25Down,
        }

        enum POWERSUPPLYTYPE
        {
            _3kV,
            _25kV,
            _1_5kV,
            _15kV,
        }

        enum AUTOTESTPHASE
        {
            InitialWait,
            FirstBraking,
            WaitFirstRelease,
            SecondBraking,
            WaitSecondRelease,
            ThirdBraking,
            WaitThirdRelease,
            FourthBraking,
            WaitFourthRelease,
            FinalWait,
        }

        float[] SCMTAutotestTimerDelayS = new float[] { 5.0f, 1.0f, 3.0f, 1.0f, 3.0f, 1.0f, 3.0f, 1.0f, 3.0f, 5.0f };



        enum SIGNALFILESTYPE
        {
            SerriniBaldi,
            PriviteraBono
        }

        // Train parameters
        bool SCMTMonitor;                                 // SCMT
        bool RSCMonitor;
        TCS ActiveTCS = TCS.SCMT;

        string[] NoRSCStations = new string[] { "Milano Centrale", "Roma Termini", "Roma TERMINI", "Firenze S. Maria Novella", "Firenze SMN", "Torino Porta Nuova",
        "Genova P.Principe" };

        string[] ETCSL2Signals = new string[] { "etcs" };

        // Corrispondenze fra Dati segnali e codici RSC e Aspect per visualizzazione
        // per linee con VMax >= 250 NextSignalAspect(0), NextSignalAspect(1), NextSignalAspect(2), NextSignalAspect(3) == CLEAR_2 -> Cod. 270**
        // per linee con VMax >= 180 NextSignalAspect(0), NextSignalAspect(1), NextSignalAspect(2) == CLEAR_2 -> Cod. 270*
        // per le altre linee NextSignalAspect(0), NextSignalAspect(1) == CLEAR_2 -> Cod. 270
        // NextSignalAspect(0) == CLEAR_2 e senza vmax o == Restricting e vmax = 120 -> Cod. 180
        // NextSignalAspect(0) == CLEAR_2 e vmax 100, NextSignalAspect(0) == APPROACH_3 e vmax 60, NextSignalAspect(0) == APPROACH_2 e vmax 60 o vmax30,
        // NextSignalAspect(0) == APPROACH_1 e vmax30 --> Cod. 120
        // NextSignalAspect(0) == STOP --> Cod. 75
        // Rimangono 2 codici disponibili
        // Corrispondenze fra codici RSC e Aspect per visualizzazione
        // Cod. 270** Clear_2
        // Cod. 270* Clear_1
        // Cod. 270 Approach_3
        // Cod. 180 Approach_2
        // Cod. 120 Approach_1 --> genera PRERIC
        // Cod. 75 Stop

        bool AutodetectSignalFilesType = true;
        SIGNALFILESTYPE SignalFilesType = SIGNALFILESTYPE.SerriniBaldi;
        bool HeavyFreightTrain;                             // MA train only
        float SafeDecelerationMpS2;                         // Gamma
        SSBSTATE SSBState = SSBSTATE.On;

        // SCMT (Sistema Controllo Marcia Treno)
        // Parameters
        bool SCMTAppliesCutsPower = true;
        float SCMTTrainSpeedLimitKpH;                        // VT
        float SCMTTrainLengthM;                              // L
        const float SCMTDelayBeforeEmergencyBrakingS = 5f;   // Tx
        float SCMTDelayBeforeBrakingEstablishedS;            // Tbo
        float SCMTOverspeedDownThresholdKpH = 5f;
        float SCMTAutopilotOverspeedDownThresholdKpH = 3f;
        float SCMTActualOverspeedDownThreshold = 5f;
        bool Autopilot = false;
        float AutopilotTimerDelayS = 1.5f;
        float AutopilotButtonPressTimerDelayS = 0.7f;
        bool CircuitBreakerOpened = false;
        bool CircuitBreakerOpen = false;
        float ReleaseSpeed = 30f;

        // Variables
        MonitoringStatus Status = MonitoringStatus.Normal;
        bool SCMTEmergencyBraking = false;
        float SCMTWarningTimerDelayS = 3f;
        Timer SCMTWarningTimer;
        bool ExternalEmergencyBraking = false;
        bool normalSignalPassed = false;
        bool prericSignalPassed = false;
        bool distanceSignalPassed = false;
        bool infoSignalPassed = false;
        float normalSignalDistanceM;
        float PreviousSignalDistanceM = 0f;
        float distanceSignalDistanceM;
        float PreviousDistanceSignalDistanceM = 0f;
        float infoSignalDistanceM;
        float PreviousInfoSignalDistanceM = 0;
        string infoSignalMainHeadSignalType = "";
        string PreviousInfoSignalMainHeadSignalType = "";
        Aspect SCMTNextSignalAspect = Aspect.Stop;
        int UpdateCount;
        float nextSignalSpeedLimitKpH;
        Aspect nextSignalAspect = Aspect.None;
        Aspect previousSignalAspect = Aspect.None;
        float currentPostSpeedLimitKpH;
        bool ColdStart = true;
        bool[] TCSButtonPressed = new bool[48];
        bool[] TCSButtonReleased = new bool[48];
        SCMTSTATE SCMTState = SCMTSTATE.Active;
        bool SCMTSpad = false;
        TRAIN_CONTROL ControlMode;
        float SpeedKpH;
        ATTIVAZIONESR AttivazioneSR = ATTIVAZIONESR.Inactive;
        Timer SRTimer;
        Timer AutopilotTimer;
        Timer AutopilotButtonPressTimer;
        Timer SCMTAutotestTimer;
        Timer StartupTimer; // disables emergency braking at game start when simulation is still in setup phase
        float CurrentMaxSpeedKpH;
        bool nextNormalSignalAspectWasAtStop = false;
        float SCMTVelocitaRilascio = 30f;		
		
        // Items for transition from one TCS to another
        AUTOTESTPHASE AutotestPhase = AUTOTESTPHASE.InitialWait;
        TCSTRANSITIONSTATE TCSTransitionState = TCSTRANSITIONSTATE.SCMT;
        Timer TCSTransitionConfirmationTimer;

        SignalFeatures genericSignalFeatures;

        struct InternalSignalFeatures
        {
            public string MainHeadSignalTypeName;
            public Aspect Aspect;
            public float DistanceM;
            public float SpeedLimitMpS;
            public float AltitudeM;
        }

        float SCMTCurrentSignalSpeedLimitKpH;
        float SCMTNextSignalSpeedLimitKpH;
        float SCMTSignalTargetSpeedKpH;
        float SCMTSignalTargetDistanceM;
        float SCMTDeclivity = 0f;                            // i

        float SCMTCurrentSpeedPostSpeedLimitKpH;
        float SCMTNextSpeedPostSpeedLimitKpH;
        float SCMTSpeedPostTargetSpeedKpH;
        float SCMTSpeedPostTargetDistanceM;

        float SCMTCurrentAlertSpeedKpH;
        float SCMTCurrentEBSpeedKpH;
        float SCMTNextAlertSpeedKpH;
        float SCMTNextEBSpeedKpH;

        float SCMTSignalEmergencySpeedCurveKpH;
        float SCMTSignalAlertSpeedCurveKpH;
        float SCMTSpeedPostEmergencySpeedCurveKpH;
        float SCMTSpeedPostAlertSpeedCurveKpH;

        bool EmergencyBrakingRequest;
        bool FullBrakingRequest;
        bool PowerCutRequest;
        bool DynamicBrakingRequest;

        float TrainSpeedLimitKpH = 320.0f;

        // RSC
        // Params
        float RSCMaxSignalSpeedCode180KpH = 115f;
        float RSCMaxSignalSpeedCode180StarKpH = 150f;
        float RSCMaxSignalSpeedCode270KpH = 180f;
        float RSCMaxSignalSpeedCode270StarKpH = 230f;
        float RSCMaxSignalSpeedCode270DoubleStarKpH = 250f;
        float RSCMinimumLengthWithoutSwitchesM = 1000f;
        float RSCNextDivergingSwitchMaxDistanceM = 300f;
        float RSCPrericonoscimentoTimerDelayS = 12f;
        float RSCCodeConfirmationDelayS = 4f;
        float TCSTransitionConfirmationDelayS = 12f;
        MESSAGGIINFOBOX messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;

        // Variables
        float RSCNextSignalAllowedSpeedKpH;
        RSCCODE RSCCode = RSCCODE.None;
        RSCCODE RSCPreviousCode = RSCCODE.None;
        RSCCODE RSCTempNewCode = RSCCODE.None;
        RSCSTATE RSCState = RSCSTATE.Inactive;
        bool RSCFirstRun = true;
        PRERICONOSCIMENTOSTATE RSCPrericonoscimentoState;
        Timer RSCPrericonoscimentoTimer;
        Timer RSCPrericonoscimento1Timer;
        Timer RSCCodeConfirmationTimer;
        Timer RSCCodeConfirmation1Timer;
        Timer RSCCheckACFinitaTimer;
        float nextDivergingSwitchDistanceM;
        float RSCMinimumLengthM = 1000f;
        bool RSCFirstCodeReceived = true;
        bool NoRSCZone = false;
        bool RSCPresent = false;
        float RSCMaxTrailingDivergingSwitchDistanceM = 1000f;
        float RemainingLengthM = 0f;
        float OldRemainingLengthM = -1f;
        bool RSCPIPassed = false;
        float IntervalDistanceM = 0f;
        bool RSCForceCod = false;
        bool RSCForceCod9 = false;
        bool RSCForceNoCod = false;
        bool RSCEndsThisSection = false;
        bool RSCEndsNextSection = false;
        bool RSCEndsNextNextSection = false;
        bool RSCEndsNextNextNextSection = false;
        int[] incIndex = new int[3];
        int RequiredNormalSignals = 3;

        // ETCS
        float ETCSDelayForEmergencyCurve = 2f;
        float ETCSDelayForWarningCurve = 4f;
        float ETCSDelayForIndicationCurve = 13f;
        float ETCSSignalIndicationSpeedCurveKpH;
        float ETCSSpeedPostIndicationSpeedCurveKpH;
        float ETCSSignalIndicationTargetSpeedKpH;
        ETCSL2STATE ETCSL2State = 0;
        bool RadioLinkState = false;
        Timer SpeedHookTimer;
        Timer StaccoAttaccoCarichiTimer;
        Timer AttesaFullSupervisionTimer;
        float SpeedHookTimerDelayS = 6f;
        float StaccoAttaccoCarichiTimerDelayS = 1.5f;
        float AttesaFullSupervisionTimerDelayS = 10f;
        float BrakingDistanceBar = 0f;
        POCSTATE POCState = POCSTATE.Zone3kV;
        PCFSTATE PCFState = PCFSTATE.OutOfPCF;
        POWERSUPPLYTYPE PowerSupplySwitchState = POWERSUPPLYTYPE._3kV;
        float TargetDistanceToRisePantosM = 0;
        float TensioneAiPantokV = 0;
        bool NeutralZone = false;
        float ETCSOverspeedDownThresholdKpH = 0;
        float ETCSSpeedLimitToleranceKpH = 1.0f;
        // table from power supply type to panto ID
        int[,] PantoIndex = new int[2, 2] {{ 1, 2 } , { 3, 4 }};

        //    POWERSUPPLYTYPE[] PantoPowerSupply = new PantoPowerSupply { POWERSUPPLYTYPE._3kV, POWERSUPPLYTYPE._3kV, POWERSUPPLYTYPE._25kV, POWERSUPPLYTYPE._25kV }; 

        SignalFeatures[] NormalSignals = new SignalFeatures[7];
        SignalFeatures[] VirtualSignals = new SignalFeatures[7];
        SignalFeatures[] InfoSignals = new SignalFeatures[10];
        TunnelInfo[] TunnelData = new TunnelInfo[10];
        InternalSignalFeatures[] TrueNormalSignals = new InternalSignalFeatures[7];


        // MSTS TCS emulation

        float EmergencyStopMonitorTimeLimitS = 66f;
        float EmergencyStopAlarmTimeLimitS = 60f;
        float EmergencyStopPenaltyTimeLimitS = 0f;
        float EmergencyStopCriticalLevelMpS = 0f;
        float EmergencyStopResetLevelMpS = 0f;
        bool EmergencyStopAppliesFullBrake = true;
        bool EmergencyStopAppliesEmergencyBrake;
        bool EmergencyStopAppliesCutsPower;
        bool EmergencyStopAppliesShutsDownEngine;

        float VigilanceMonitorTimeLimitS = 66f;
        float VigilanceAlarmTimeLimitS = 60f;
        float VigilancePenaltyTimeLimitS = 0f;
        float VigilanceCriticalLevelMpS = 0f;
        float VigilanceResetLevelMpS = 0f;
        bool VigilanceAppliesFullBrake = true;
        bool VigilanceAppliesEmergencyBrake;
        bool VigilanceAppliesCutsPower;
        bool VigilanceAppliesShutsDownEngine;
        bool VigilanceResetOnZeroSpeed = true;
        bool VigilanceResetOnDirectionNeutral;
        bool VigilanceResetOnResetButton;
        bool VigilanceRestartOnControlsMoved;

        Timer VigilanceEmergencyTimer;
        Timer VigilancePenaltyTimer;

        MonitorState VigilanceMonitorState;
        MonitorState SCMTOverspeedMonitorState;

        float VigilanceAlarmTimeoutS;


        bool VigilanceMonitor = false;
        bool SCMTOverspeedMonitor = false;
        bool EmergencyStopMonitor = false;

        public TCS_SCMTETCS_IT_ETR400() { }

        public bool ResetButtonPressed { get; private set; }

        public bool VigilanceSystemEnabled
        {
            get
            {
                bool enabled = true;

                enabled &= IsAlerterEnabled();

                if (VigilanceMonitor)
                {
                    if (VigilanceResetOnDirectionNeutral)
                    {
                        enabled &= CurrentDirection() != Direction.N;
                    }
                }

                return enabled;
            }
        }

        public bool VigilanceReset
        {
            get
            {
                bool vigilanceReset = true;

                if (VigilanceMonitor)
                {
                    if (VigilanceResetOnDirectionNeutral)
                    {
                        vigilanceReset &= CurrentDirection() == Direction.N;
                    }

                    if (VigilanceResetOnZeroSpeed)
                    {
                        vigilanceReset &= SpeedMpS() < 0.1f;
                    }

                    if (VigilanceResetOnResetButton)
                    {
                        vigilanceReset &= ResetButtonPressed;
                    }
                }

                return vigilanceReset;
            }
        }

        public bool SpeedControlSystemEnabled
        {
            get
            {
                bool enabled = true;

                enabled &= IsSpeedControlEnabled();

                return enabled;
            }
        }

        public bool SCMTOverspeed
        {
            get
            {
                bool overspeed = false;

                if (SCMTOverspeedMonitor)
                {
                    overspeed |= SpeedKpH > SCMTSignalEmergencySpeedCurveKpH;
                    overspeed |= SpeedKpH > SCMTSpeedPostEmergencySpeedCurveKpH;


                }
                return overspeed;
            }
        }

        public bool SCMTOverspeedWarning
        {
            get
            {
                bool overspeedWarning = false;

                if (SCMTOverspeedMonitor)
                {
                    overspeedWarning |= SpeedKpH > SCMTSignalAlertSpeedCurveKpH;
                    overspeedWarning |= SpeedKpH > SCMTSpeedPostAlertSpeedCurveKpH;
                }
                return overspeedWarning;
            }
        }

        public bool SCMTOverspeedReadyToReset
        {
            get
            {
                bool overspeedReset = true;

                if (SCMTOverspeedMonitor)
                {
                    overspeedReset &= SpeedKpH < SCMTSignalAlertSpeedCurveKpH - SCMTActualOverspeedDownThreshold || SpeedKpH < 0.36f;
                    overspeedReset &= SpeedKpH < SCMTSpeedPostAlertSpeedCurveKpH - SCMTActualOverspeedDownThreshold || SpeedKpH < 0.36f;
                }
                return overspeedReset;
            }
        }

        public bool SCMTOverspeedWarningReset
        {
            get
            {
                bool overspeedWarningReset = true;

                if (SCMTOverspeedMonitor)
                {
                    overspeedWarningReset &= SpeedKpH < SCMTSignalAlertSpeedCurveKpH - SCMTActualOverspeedDownThreshold || SpeedKpH < 0.36f;
                    overspeedWarningReset &= SpeedKpH < SCMTSpeedPostAlertSpeedCurveKpH - SCMTActualOverspeedDownThreshold || SpeedKpH < 0.36f;
                    overspeedWarningReset |= SCMTOverspeed;
                }
                return overspeedWarningReset;
            }
        }

        public override void Initialize()
        {
            // General section
            SCMTMonitor = GetBoolParameter("General", "SCMTMonitor", false);
            VigilanceMonitor = GetBoolParameter("General", "VigilanceMonitor", false);
            EmergencyStopMonitor = GetBoolParameter("General", "EmergencyStopMonitor", false);
            SCMTOverspeedMonitor = GetBoolParameter("General", "SCMTOverspeedMonitor", false);
            RSCMonitor = GetBoolParameter("General", "RSCMonitor", false);
            AutodetectSignalFilesType = GetBoolParameter("General", "AutodetectSignalFilesType", true);
            SignalFilesType = (SIGNALFILESTYPE)GetIntParameter("General", "SignalFilesType", 0);
            HeavyFreightTrain = GetBoolParameter("General", "HeavyFreightTrain", false);
            SafeDecelerationMpS2 = GetFloatParameter("General", "SafeDecelerationMpS2", 0.7f);

            // SCMT section
            SCMTWarningTimerDelayS = GetFloatParameter("SCMT", "WarningTimerDelayS", 3f);
            SCMTAppliesCutsPower = GetBoolParameter("SCMT", "AppliesCutsPower", SCMTAppliesCutsPower);
            SCMTDeclivity = GetFloatParameter("SCMT", "DeclivityPerMille", 0f) / 1000f;

            // RSC section
            RSCMaxSignalSpeedCode180KpH = GetFloatParameter("RSC", "MaxSignalSpeedCode180KpH", RSCMaxSignalSpeedCode180KpH);
            RSCMinimumLengthWithoutSwitchesM = GetFloatParameter("RSC", "MinimumLengthWithoutSwitchesM", RSCMinimumLengthWithoutSwitchesM);
            RSCMinimumLengthWithoutSwitchesM = GetFloatParameter("RSC", "MinimumLengthWithoutSwitchesM", RSCMinimumLengthWithoutSwitchesM);
            RSCCodeConfirmationDelayS = GetFloatParameter("RSC", "CodeConfirmationDelayS", RSCCodeConfirmationDelayS);

            // MSTS TCS Emulation of .eng parameters

            // Vigilance monitor
            EmergencyStopMonitorTimeLimitS = GetFloatParameter("EmergencyStopMonitor", "MonitorTimeLimitS", EmergencyStopMonitorTimeLimitS);
            EmergencyStopAlarmTimeLimitS = GetFloatParameter("EmergencyStopMonitor", "AlarmTimeLimitS", EmergencyStopAlarmTimeLimitS);
            EmergencyStopPenaltyTimeLimitS = GetFloatParameter("EmergencyStopMonitor", "PenaltyTimeLimitS", EmergencyStopPenaltyTimeLimitS);
            EmergencyStopCriticalLevelMpS = GetFloatParameter("EmergencyStopMonitor", "CriticalLevelMpS", EmergencyStopCriticalLevelMpS);
            EmergencyStopResetLevelMpS = GetFloatParameter("EmergencyStopMonitor", "ResetLevelMpS", EmergencyStopResetLevelMpS);
            EmergencyStopAppliesFullBrake = GetBoolParameter("EmergencyStopMonitor", "AppliesFullBrake", EmergencyStopAppliesFullBrake);
            EmergencyStopAppliesEmergencyBrake = GetBoolParameter("EmergencyStopMonitor", "AppliesEmergencyBrake", EmergencyStopAppliesEmergencyBrake);
            EmergencyStopAppliesCutsPower = GetBoolParameter("EmergencyStopMonitor", "AppliesCutsPower", EmergencyStopAppliesCutsPower);
            EmergencyStopAppliesShutsDownEngine = GetBoolParameter("EmergencyStopMonitor", "AppliesShutsDownEngine", EmergencyStopAppliesShutsDownEngine);

            VigilanceMonitorTimeLimitS = GetFloatParameter("VigilanceMonitor", "MonitorTimeLimitS", VigilanceMonitorTimeLimitS);
            VigilanceAlarmTimeLimitS = GetFloatParameter("VigilanceMonitor", "AlarmTimeLimitS", VigilanceAlarmTimeLimitS);
            VigilancePenaltyTimeLimitS = GetFloatParameter("VigilanceMonitor", "PenaltyTimeLimitS", VigilancePenaltyTimeLimitS);
            VigilanceCriticalLevelMpS = GetFloatParameter("VigilanceMonitor", "CriticalLevelMpS", VigilanceCriticalLevelMpS);
            VigilanceResetLevelMpS = GetFloatParameter("VigilanceMonitor", "ResetLevelMpS", VigilanceResetLevelMpS);
            VigilanceAppliesFullBrake = GetBoolParameter("VigilanceMonitor", "AppliesFullBrake", VigilanceAppliesFullBrake);
            VigilanceAppliesEmergencyBrake = GetBoolParameter("VigilanceMonitor", "AppliesEmergencyBrake", VigilanceAppliesEmergencyBrake);
            VigilanceAppliesCutsPower = GetBoolParameter("VigilanceMonitor", "AppliesCutsPower", VigilanceAppliesCutsPower);
            VigilanceAppliesShutsDownEngine = GetBoolParameter("VigilanceMonitor", "AppliesShutsDownEngine", VigilanceAppliesShutsDownEngine);
            VigilanceResetOnDirectionNeutral = GetBoolParameter("VigilanceMonitor", "ResetOnDirectionNeutral", VigilanceResetOnDirectionNeutral);
            VigilanceResetOnZeroSpeed = GetBoolParameter("VigilanceMonitor", "ResetOnZeroSpeed", VigilanceResetOnZeroSpeed);
            VigilanceResetOnResetButton = GetBoolParameter("VigilanceMonitor", "ResetOnResetButton", VigilanceResetOnResetButton);
            VigilanceRestartOnControlsMoved = GetBoolParameter("VigilanceMonitor", "RestartOnControlsMoved", VigilanceRestartOnControlsMoved);


            // Initialize customized TCS command strings
            var i = 0;
            foreach (string TCSControlString in Enum.GetNames(typeof(TCSCommandEvent)))
            {
                if (TCSControlString == "None") continue;
 //               SetCustomizedTCSControlString(TCSControlString);
                SetCustomizedCabviewControlName(i, TCSControlString);
                i++;
            }

            // Variables initialization
            SRTimer = new Timer(this);
            SRTimer.Setup(SRTimerDelayS[0]);
            SCMTWarningTimer = new Timer(this);
            SCMTWarningTimer.Setup(SCMTWarningTimerDelayS);
            RSCPrericonoscimento1Timer = new Timer(this);
            RSCPrericonoscimento1Timer.Setup(RSCCodeConfirmationDelayS);
            RSCPrericonoscimentoTimer = new Timer(this);
            RSCPrericonoscimentoTimer.Setup(RSCPrericonoscimentoTimerDelayS);
            RSCCodeConfirmationTimer = new Timer(this);
            RSCCodeConfirmationTimer.Setup(RSCCodeConfirmationDelayS);
            RSCCodeConfirmation1Timer = new Timer(this);
            RSCCodeConfirmation1Timer.Setup(RSCCodeConfirmationDelayS);
            RSCCheckACFinitaTimer = new Timer(this);
            RSCCheckACFinitaTimer.Setup(RSCCodeConfirmationDelayS);
            AutopilotTimer = new Timer(this);
            AutopilotTimer.Setup(AutopilotTimerDelayS);
            AutopilotButtonPressTimer = new Timer(this);
            AutopilotButtonPressTimer.Setup(AutopilotButtonPressTimerDelayS);
            SCMTAutotestTimer = new Timer(this);
            SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[0]);
            TCSTransitionConfirmationTimer = new Timer(this);
            TCSTransitionConfirmationTimer.Setup(TCSTransitionConfirmationDelayS);
            SpeedHookTimer = new Timer(this);
            SpeedHookTimer.Setup(SpeedHookTimerDelayS);
            StaccoAttaccoCarichiTimer = new Timer(this);
            StaccoAttaccoCarichiTimer.Setup(StaccoAttaccoCarichiTimerDelayS);
            AttesaFullSupervisionTimer = new Timer(this);
            AttesaFullSupervisionTimer.Setup(AttesaFullSupervisionTimerDelayS);
            StartupTimer = new Timer(this);
            StartupTimer.Setup(3);


            // Variables initialization
            SCMTCurrentSignalSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
            SCMTNextSignalSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
            SCMTSignalTargetSpeedKpH = SCMTTrainSpeedLimitKpH;
            SCMTSignalTargetDistanceM = 0f;

            SCMTCurrentSpeedPostSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
            SCMTNextSpeedPostSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
            SCMTSpeedPostTargetSpeedKpH = SCMTTrainSpeedLimitKpH;
            SCMTSpeedPostTargetDistanceM = 0f;

            SCMTCurrentAlertSpeedKpH =3f;
            SCMTCurrentEBSpeedKpH = 5f;
            SCMTNextAlertSpeedKpH = 3f;
            SCMTNextEBSpeedKpH = 5f;

            SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTSTATE.Off + 1);

            //            ETCSStatus = new ETCSStatus();


            Activated = true;

            ETCSStatus.ShowTextMessageArea = false;
            InitializeMSTSTCSEmulation();
            StartupTimer.Start();
        }

        protected void InitializeMSTSTCSEmulation()
        {
            {
                VigilanceEmergencyTimer = new Timer(this);
                VigilancePenaltyTimer = new Timer(this);

                if (VigilanceMonitor)
                {
                    if (VigilanceMonitorTimeLimitS > VigilanceAlarmTimeLimitS)
                        VigilanceAlarmTimeoutS = VigilanceMonitorTimeLimitS - VigilanceAlarmTimeLimitS;
                    VigilanceEmergencyTimer.Setup(VigilanceAlarmTimeoutS);
                    VigilancePenaltyTimer.Setup(VigilancePenaltyTimeLimitS);
                }
            }
        }

        public override void Update()
        {
            SpeedKpH = MpS.ToKpH(SpeedMpS());
            TrainSpeedLimitKpH = MpS.ToKpH(TrainSpeedLimitMpS());
            // At startup not all variables are already available
            if (UpdateCount < 2)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTSTATE.Off + 1);
                UpdateCount++;
                return;
            }
            if (UpdateCount == 2)
            {
                FirstUpdateInitializations();
                UpdateCount++;
                return;
            }

            UpdateTCSTransitionState();
            SetCabDisplayControl((int)(TCSCommandEvent.TCSID) - 1, (int)ActiveTCS);
            SetCabDisplayControl((int)(TCSCommandEvent.FullSupervision) - 1, ETCSL2State == ETCSL2STATE.FullSupervision ? 1 : 0);
            SetCabDisplayControl((int)(TCSCommandEvent.RadioLink) - 1, RadioLinkState ? 1 : 0);
            Autopilot = IsAutopiloted();

            if (TCSButtonPressed[(int)(TCSCommandEvent.CommutatoreTensione)])
                if (PowerSupplySwitchState == POWERSUPPLYTYPE._3kV)
                    PowerSupplySwitchState = POWERSUPPLYTYPE._25kV;
                else if (PowerSupplySwitchState == POWERSUPPLYTYPE._25kV)
                    PowerSupplySwitchState = POWERSUPPLYTYPE._3kV;
            TCSButtonPressed[(int)(TCSCommandEvent.CommutatoreTensione)] = false;
            if (TCSButtonReleased[(int)(TCSCommandEvent.CommutatoreTensione)])
                TCSButtonReleased[(int)(TCSCommandEvent.CommutatoreTensione)] = false;
            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_CommutatoreTensione) - 1, (int)PowerSupplySwitchState);
            // Avoid wrong pantographs up
            if (IsTrainControlEnabled())
            {
                if (POCState == POCSTATE.Zone3kV)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._25kV, i]) == PantographState.Up || GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._25kV, i]) == PantographState.Raising)
                        {
                            SetPantographUp(PantoIndex[(int)POWERSUPPLYTYPE._3kV, i]);
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._25kV, i]);
                        }
                    }
                }
                else if (POCState == POCSTATE.Zone25kV)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        if (GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._3kV, i]) == PantographState.Up || GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._3kV, i]) == PantographState.Raising)
                        {
                            SetPantographUp(PantoIndex[(int)POWERSUPPLYTYPE._25kV, i]);
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._3kV, i]);
                        }
                    }
                }
            }

            TensioneAiPantokV = 0;
            if (!(ArePantographsDown() || NeutralZone))
            {
                if ((GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 0]) == PantographState.Up || GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 1]) == PantographState.Up)
                    && POCState == POCSTATE.Zone25kV)
                    TensioneAiPantokV = 25;
                else if ((GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 0]) == PantographState.Up || GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 1]) == PantographState.Up)
                    && POCState == POCSTATE.Zone3kV)
                    TensioneAiPantokV = 3;
            }
            SetCabDisplayControl((int)(TCSCommandEvent.TensioneAiPanto) - 1, TensioneAiPantokV);



            switch (ActiveTCS)
            {
                case TCS.SCMT:
                    SCMTCurrentAlertSpeedKpH = 3f;
                    SCMTCurrentEBSpeedKpH = 5f;
                    SCMTNextAlertSpeedKpH = 3f;
                    SCMTNextEBSpeedKpH = 5f;
                    ETCSStatus.DMIActive = true;
                    ETCSStatus.PlanningAreaShown = false;
                    ETCSStatus.TimeToPermittedS = null;
                    ETCSStatus.CurrentMode = Mode.SB;
                    SetCabDisplayControl((int)(TCSCommandEvent.TargetDistance) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.MascheraBarraFrenata) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.MascheraReleaseSpeed) - 1, 1);
                    if (IsTrainControlEnabled() || Autopilot)
                    {
                        UpdateSSBState();
                        UpdateSCMTState();
                    }
                    if (RSCState == RSCSTATE.Active)
                        SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTSTATE.ActiveAndRSC + 1);
                    else
                        SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTState + 1);
                    if (SCMTState != SCMTSTATE.Autotest)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_SCMT) - 1, SCMTState == SCMTSTATE.Active || SCMTState == SCMTSTATE.ActiveAndRSC ? 1 : 0);
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_Pulsante_RSC) - 1, RSCState == RSCSTATE.Active ? 1 : RSCState == RSCSTATE.Inactive ? 0 : 2);
                    }

                    if (SCMTState != SCMTSTATE.Off && SCMTState != SCMTSTATE.Autotest &&
                        SCMTState != SCMTSTATE.Start && SCMTState != SCMTSTATE.ConfermaStart ||RSCState == RSCSTATE.Active)
                    {
                        UpdateSignal();
                        UpdateInputs();
                    }

                    if (SCMTState != SCMTSTATE.Manovra && (IsTrainControlEnabled() || Autopilot))
                    {
                        if (RSCState == RSCSTATE.Active)
                            CheckNoRSCZone();
                        if (RSCMonitor) UpdateRSCState();
                        if (RSCState == RSCSTATE.Active)
                            UpdateRSC();
                        SetCabDisplayControl((int)(TCSCommandEvent.Cancella_Pulsante_Manovra) - 1, SpeedMpS() > 0.01f ? 1 : 0);
                    }

                    if (SCMTState != SCMTSTATE.Off && SCMTState != SCMTSTATE.Autotest &&
                    SCMTState != SCMTSTATE.Start && SCMTState != SCMTSTATE.ConfermaStart)
                    {
                        if (IsTrainControlEnabled() || Autopilot)
                        {
                            SetCabDisplayControl((int)(TCSCommandEvent.Attivazione_SR) - 1, AttivazioneSR == ATTIVAZIONESR.Active || AttivazioneSR == ATTIVAZIONESR.SignalPassed ? 1 : 0);

                            SetCabDisplayControl((int)(TCSCommandEvent.Vigilante) - 1, VigilanceMonitorState == MonitorState.Emergency ? 1 : 0);

                            SCMTActualOverspeedDownThreshold = Autopilot ? SCMTAutopilotOverspeedDownThresholdKpH : SCMTOverspeedDownThresholdKpH;

                            if (SCMTState == SCMTSTATE.Active || RSCState == RSCSTATE.Active)
                            {
                                UpdateAttivazioneSR();
                            }
							if (SCMTState == SCMTSTATE.Active)
                            {
                            SetCabDisplayControl((int)(TCSCommandEvent.Approccio10) - 1, SCMTVelocitaRilascio == 10f && SCMTNextSignalSpeedLimitKpH == 0 ? 1 : 0);
                            }
                            CheckSPAD();
							UpdateLuceBlu();
                            if (AutopilotButtonPressTimer.Triggered)
                            {
                                TriggerSoundPenalty2();
                                AutopilotButtonPressTimer.Stop();
                            }
                            if ((normalSignalPassed) && SCMTState != SCMTSTATE.Active && SCMTState != SCMTSTATE.Manovra)
                            {
                                SCMTState = SCMTSTATE.Active;
                                TriggerSoundInfo1();
                            }
                            if (SCMTState == SCMTSTATE.Active || SCMTState == SCMTSTATE.Predisposizione || SCMTState == SCMTSTATE.Manovra)
                            {
                                UpdateSCMT();
                            }
                            if (VigilanceMonitor)
                                UpdateVigilance();
                            if (StartupTimer.Triggered)
                            {
                                UpdateSCMTOverspeedMonitor();
                                SetInterventionRequests();
                            }
                            if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] && EmergencyBrakingRequest == true)
                            {
                                TriggerGenericSound(Event.GenericEvent2);
                                if (Math.Abs(SpeedKpH) < 0.36f)
                                {
                                    SCMTEmergencyBraking = false;
                                    VigilanceMonitorState = MonitorState.Disabled;
                                    TriggerSoundAlert2();
                                    SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 0);
                                    if (RSCState == RSCSTATE.AckMissed)
                                    {
                                        RSCState = RSCSTATE.WaitingAck;
                                        TriggerSoundWarning1();
                                        SCMTWarningTimer.Start();
                                    }

                                    if (SCMTSpad)
                                    {
                                        ControlMode = GetControlMode();
                                        SetCabDisplayControl((int)(TCSCommandEvent.SR) - 1, 0);
                                    }
                                    if (RSCState == RSCSTATE.WaitingShutdown)
                                    {
                                        RSCState = RSCSTATE.Inactive;
                                        RSCPreviousCode = RSCCODE.None;
                                        RSCFirstRun = false;
                                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Invalid;
                                        RSCFirstCodeReceived = false;
                                    }
                                }
                                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_spente_lampeggio) - 1, 0);
                                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_accese_lampeggio) - 1, 0);
                                SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 0);
                                if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.AckMissed) RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Active;
                                else if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.Timer12secExpired)
                                {
                                    SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 1);
                                    RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingPre;
                                }
                            }
                        }
                    }
                    else if (SCMTState == SCMTSTATE.Autotest)
                        UpdateSCMTAutotest();
                    break;
                case TCS.ETCSL2:
                    SCMTCurrentAlertSpeedKpH = ETCSSpeedLimitToleranceKpH;
                    SCMTCurrentEBSpeedKpH = 3.0f + ETCSSpeedLimitToleranceKpH;
                    SCMTNextAlertSpeedKpH = ETCSSpeedLimitToleranceKpH;
                    SCMTNextEBSpeedKpH = 3.0f + ETCSSpeedLimitToleranceKpH;
                    ETCSStatus.DMIActive = true;
                    ETCSStatus.PlanningAreaShown = ETCSL2State == ETCSL2STATE.FullSupervision;
                    if (ETCSL2State != ETCSL2STATE.FullSupervision)
                        SetCabDisplayControl((int)(TCSCommandEvent.MascheraReleaseSpeed) - 1, 1);
                    ETCSStatus.CurrentMode = Mode.FS;
                    RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Inactive;
                    SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)ETCSL2State + 1);
                    SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_SCMT) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_Pulsante_RSC) - 1, 3); //fully black
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_spente_lampeggio) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_accese_lampeggio) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, 0);
                    if (IsTrainControlEnabled() || Autopilot)
                    {
                        UpdateSSBState();
                        UpdateETCSL2State();
                    }

                    if (ETCSL2State == ETCSL2STATE.FullSupervision)
                    {
                        UpdatePOCState();
                        UpdatePCFState();
                        UpdateSignal();
                        UpdateInputs();

                        if (IsTrainControlEnabled() || Autopilot)
                        {
                            SetCabDisplayControl((int)(TCSCommandEvent.Attivazione_SR) - 1,
                                AttivazioneSR == ATTIVAZIONESR.Active || AttivazioneSR == ATTIVAZIONESR.SignalPassed ? 1 : 0);
//                            SetCabDisplayControl((int)(TCSCommandEvent.Vigilante) - 1, VigilanceMonitorState == MonitorState.Emergency ? 1 : 0);
                            SetCabDisplayControl((int)(TCSCommandEvent.Cancella_Pulsante_Manovra) - 1, 1);

                            SCMTActualOverspeedDownThreshold = ETCSOverspeedDownThresholdKpH;

                            CheckSPAD();
;
                            
                            if (AutopilotButtonPressTimer.Triggered)
                            {
                                TriggerSoundPenalty2();
                                AutopilotButtonPressTimer.Stop();
                            }

                            UpdateETCSL2();

                            if (VigilanceMonitor)
                                UpdateVigilance();
                            if (StartupTimer.Triggered)
                            {
                                UpdateETCSL2OverspeedMonitor();
                                SetInterventionRequests();
                            }
                            if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] && EmergencyBrakingRequest == true)
                            {
                                TriggerGenericSound(Event.GenericEvent2);
                                if (Math.Abs(SpeedKpH) < 0.36f)
                                {
                                    SCMTEmergencyBraking = false;
                                    VigilanceMonitorState = MonitorState.Disabled;
                                    TriggerSoundAlert2();
                                    SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 0);
                                    if (RSCState == RSCSTATE.AckMissed)                                    {
                                        RSCState = RSCSTATE.WaitingAck;
                                        TriggerSoundWarning1();
                                        SCMTWarningTimer.Start();
                                    }

                                    if (SCMTSpad)
                                    {
                                        ControlMode = GetControlMode();
                                        SetCabDisplayControl((int)(TCSCommandEvent.SR) - 1, 0);
                                    }
                                }
                                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_spente_lampeggio) - 1, 0);
                                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_accese_lampeggio) - 1, 0);
                                SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 0);
                            }


                            // Update monitoring status
                            ETCSStatus.CurrentMonitor = Monitor.CeilingSpeed;
                            CurrentMaxSpeedKpH = Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH);
                            if (Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH) + 1 < Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH)
                                && Math.Min(ETCSSignalIndicationSpeedCurveKpH, ETCSSpeedPostIndicationSpeedCurveKpH) + 1 < Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH)
                                && SpeedKpH > Math.Min(SCMTSignalTargetSpeedKpH, SCMTSpeedPostTargetSpeedKpH))
                                ETCSStatus.CurrentMonitor = Monitor.TargetSpeed;
                            if (SCMTNextSignalSpeedLimitKpH == 0 && SCMTSignalTargetSpeedKpH > SCMTNextSignalSpeedLimitKpH && NormalSignals[0].DistanceM < 200f)
                                ETCSStatus.CurrentMonitor = Monitor.ReleaseSpeed;
                            if (SCMTOverspeed)
                                ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Intervention;
                            else if (SCMTOverspeedWarning)
                            {
                                ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Warning;
                            }
                            //                       else if (Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH) + 1 < Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH) ||
                            //                           SpeedKpH > Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH))
                            else if (ETCSStatus.CurrentMonitor == Monitor.TargetSpeed || ETCSStatus.CurrentMonitor == Monitor.ReleaseSpeed)
                            {
                                if (SpeedKpH > Math.Min(SCMTSignalAlertSpeedCurveKpH, SCMTSpeedPostAlertSpeedCurveKpH))
                                    ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Warning;
                                else if (SpeedKpH > Math.Min(ETCSSignalIndicationSpeedCurveKpH, ETCSSpeedPostIndicationSpeedCurveKpH))
                                    ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Indication;
                                else
                                    ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Normal;
                            }
                            else
                                ETCSStatus.CurrentSupervisionStatus = SupervisionStatus.Normal;


                            // Display status
                            var targetDistanceM = 0f;
                            if (Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH) + 1 < Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH))
                            // compute target distance to display it
                            {
                                targetDistanceM = Math.Max(SCMTSignalTargetDistanceM, SCMTSpeedPostTargetDistanceM);
                                if (SCMTNextSignalSpeedLimitKpH + 1 > CurrentMaxSpeedKpH)
                                    targetDistanceM = SCMTSpeedPostTargetDistanceM;
                                else if (SCMTNextSpeedPostSpeedLimitKpH + 1 > CurrentMaxSpeedKpH)
                                    targetDistanceM = SCMTSignalTargetDistanceM;
                                else if (SCMTNextSignalSpeedLimitKpH >= SCMTNextSpeedPostSpeedLimitKpH && SCMTSignalTargetDistanceM > SCMTSpeedPostTargetDistanceM)
                                    targetDistanceM = SCMTSpeedPostTargetDistanceM;
                                else if (SCMTNextSpeedPostSpeedLimitKpH >= SCMTNextSignalSpeedLimitKpH && SCMTSpeedPostTargetDistanceM > SCMTSignalTargetDistanceM)
                                    targetDistanceM = SCMTSignalTargetDistanceM;
                            }
                            SetCabDisplayControl((int)(TCSCommandEvent.TargetDistance) - 1, targetDistanceM);
                            SetCabDisplayControl((int)(TCSCommandEvent.MascheraBarraFrenata) - 1, targetDistanceM == 0 ? 0 : 1);
                            // calculation of braking distance bar
                            if (targetDistanceM == 0)
                                BrakingDistanceBar = 0f;
                            else
                            {
                                if (targetDistanceM <= 100.0f)
                                    BrakingDistanceBar = targetDistanceM * 0.1774f;
                                else if (targetDistanceM >= 1000.0f)
                                    BrakingDistanceBar = 100f;
                                else
                                    BrakingDistanceBar = 17.74f + Convert.ToSingle(Math.Log10(targetDistanceM * 0.01f)) * 82.25f;
                                SetCabDisplayControl((int)(TCSCommandEvent.BarraDistanzaFrenata) - 1, BrakingDistanceBar);
                            }

                            // display braking square
                            if (ETCSStatus.CurrentMonitor == Monitor.TargetSpeed || ETCSStatus.CurrentMonitor == Monitor.ReleaseSpeed)
                            {
                                var permittedSpeedCurveKpH = Math.Min(SCMTSignalAlertSpeedCurveKpH, SCMTSpeedPostAlertSpeedCurveKpH);
                                if (SpeedKpH > Math.Min(ETCSSignalIndicationSpeedCurveKpH, ETCSSpeedPostIndicationSpeedCurveKpH))
                                {
                                    ETCSStatus.TimeToPermittedS = Math.Max(MpS.FromKpH(permittedSpeedCurveKpH - SpeedKpH) / SafeDecelerationMpS2, 0);
                                }
                                else
                                    ETCSStatus.TimeToPermittedS = null;
                            }
                            else ETCSStatus.TimeToPermittedS = null;

                            // display release speed
                            SetCabDisplayControl((int)(TCSCommandEvent.MascheraReleaseSpeed) - 1, ETCSSignalIndicationTargetSpeedKpH == 0 ? 0 : 1);
                            if (ETCSSignalIndicationTargetSpeedKpH == 0)
                            {
                                SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_ReleaseSpeed) - 1, ReleaseSpeed);
                                ETCSStatus.ReleaseSpeedMpS = MpS.FromKpH(ReleaseSpeed + ETCSSpeedLimitToleranceKpH);
                            }
                            else
                                ETCSStatus.ReleaseSpeedMpS = null;
                            if (ETCSStatus.PlanningAreaShown)
                                BuildUpPlanningProfile();

                        }
                    }
                    else if (ETCSL2State == ETCSL2STATE.Autotest)
                        UpdateSCMTAutotest();
                    break;
            }
            if (IsTrainControlEnabled() || Autopilot)
            {
                if (TCSButtonReleased[(int)(TCSCommandEvent.Conferma_Infobox)])
                {
                    if (SCMTSpad)
                    {
                        if (ControlMode != TRAIN_CONTROL.EXPLORER)
                            RequestToggleManualMode();
                        SCMTSpad = false;
                        RSCCode = RSCCODE.None;
                    }
                }

                //               EmergencyBrakingRequest = RSOEmergencyBraking || VACMAEmergencyBraking || ExternalEmergencyBraking;
                SetEmergencyBrake(EmergencyBrakingRequest);
                SetFullBrake(!EmergencyBrakingRequest && FullBrakingRequest);
                SetTractionAuthorization((!DoesBrakeCutPower() || LocomotiveBrakeCylinderPressureBar() < BrakeCutsPowerAtBrakeCylinderPressureBar()) && !PowerCutRequest &&
                    (SCMTState == SCMTSTATE.Manovra || SCMTState == SCMTSTATE.Predisposizione || SCMTState == SCMTSTATE.Active || SCMTState == SCMTSTATE.ActiveAndRSC || 
                    ETCSL2State == ETCSL2STATE.FullSupervision));
                SetFullDynamicBrake(DynamicBrakingRequest);

                SetPenaltyApplicationDisplay(IsBrakeEmergency() || IsBrakeFullService());
            }

            SetCabDisplayControl((int)(TCSCommandEvent.MessaggiInfobox) - 1, (int)messaggioInfoboxAttivo);
            var timestampOn = SCMTState == SCMTSTATE.Start || SCMTState == SCMTSTATE.ConfermaStart ||
                SCMTOverspeedMonitorState == MonitorState.ReadyToReset || EmergencyBrakingRequest && Math.Abs(SpeedKpH) < 0.36f
                || messaggioInfoboxAttivo != MESSAGGIINFOBOX.Void ? 1 : 0;
            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_Timestamp) - 1, timestampOn);
            for (int i = 0; i < TCSButtonPressed.Length; i++)
            {
                TCSButtonPressed[i] = false;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Conferma_Infobox)])
            {
                TCSButtonReleased[(int)(TCSCommandEvent.Conferma_Infobox)] = false;
            }
        }

        // First Update Initializations

        protected void FirstUpdateInitializations()
        {
            // Do it here because before Train is not defined
            SCMTTrainSpeedLimitKpH = GetFloatParameter("SCMT", "TrainSpeedLimitKpH", MpS.ToKpH(TrainMaxSpeedMpS()));
            if (ColdStart)
            {
                SelectStartupTCS();
            }
            switch (ActiveTCS)
            {
                case TCS.SCMT:
                    if (ColdStart)
                    {
                        SCMTState = (!IsColdStart() && !DoesStartFromTerminalStation() && SCMTMonitor ||
                            SpeedMpS() > 0f) ? SCMTSTATE.Active : SCMTSTATE.Off;
                        SSBState = SCMTState == SCMTSTATE.Off ? SSBSTATE.Off : SSBSTATE.On;
                        if (DoesStartFromTerminalStation())
                        {
                            NoRSCZone = true;
                            RSCPresent = false;
                        }
                        if (SCMTState == SCMTSTATE.Off && SCMTMonitor)
                            SetTractionAuthorization(false);
                    }
                    if (!RSCPresent && !NoRSCZone) CheckRSCPresent();
                    if (ColdStart)
                    {
                        RSCState = !IsColdStart() && !DoesStartFromTerminalStation() && RSCMonitor && RSCPresent ? RSCSTATE.Active : RSCSTATE.Inactive;
                        RSCFirstRun = RSCState == RSCSTATE.Active && IsColdStart();
                    }
                    if (RSCState == RSCSTATE.Active)
                        SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTSTATE.ActiveAndRSC + 1);
                    else
                        SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)SCMTState + 1);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCODE.C75 : 0);

                    distanceSignalDistanceM = NextDistanceSignalDistanceM();
                    normalSignalDistanceM = NextSignalDistanceM(0);
                    infoSignalDistanceM = NextGenericSignalDistanceM("INFO");
                    normalSignalPassed = false;
                    distanceSignalPassed = false;
                    if (normalSignalDistanceM > 0)
                    {
                        if (normalSignalDistanceM != float.MaxValue && AutodetectSignalFilesType)
                            SignalFilesType = DoesNextNormalSignalHaveRepeaterHead() ? SIGNALFILESTYPE.SerriniBaldi : SIGNALFILESTYPE.PriviteraBono;
                        UpdateParams();
                    }
                    if (RSCState == RSCSTATE.Active)
                        SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, 11 - (int)RSCCode);
                    break;
                case TCS.ETCSL2:
                    if (ColdStart)
                    {
                        SelectStartupTension();
                        ETCSL2State = (!IsColdStart() && !DoesStartFromTerminalStation() && SCMTMonitor ||
                            SpeedMpS() > 0f) ? ETCSL2STATE.FullSupervision : ETCSL2STATE.Off;
                        SSBState = ETCSL2State == ETCSL2STATE.Off ? SSBSTATE.Off : SSBSTATE.On;
                        if (ETCSL2State == ETCSL2STATE.Off && SCMTMonitor)
                            SetTractionAuthorization(false);
                    }
                    SetCabDisplayControl((int)(TCSCommandEvent.Splash) - 1, (int)ETCSL2State + 1);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCODE.C75 : 0);

                    distanceSignalDistanceM = NextDistanceSignalDistanceM();
                    normalSignalDistanceM = NextSignalDistanceM(0);
                    infoSignalDistanceM = NextGenericSignalDistanceM("INFO");
                    normalSignalPassed = false;
                    distanceSignalPassed = false;
                    if (normalSignalDistanceM > 0)
                    {
                        if (normalSignalDistanceM != float.MaxValue && AutodetectSignalFilesType)
                            SignalFilesType = DoesNextNormalSignalHaveRepeaterHead() ? SIGNALFILESTYPE.SerriniBaldi : SIGNALFILESTYPE.PriviteraBono;
                        UpdateParams();
                    }
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, 0);
                    break;
            }
        }

        protected void SelectStartupTCS()
        {
            GetSignalData();
            // Check if there is an AV signal in front
            if (NormalSignals[0].DistanceM != float.MaxValue)
            {
                var nextSignalTypeName = NormalSignals[0].MainHeadSignalTypeName.ToLower();
                foreach (string etcsL2Signal in ETCSL2Signals)
                    if (nextSignalTypeName.Contains(etcsL2Signal))
                    {
                        ActiveTCS = TCS.ETCSL2;
                        TCSTransitionState = TCSTRANSITIONSTATE.ETCS;
                        break;
                    }
            }
        }

        protected void SelectStartupTension()
        {
            // Check if there is a POC or a PCF info signal in front; if there is PCF, we are at 25kV; if there is a POC, check type

            foreach (var infoSignal in InfoSignals)
            {
                POCState = POCSTATE.Zone25kV;
                PowerSupplySwitchState = POWERSUPPLYTYPE._25kV;
                var infoSignalMainHeadName = infoSignal.MainHeadSignalTypeName.ToLower();
                if (infoSignalMainHeadName == "avvstartneutro") // PCF
                    break;
                if (infoSignalMainHeadName == "avvpoc3-25") // we are in 3 kV zone
                {
                    POCState = POCSTATE.Zone3kV;
                    PowerSupplySwitchState = POWERSUPPLYTYPE._3kV;
                    break;
                }
                if (infoSignalMainHeadName == "avvpoc25-3") // we are in 25kV zone
                    break;
                if (infoSignalMainHeadName == "avvendertms" || infoSignalMainHeadName == "endertms") // we are just before end of ERTMS
                {
                    POCState = POCSTATE.Zone3kV;
                    PowerSupplySwitchState = POWERSUPPLYTYPE._3kV;
                    break;
                }
            }
        }

        public void UpdateInputs()
        {
        }

        void UpdateVigilance()
        {
            switch (VigilanceMonitorState)
            {
                case MonitorState.Disabled:
                    if (VigilanceSystemEnabled && SpeedKpH < 0.36f)
                    {
                        VigilanceMonitorState = MonitorState.StandBy;
                    }
                    break;

                case MonitorState.StandBy:
                    if (!VigilanceSystemEnabled)
                    {
                        VigilanceMonitorState = MonitorState.Disabled;
                    }
                    else
                    {
                        if (SpeedKpH > 2.5f)
                        {
                            VigilanceMonitorState = MonitorState.Alarm;
                            TriggerGenericSound(Event.GenericEvent1);
                        }
                        
                        if (VigilanceEmergencyTimer.Started)
                        {
                            VigilanceEmergencyTimer.Stop();
                            TriggerSoundAlert2();
                        }
                    }
                    break;

                case MonitorState.Alarm:
                    if (!VigilanceSystemEnabled)
                    {
                        VigilanceMonitorState = MonitorState.Disabled;
                    }
                    else
                    {
                        if (!VigilanceEmergencyTimer.Started)
                        {
                            VigilanceEmergencyTimer.Start();
                            if (Autopilot) AutopilotTimer.Start();
                        }
                        if (VigilanceEmergencyTimer.Started && (ResetButtonPressed || AutopilotTimer.Triggered))
                        {
                            VigilanceEmergencyTimer.Stop();
                            AutopilotTimer.Stop();
                            TriggerSoundAlert2();
                            VigilanceMonitorState = MonitorState.Disabled;
                        }
                        if (VigilanceEmergencyTimer.Triggered)
                        {
                            VigilanceEmergencyTimer.Stop();
                            TriggerSoundAlert2();
                            TriggerSoundWarning1();
                            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                            VigilanceMonitorState = MonitorState.Emergency;
                        }
                    }
                    break;

                case MonitorState.Emergency:
                    break;
            }

            SetVigilanceAlarmDisplay(VigilanceMonitorState == MonitorState.Alarm);
            SetVigilanceEmergencyDisplay(VigilanceMonitorState == MonitorState.Emergency);
        }

        void UpdateSCMTOverspeedMonitor()
        { 
            switch (SCMTOverspeedMonitorState)
            {
                case MonitorState.Disabled:
                    if (SpeedControlSystemEnabled)
                    {
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                    }
                    break;

                case MonitorState.StandBy:
                    if (!SpeedControlSystemEnabled)
                    {
                        SCMTOverspeedMonitorState = MonitorState.Disabled;
                    }
                    else
                    {
                        if (SCMTOverspeed)
                        {
                            TriggerSoundInfo2();
                            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                            SetCabDisplayControl((int)(TCSCommandEvent.Luce_rossa_tacho) - 1, 2);
                            SCMTOverspeedMonitorState = MonitorState.Emergency;
                        }
                        else if (SCMTOverspeedWarning)
                        {
                            TriggerSoundPenalty1();
                            SetCabDisplayControl((int)(TCSCommandEvent.Luce_rossa_tacho) - 1, 1);
                            SCMTOverspeedMonitorState = MonitorState.Alarm;
                        }
 
                    }
                    break;
                case MonitorState.Alarm:
                    if (SCMTOverspeed)
                    {
                        TriggerSoundAlert2();
                        TriggerSoundInfo2();
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                        SetCabDisplayControl((int)(TCSCommandEvent.Luce_rossa_tacho) - 1, 2);
                        SCMTOverspeedMonitorState = MonitorState.Emergency;
                    }
                    else if (SCMTOverspeedReadyToReset)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.Luce_rossa_tacho) - 1, 0);
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                        TriggerSoundAlert2();
                    }
                    break;
                case MonitorState.Emergency:
                    if (SCMTOverspeedReadyToReset)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 1);
                        SCMTOverspeedMonitorState = MonitorState.ReadyToReset;
                        if (Autopilot) AutopilotTimer.Start();
                    }
                    break;
                case MonitorState.ReadyToReset:
                    if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] || Autopilot && AutopilotTimer.Triggered)
                    {
                        AutopilotTimer.Stop();
                        AutopilotButtonPressTimer.Start();
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 0);
                        SetCabDisplayControl((int)(TCSCommandEvent.Luce_rossa_tacho) - 1, 0);
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                        TriggerGenericSound(Event.GenericEvent2);
                        TriggerSoundAlert2();
                    }
                    else
                    {

                    }
                    break;
            }
        }

        public override void SetEmergency(bool emergency)
        {
            ExternalEmergencyBraking = emergency;
        }

        protected void UpdateSSBState()
        {
            switch (SSBState)
            {
                case SSBSTATE.ReadyToOn:
                    if (TCSButtonPressed[(int)(TCSCommandEvent.AccendiSpegniSSB)] && 
                        (SCMTState == SCMTSTATE.Off && ActiveTCS == TCS.SCMT || ETCSL2State == ETCSL2STATE.Off && ActiveTCS == TCS.ETCSL2))
                    {
                        SCMTAutotestTimer.Setup(2);
                        SCMTAutotestTimer.Start();
                        SSBState = SSBSTATE.On;
                        TriggerSoundSystemActivate();
                    }
                    break;
                case SSBSTATE.On:
                    if (TCSButtonPressed[(int)(TCSCommandEvent.AccendiSpegniSSB)])
                    {
                        SCMTState = SCMTSTATE.Off;
                        RSCState = RSCSTATE.Inactive;
                        SSBState = SSBSTATE.ReadyToOn;
                        ETCSL2State = ETCSL2STATE.Off;
                        TriggerSoundSystemDeactivate();
                    }
                    if (CircuitBreakerOpened)
                    {
                        TriggerSoundSystemDeactivate();
                    }
                    break;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.AccendiSpegniSSB)])
                TCSButtonReleased[(int)(TCSCommandEvent.AccendiSpegniSSB)] = false;
            CircuitBreakerOpened = false;
        }

        protected void UpdateTCSTransitionState()
        {
            switch (TCSTransitionState)
            {
                case TCSTRANSITIONSTATE.SCMT:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "avvstartertms" || MpS.ToKpH(NextPostSpeedLimitMpS(0)) > 250.0f && NextPostDistanceM(0) < 1000)
                    {
                        RadioLinkState = true;
                        TriggerGenericSound(Event.GenericEvent3);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Liv2Annunciato;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToETCSRadiolink;
                        PreviousInfoSignalMainHeadSignalType = "";
                    }
                    break;
                case TCSTRANSITIONSTATE.ToETCSRadiolink:
                    if (infoSignalMainHeadSignalType == "startertms" && infoSignalDistanceM < 30.0f || MpS.ToKpH(NextPostSpeedLimitMpS(0)) > 250.0f && NextPostDistanceM(0) < 500)
                    {
                        TriggerGenericSound(Event.GenericEvent3);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.ConfermareTransizioneLiv2;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToETCSRequestSwitch;
                        if (Autopilot) AutopilotTimer.Start();
                        TCSTransitionConfirmationTimer.Start();
                    }
                    break;
                case TCSTRANSITIONSTATE.ToETCSRequestSwitch:
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] || AutopilotTimer.Triggered) && TCSTransitionConfirmationTimer.Started)
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToETCSAckSwitchWaitSegnaleConfine;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "startertms")
                    {
                        ActiveTCS = TCS.ETCSL2;
                        ETCSL2State = ETCSL2STATE.FullSupervision;
                        PreviousInfoSignalMainHeadSignalType = "";
                        if (TCSTransitionState == TCSTRANSITIONSTATE.ToETCSAckSwitchWaitSegnaleConfine)
                            TCSTransitionState = TCSTRANSITIONSTATE.ETCS;
                        else TCSTransitionState = TCSTRANSITIONSTATE.ToETCSSegnaleConfinePassed;
                    }
                    break;
                case TCSTRANSITIONSTATE.ToETCSSegnaleConfinePassed:
                    ActiveTCS = TCS.ETCSL2;
                    ETCSL2State = ETCSL2STATE.FullSupervision;
                    PreviousInfoSignalMainHeadSignalType = "";
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] || AutopilotTimer.Triggered) && TCSTransitionConfirmationTimer.Started)
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.ETCS;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    else if (TCSTransitionConfirmationTimer.Triggered)
                    {
                        //Emergency braking
                        TCSTransitionState = TCSTRANSITIONSTATE.ToETCSTransitionTimeoutAfterSegnaleConfine;
                    }
                    break;
                case TCSTRANSITIONSTATE.ToETCSTransitionTimeoutBeforeSegnaleConfine:
                    break;
                case TCSTRANSITIONSTATE.ToETCSTransitionTimeoutAfterSegnaleConfine:
                    if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)])
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.ETCS;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    break;
                case TCSTRANSITIONSTATE.ToETCSAckSwitchWaitSegnaleConfine:
                    // add displays
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "startertms")
                    {
                        ActiveTCS = TCS.ETCSL2;
                        ETCSL2State = ETCSL2STATE.FullSupervision;
                        PreviousInfoSignalMainHeadSignalType = "";
                        TCSTransitionState = TCSTRANSITIONSTATE.ETCS;
                    }
                    break;
                case TCSTRANSITIONSTATE.ETCS:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "avvendertms" || MpS.ToKpH(NextPostSpeedLimitMpS(0)) <= 100.0f && NextPostDistanceM(0) < 1000)
                    {
                        TriggerGenericSound(Event.GenericEvent3);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.LivSTMAnnunciato;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToSCMTAnnunciato;
                        PreviousInfoSignalMainHeadSignalType = "";
                    }
                    break;
                case TCSTRANSITIONSTATE.ToSCMTAnnunciato:
                    if (infoSignalMainHeadSignalType == "endertms" && infoSignalDistanceM < 30.0f || MpS.ToKpH(NextPostSpeedLimitMpS(0)) <= 100.0f && NextPostDistanceM(0) < 500)
                    {
                        TriggerGenericSound(Event.GenericEvent3);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.ConfermareTransizioneLivSTM;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToSCMTRequestSwitch;
                        if (Autopilot) AutopilotTimer.Start();
                        TCSTransitionConfirmationTimer.Start();
                    }
                    break;
                case TCSTRANSITIONSTATE.ToSCMTRequestSwitch:
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] || AutopilotTimer.Triggered) && TCSTransitionConfirmationTimer.Started)
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.ToSCMTAckSwitchWaitSegnaleConfine;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "endertms")
                    {
                        ActiveTCS = TCS.SCMT;
                        ActivateSCMTRSC();
                        ETCSL2State = ETCSL2STATE.Off;
                        RadioLinkState = false;
                        PreviousInfoSignalMainHeadSignalType = "";
                        if (TCSTransitionState == TCSTRANSITIONSTATE.ToSCMTAckSwitchWaitSegnaleConfine)
                            TCSTransitionState = TCSTRANSITIONSTATE.SCMT;
                        else TCSTransitionState = TCSTRANSITIONSTATE.ToSCMTSegnaleConfinePassed;
                    }
                    break;
                case TCSTRANSITIONSTATE.ToSCMTSegnaleConfinePassed:
                    ActiveTCS = TCS.SCMT;
                    ActivateSCMTRSC();
                    ETCSL2State = ETCSL2STATE.Off;
                    RadioLinkState = false;
                    PreviousInfoSignalMainHeadSignalType = "";
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] || AutopilotTimer.Triggered) && TCSTransitionConfirmationTimer.Started)
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.SCMT;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    else if (TCSTransitionConfirmationTimer.Triggered)
                    {
                        //Emergency braking
                        TCSTransitionState = TCSTRANSITIONSTATE.ToSCMTTransitionTimeoutAfterSegnaleConfine;
                    }
                    break;
                case TCSTRANSITIONSTATE.ToSCMTTransitionTimeoutBeforeSegnaleConfine:
                    break;
                case TCSTRANSITIONSTATE.ToSCMTTransitionTimeoutAfterSegnaleConfine:
                    if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)])
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AutopilotTimer.Stop();
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        TCSTransitionState = TCSTRANSITIONSTATE.SCMT;
                        TCSTransitionConfirmationTimer.Stop();
                    }
                    break;
                case TCSTRANSITIONSTATE.ToSCMTAckSwitchWaitSegnaleConfine:
                    // add displays
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "endertms")
                    {
                        ActiveTCS = TCS.SCMT;
                        ETCSL2State = ETCSL2STATE.Off;
                        RadioLinkState = false;
                        ActivateSCMTRSC();
                        PreviousInfoSignalMainHeadSignalType = "";
                        TCSTransitionState = TCSTRANSITIONSTATE.SCMT;
                    }
                    break;
                default:
                    break;
            }
        }

        protected void ActivateSCMTRSC()
        {
            SCMTState = SCMTSTATE.ActiveAndRSC;
            RSCState = RSCSTATE.Active;
            RSCCode = RSCCODE.None;
            RSCTempNewCode = RSCCODE.None;
            RSCPreviousCode = RSCCODE.None;
            RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Inactive;
            NoRSCZone = false;
            RSCPresent = true;
        }

        protected void SetInterventionRequests()
        {

            EmergencyBrakingRequest = false;
            FullBrakingRequest = false;
            PowerCutRequest = false;
            DynamicBrakingRequest = false;
            if (VigilanceMonitor)
            {
                if (VigilanceAppliesEmergencyBrake)
                    EmergencyBrakingRequest |= (VigilanceMonitorState == MonitorState.Emergency);
                else if (VigilanceAppliesFullBrake)
                    FullBrakingRequest |= (VigilanceMonitorState == MonitorState.Emergency);

                if (VigilanceAppliesCutsPower)
                    PowerCutRequest |= (VigilanceMonitorState == MonitorState.Emergency);
            }

            if (SCMTOverspeedMonitor)
            {
                EmergencyBrakingRequest |= (SCMTOverspeedMonitorState == MonitorState.Emergency || SCMTOverspeedMonitorState == MonitorState.ReadyToReset);
                PowerCutRequest |= (SCMTOverspeedMonitorState == MonitorState.Emergency || SCMTOverspeedMonitorState == MonitorState.ReadyToReset);
                //                       DynamicBrakingRequest |= SCMTOverspeedMonitorState == MonitorState.Alarm;
            }

            if (EmergencyStopMonitor)
            {
                if (EmergencyStopAppliesEmergencyBrake)
                    EmergencyBrakingRequest |= ExternalEmergencyBraking;
                else if (EmergencyStopAppliesFullBrake)
                    FullBrakingRequest |= ExternalEmergencyBraking;

                if (EmergencyStopAppliesCutsPower)
                    PowerCutRequest |= ExternalEmergencyBraking;
            }
            EmergencyBrakingRequest |= SCMTEmergencyBraking;

            if (SCMTMonitor)
                if (SCMTAppliesCutsPower)
                    PowerCutRequest |= SCMTEmergencyBraking;

            if (EmergencyBrakingRequest && Math.Abs(SpeedKpH) < 0.36f)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 1);
            }
        }

        protected void UpdateSCMTState()
        {
            if (SpeedMpS() == 0)
            {
                switch (SCMTState)
                {
                    case SCMTSTATE.Off:
                        if (SCMTAutotestTimer.Triggered)
                        {
                            SCMTAutotestTimer.Stop();
                            SCMTState = SCMTSTATE.Autotest;
                            AutotestPhase = AUTOTESTPHASE.InitialWait;
                            SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[0]);
                        }
                        break;
                    case SCMTSTATE.Manovra:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Man)])
                        {
                            TriggerSoundWarning2();
                            SCMTState = SCMTSTATE.Start;
                            TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Man)] = false;
                            SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Man) - 1, 0);
                        }
                        break;
                    case SCMTSTATE.Start:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Bottone_Start)])
                        {
                            TriggerGenericSound(Event.GenericEvent2);
                            SCMTState = SCMTSTATE.ConfermaStart;
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.ConfermareLivSTMNazionale;
                            TCSButtonPressed[(int)(TCSCommandEvent.Bottone_Start)] = false;
                        }
                        break;
                    case SCMTSTATE.ConfermaStart:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)])
                        {
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                            TriggerGenericSound(Event.GenericEvent2);
                            SCMTState = SCMTSTATE.Predisposizione;
                            TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] = false;
                        }
                        break;
                    case SCMTSTATE.Predisposizione:
                    case SCMTSTATE.Active:
                    case SCMTSTATE.ActiveAndRSC:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Man)])
                        {
                            TriggerSoundWarning2();
                            SCMTState = SCMTSTATE.Manovra;
                            TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Man)] = false;
                            SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Man) - 1, 1);
                            RSCState = RSCSTATE.Inactive;
                        }
                        break;
                }
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Bottone_Start)])
            {
                TCSButtonReleased[(int)(TCSCommandEvent.Bottone_Start)] = false;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Man)])
            {
                TriggerSoundPenalty2();
                TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Man)] = false;
            }
        }

        protected void UpdateRSCState()
        {
            switch (RSCState)
            {
                case RSCSTATE.Active:
                    if (NoRSCZone || RSCForceNoCod)
                    {
                        RSCState = RSCSTATE.WaitingShutdown;
                        TriggerSoundWarning1();
                        SCMTWarningTimer.Start();
                        if (Autopilot) AutopilotTimer.Start();
                    }
                    else
                    {
                        RSCFirstRun = false;
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_RSC)])
                        {
                            TriggerSoundWarning2();
                            RSCState = RSCSTATE.Inactive;
                            RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Invalid;
                            SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 0);
                        }
                    }
                    break;
                case RSCSTATE.Inactive:
                    if (RSCCodeConfirmation1Timer.Triggered)
                    {
                        RSCCodeConfirmation1Timer.Stop();
                        RSCState = RSCSTATE.WaitingAck;
                        TriggerSoundWarning1();
                        SCMTWarningTimer.Start();
                        if (Autopilot) AutopilotTimer.Start();
                        RSCPresent = true;
                    }
                    else if (RSCPIPassed)
                    {
                        if (!RSCCodeConfirmation1Timer.Started)
                            RSCCodeConfirmation1Timer.Start();
                    }
                    else if (TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_RSC)] && RSCPresent)
                    {
                        if (!RSCCodeConfirmation1Timer.Started)
                            RSCCodeConfirmation1Timer.Start();
                        RSCState = RSCSTATE.ActivationRequested;
                        TriggerSoundWarning2();
                    }
                    break;
                case RSCSTATE.WaitingAck:
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_RSC)] || AutopilotTimer.Triggered) && SCMTWarningTimer.Started)
                    {
                        TriggerSoundWarning2();
                        TriggerSoundAlert2();
                        SCMTWarningTimer.Stop();
                        AutopilotTimer.Stop();
                        if (Autopilot) AutopilotButtonPressTimer.Start();
                        RSCState = RSCSTATE.Active;
                        NoRSCZone = false;
                        RSCPreviousCode = RSCCODE.None;
                        RSCFirstRun = true;
                        RSCFirstCodeReceived = true;
                    }
                    if (SCMTWarningTimer.Started && SCMTWarningTimer.Triggered)
                    {
                        SCMTEmergencyBraking = true;
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                        RSCState = RSCSTATE.AckMissed;
                        SCMTWarningTimer.Stop();
                    }
                    break;
                case RSCSTATE.WaitingShutdown:
                    if ((TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_RSC)] || AutopilotTimer.Triggered) && SCMTWarningTimer.Started)
                    {
                        TriggerSoundWarning2();
                        TriggerSoundAlert2();
                        SCMTWarningTimer.Stop();
                        AutopilotTimer.Stop();
                        if (Autopilot) AutopilotButtonPressTimer.Start();
                        RSCState = RSCSTATE.Inactive;
                        RSCPreviousCode = RSCCODE.None;
                        RSCFirstRun = false;
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Invalid;
                        SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 0);
                        RSCFirstCodeReceived = false;
                    }
                    else if (SCMTWarningTimer.Triggered)
                    {
                        SCMTWarningTimer.Stop();
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                        SCMTEmergencyBraking = true;
                    }
                    break;
                case RSCSTATE.ActivationRequested:
                    if (RSCCodeConfirmation1Timer.Triggered)
                    {
                        RSCCodeConfirmation1Timer.Stop();
                        RSCState = RSCSTATE.Active;
                        TriggerSoundInfo1();
                    }
                    break;
                default:
                    break;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_RSC)])
            {
                TriggerSoundPenalty2();
                TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_RSC)] = false;
            }
        }

        protected void CheckNoRSCZone()
        {
            if (!NoRSCZone && !RSCForceCod && !RSCForceCod9)
            {
                var nextStationName = NextStationName();
                if (nextStationName != "" && NoRSCStations.Contains(nextStationName) && NextStationDistanceM() < 2000f && normalSignalDistanceM < 100f
                    && (NextSignalDistanceM(1) > NextStationDistanceM() || NextSignalDistanceM(1) == normalSignalDistanceM))
                {
                    NoRSCZone = true;
                    RSCPresent = false;
                }
            }
        }

        protected void CheckRSCPresent()
        {
            if (RSCForceCod || RSCForceCod9)
            {
                RSCPresent = true;
                return;
            }
            if (infoSignalMainHeadSignalType != "startcod" && infoSignalMainHeadSignalType != "startcod9")
            {
                var nextTrailingSwitchDistanceM = NextTrailingDivergingSwitchDistanceM(RSCMaxTrailingDivergingSwitchDistanceM);
                if (nextTrailingSwitchDistanceM == float.MaxValue && !RSCForceNoCod)
                    RSCPresent = true;
            }
        }

        protected void UpdateSCMT()
        {
            if (SCMTState == SCMTSTATE.Manovra || (SCMTState != SCMTSTATE.Active && SCMTState != SCMTSTATE.ActiveAndRSC))
            {
                SCMTCurrentSignalSpeedLimitKpH = 30f;
                SetNextSpeedLimitMpS(MpS.FromKpH(SCMTCurrentSignalSpeedLimitKpH));
                var currentPostSpeedLimitMpS = CurrentPostSpeedLimitMpS();
                SCMTCurrentSpeedPostSpeedLimitKpH = currentPostSpeedLimitMpS > 500 ? SCMTTrainSpeedLimitKpH : MpS.ToKpH(currentPostSpeedLimitMpS);
                SetCurrentSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH)));
                SCMTSignalEmergencySpeedCurveKpH = SCMTCurrentSignalSpeedLimitKpH + SCMTCurrentEBSpeedKpH;
                SCMTSignalAlertSpeedCurveKpH = SCMTCurrentSignalSpeedLimitKpH + SCMTCurrentAlertSpeedKpH;
                SCMTSpeedPostEmergencySpeedCurveKpH = SCMTCurrentSpeedPostSpeedLimitKpH + SCMTCurrentEBSpeedKpH;
                SCMTSpeedPostAlertSpeedCurveKpH = SCMTCurrentSpeedPostSpeedLimitKpH + SCMTCurrentAlertSpeedKpH;
            }
            else
            {
                SCMTTrainLengthM = (float)Math.Ceiling((double)(TrainLengthM() / 100f)) * 100f;
                if (HeavyFreightTrain)
                    SCMTDelayBeforeBrakingEstablishedS = 12f + SCMTTrainLengthM / 200f;
                else
                    SCMTDelayBeforeBrakingEstablishedS = 2f + 2f * SCMTTrainLengthM * SCMTTrainLengthM * 0.00001f;
                var oldSCMTNextSignalSpeedLimitKpH = SCMTNextSignalSpeedLimitKpH;
                if (RSCState == RSCSTATE.Active)
                    SCMTNextSignalSpeedLimitKpH = RSCNextSignalAllowedSpeedKpH;
                else
                    SCMTNextSignalSpeedLimitKpH = Math.Min(150f, RSCNextSignalAllowedSpeedKpH);
                if (oldSCMTNextSignalSpeedLimitKpH == 0 && SCMTNextSignalSpeedLimitKpH != 0 && SCMTVelocitaRilascio == 10f)
                {
                    TriggerGenericSound(Event.GenericEvent7);
                    SCMTVelocitaRilascio = 30f;
                }
                    SCMTSignalTargetSpeedKpH = Math.Max(SCMTNextSignalSpeedLimitKpH == -3.6f ? SCMTTrainSpeedLimitKpH : SCMTNextSignalSpeedLimitKpH, SCMTVelocitaRilascio);
                var incIndex = 0;
                //if next normal signal is a virtual signal that restores line speed, it's not considered
                var nextNormalSignalMainHeadSignalType = NextGenericSignalFeatures("NORMAL", 0, float.MaxValue).MainHeadSignalTypeName;
                if (nextNormalSignalMainHeadSignalType == "riprvel" || nextNormalSignalMainHeadSignalType == "fsvirtfinelim")
                    incIndex = 1;
                SCMTSignalTargetDistanceM = NextSignalDistanceM(incIndex);
                if (SCMTNextSignalSpeedLimitKpH == 0) SCMTSignalTargetDistanceM = Math.Max(SCMTSignalTargetDistanceM - 200, 0);

                SCMTCurrentSignalSpeedLimitKpH = MpS.ToKpH(CurrentSignalSpeedLimitMpS());
                if (SCMTCurrentSignalSpeedLimitKpH == -3.6f) SCMTCurrentSignalSpeedLimitKpH = TrainSpeedLimitKpH;
                if (RSCState == RSCSTATE.Active)
                // depending from next code you have a max actual signal speed
                {
                    var maxAllowedSpeedfromRSCCodeKpH = SCMTCurrentSignalSpeedLimitKpH;
                    switch (RSCTempNewCode)
                    {
                        case RSCCODE.C270S:
                            maxAllowedSpeedfromRSCCodeKpH = RSCForceCod ? RSCMaxSignalSpeedCode270StarKpH : RSCMaxSignalSpeedCode270DoubleStarKpH;
                            break;
                        case RSCCODE.C270:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode270StarKpH;
                            break;
                        case RSCCODE.C180S:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode270KpH;
                            break;
                        case RSCCODE.C180:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode270KpH;
                            break;
                        case RSCCODE.C120S:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode180StarKpH;
                            break;
                        case RSCCODE.C120:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode180KpH;
                            break;
                        case RSCCODE.C75:
                            maxAllowedSpeedfromRSCCodeKpH = RSCMaxSignalSpeedCode180KpH;
                            break;

                        default:
                            break;
                    }
                    SCMTCurrentSignalSpeedLimitKpH = Math.Min(SCMTCurrentSignalSpeedLimitKpH, maxAllowedSpeedfromRSCCodeKpH);
                }

                // Update current speed limit when speed is below the target or when the train approaches the signal
                /*               if (NextSignalDistanceM(0) <= 5f)
                               {
                                   if (nextSignalSpeedLimitKpH > 0f && nextSignalSpeedLimitKpH < SCMTTrainSpeedLimitKpH)
                                       SCMTCurrentSignalSpeedLimitKpH = nextSignalSpeedLimitKpH;
                                   else
                                       SCMTCurrentSignalSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
                               }*/


                // Speed post speed limit preparation

                SCMTNextSpeedPostSpeedLimitKpH = (NextPostSpeedLimitMpS(0) > 0 ? MpS.ToKpH(NextPostSpeedLimitMpS(0)) : SCMTTrainSpeedLimitKpH);
                SCMTCurrentSpeedPostSpeedLimitKpH = MpS.ToKpH(CurrentPostSpeedLimitMpS());
                SCMTSpeedPostTargetSpeedKpH = SCMTNextSpeedPostSpeedLimitKpH;
                SCMTSpeedPostTargetDistanceM = NextPostDistanceM(0);


                SetNextSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH)));
//                SetCurrentSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH)));
//                SetCurrentSpeedLimitMpS(MpS.FromKpH(SCMTTrainSpeedLimitKpH));
                UpdateSCMTSpeedCurve(0, SCMTDelayBeforeEmergencyBrakingS);
                SetCurrentSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTSignalAlertSpeedCurveKpH, SCMTSpeedPostAlertSpeedCurveKpH) - SCMTCurrentAlertSpeedKpH));
                SetInterventionSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTSignalEmergencySpeedCurveKpH, SCMTSpeedPostEmergencySpeedCurveKpH)));
            }
        }

        protected void UpdateSCMTSpeedCurve( float delayForEmergencyCurve, float delayForWarningCurve)
        {
            SCMTSignalEmergencySpeedCurveKpH = 
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSignalTargetDistanceM,
                                MpS.FromKpH(SCMTSignalTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForEmergencyCurve,
                                SafeDecelerationMpS2
                            )),
                            SCMTSignalTargetSpeedKpH + SCMTNextEBSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH + SCMTCurrentEBSpeedKpH
                    ),
                    SCMTCurrentSignalSpeedLimitKpH + SCMTCurrentEBSpeedKpH
                );
            SCMTSignalAlertSpeedCurveKpH = 
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSignalTargetDistanceM,
                                MpS.FromKpH(SCMTSignalTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForWarningCurve,
                                SafeDecelerationMpS2
                            )),
                            SCMTSignalTargetSpeedKpH + SCMTNextAlertSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH + SCMTCurrentAlertSpeedKpH
                    ),
                    SCMTCurrentSignalSpeedLimitKpH + SCMTCurrentAlertSpeedKpH
                );
            SCMTSpeedPostEmergencySpeedCurveKpH = 
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSpeedPostTargetDistanceM,
                                MpS.FromKpH(SCMTSpeedPostTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForEmergencyCurve,
                                SafeDecelerationMpS2
                            )),
                            SCMTNextSpeedPostSpeedLimitKpH + SCMTNextEBSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH + SCMTCurrentEBSpeedKpH
                    ),
                    SCMTCurrentSpeedPostSpeedLimitKpH + SCMTCurrentEBSpeedKpH
                );
            SCMTSpeedPostAlertSpeedCurveKpH = 
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSpeedPostTargetDistanceM,
                                MpS.FromKpH(SCMTSpeedPostTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForWarningCurve,
                                SafeDecelerationMpS2
                            )),
                            SCMTNextSpeedPostSpeedLimitKpH + SCMTNextAlertSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH + SCMTCurrentAlertSpeedKpH
                    ),
                    SCMTCurrentSpeedPostSpeedLimitKpH + SCMTCurrentAlertSpeedKpH
                );

        }

        protected void UpdateSCMTAutotest()
        {
            switch (AutotestPhase)
            {
                case AUTOTESTPHASE.InitialWait:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.FirstBraking;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
                case AUTOTESTPHASE.FirstBraking:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SetFullBrake(false);
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.WaitFirstRelease;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                        SetFullBrake(true);
                    }
                    break;
                case AUTOTESTPHASE.WaitFirstRelease:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.SecondBraking;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
                case AUTOTESTPHASE.SecondBraking:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SetFullBrake(false);
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.WaitSecondRelease;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                        SetFullBrake(true);
                    }
                    break;
                case AUTOTESTPHASE.WaitSecondRelease:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.ThirdBraking;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
                case AUTOTESTPHASE.ThirdBraking:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SetFullBrake(false);
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.WaitThirdRelease;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                        SetFullBrake(true);
                    }
                    break;
                case AUTOTESTPHASE.WaitThirdRelease:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.FourthBraking;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
                case AUTOTESTPHASE.FourthBraking:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SetFullBrake(false);
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.WaitFourthRelease;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                        SetFullBrake(true);
                    }
                    break;
                case AUTOTESTPHASE.WaitFourthRelease:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        AutotestPhase = AUTOTESTPHASE.FinalWait;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
                case AUTOTESTPHASE.FinalWait:
                    if (SCMTAutotestTimer.Triggered)
                    {
                        SCMTAutotestTimer.Stop();
                        if (ActiveTCS == TCS.SCMT)
                            SCMTState = SCMTSTATE.Start;
                        else
                            ETCSL2State = ETCSL2STATE.Start;
                        AutotestPhase = AUTOTESTPHASE.FirstBraking;
                        SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[(int)AutotestPhase]);
                    }
                    else if (!SCMTAutotestTimer.Started)
                    {
                        SCMTAutotestTimer.Start();
                    }
                    break;
            }
        }
        
		protected void UpdateLuceBlu()
        {
            if (normalSignalDistanceM > 200)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Luce_blu_tacho) - 1, 1);
                nextNormalSignalAspectWasAtStop = false;
                return;
            }
            if (nextSignalAspect == Aspect.Stop)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Luce_blu_tacho) - 1, 2);
                nextNormalSignalAspectWasAtStop = true;
                return;
            }
            if (nextNormalSignalAspectWasAtStop && SCMTState != SCMTSTATE.Manovra && RSCState == RSCSTATE.Active && RSCCode != RSCCODE.AC)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Luce_blu_tacho) - 1, 1);
                return;
            }
            if (nextNormalSignalAspectWasAtStop)
            {
                SetCabDisplayControl((int)(TCSCommandEvent.Luce_blu_tacho) - 1, 2);
                return;
            }
            SetCabDisplayControl((int)(TCSCommandEvent.Luce_blu_tacho) - 1, 1);
        }
		
        protected void UpdateRSC()
        {
            for (int i = 0; i < 7; i++)
            {
                if ((TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Ric270S) + i] || AutopilotTimer.Triggered) && SCMTWarningTimer.Started)
                {
                    TriggerSoundWarning2();
                    TriggerSoundAlert2();
                    SCMTWarningTimer.Stop();
                    AutopilotTimer.Stop();
                    if (Autopilot) AutopilotButtonPressTimer.Start();
                    if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.WaitingRic)
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingRicEnd;
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_spente_lampeggio) - 1, 0);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_accese_lampeggio) - 1, 0);
                    TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Ric270S) + i] = false;
                    break;
                }
            }
            for (int i = 0; i < 7; i++)
            {
                if (TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Ric270S) + i] || AutopilotButtonPressTimer.Triggered)
                {
                    AutopilotButtonPressTimer.Stop();
                    TriggerSoundPenalty2();
                    TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Ric270S) + i] = false;
                    if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.WaitingRicEnd)
                    {
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingPre;
                        SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 1);
                    }
                    break;
                }
            }
            if (SCMTWarningTimer.Started && SCMTWarningTimer.Triggered)
            {
                SCMTEmergencyBraking = true;
                SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                SCMTWarningTimer.Stop();
            }
            if (RSCCode != RSCCODE.AC && RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.Active && RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.AckMissed)
            {
                if ((RSCFirstCodeReceived || RSCFirstRun || (int)RSCCode < (int)RSCPreviousCode) && RSCCode != RSCCODE.C75)
                {
                    // più permissivo
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
                    if (RSCCode != RSCCODE.None)
                        TriggerSoundInfo1();
                    if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.WaitingRic)
                    {
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingPre;
                        SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 1);
                    }
                }
                else if ((int)RSCCode > (int)RSCPreviousCode || (RSCCode == RSCCODE.C75 && RSCCode != RSCPreviousCode))
                {
                    // più restrittivo
                    TriggerSoundWarning1();
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
                    SCMTWarningTimer.Start();
                    if (Autopilot && !AutopilotTimer.Started) AutopilotTimer.Start();
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_spente_lampeggio) - 1, 11 - (int)RSCCode);
                    SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese_accese_lampeggio) - 1, 11 - (int)RSCCode);
                }
                RSCFirstCodeReceived = false;
            }
            else if (RSCCode == RSCCODE.AC && RSCState == RSCSTATE.Active)
                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
        }

        protected void UpdateAttivazioneSR()
        {
            switch (AttivazioneSR)
            {
                case ATTIVAZIONESR.Inactive:
                    if (SpeedKpH > 30f)
                        return;
                    if (TCSButtonPressed[(int)(TCSCommandEvent.Soppr)])
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AttivazioneSR = ATTIVAZIONESR.WaitingConfirmation;
                        SRTimer.Stop();
                        if (!SRTimer.Started)
                        {
                            SRTimer.Setup(SRTimerDelayS[(int)AttivazioneSR]);
                            SRTimer.Start();
                        }
                        TCSButtonPressed[(int)(TCSCommandEvent.Soppr)] = false;
                    }
                    break;
                case ATTIVAZIONESR.WaitingConfirmation:
                    if (SpeedKpH > 30f)
                    {
                        AttivazioneSR = ATTIVAZIONESR.Inactive;
                        SRTimer.Stop();
                        return;
                    }
                    if (SRTimer.Triggered)
                    {
                        SRTimer.Stop();
                        AttivazioneSR = ATTIVAZIONESR.Inactive;
                        return;
                    }
                    if (TCSButtonPressed[(int)(TCSCommandEvent.SopprConfirmation)])
                    {
                        TriggerGenericSound(Event.GenericEvent2);
                        AttivazioneSR = ATTIVAZIONESR.InternalChecks;
                        SRTimer.Stop();
                        if (!SRTimer.Started)
                        {
                            SRTimer.Setup(SRTimerDelayS[(int)AttivazioneSR]);
                            SRTimer.Start();
                        }
                        TCSButtonPressed[(int)(TCSCommandEvent.SopprConfirmation)] = false;
                    }
                    break;
                case ATTIVAZIONESR.InternalChecks:
                    if (SRTimer.Triggered)
                    {
                        SRTimer.Stop();
                        AttivazioneSR = ATTIVAZIONESR.Active;
                        if (!SRTimer.Started)
                        {
                            SRTimer.Setup(SRTimerDelayS[(int)AttivazioneSR]);
                            SRTimer.Start();
                        }
                    }
                    break;
                case ATTIVAZIONESR.Active:
                    if (SRTimer.Triggered)
                    {
                        SRTimer.Stop();
                        AttivazioneSR = ATTIVAZIONESR.Inactive;
                    }
                    else if (normalSignalPassed && (previousSignalAspect == Aspect.Stop || previousSignalAspect == Aspect.Permission))
                    {
                        SRTimer.Stop();
                        SRTimer.Setup(SRTimerDelayS[(int)AttivazioneSR]);
                        SRTimer.Start();
                        AttivazioneSR = ATTIVAZIONESR.SignalPassed;
                        if (previousSignalAspect == Aspect.Stop)
                        {
                            RequestToggleManualMode();
                            RSCCode = RSCCODE.AC;
                        }
                    }
                    else
                        SetCabDisplayControl((int)(TCSCommandEvent.Soppr) - 1, 1);
                    break;
                case ATTIVAZIONESR.SignalPassed:
                    if (SRTimer.Triggered)
                    {
                        SRTimer.Stop();
                        AttivazioneSR = ATTIVAZIONESR.Inactive;
                        ControlMode = GetControlMode();
                        if (ControlMode != TRAIN_CONTROL.EXPLORER)
                            RequestToggleManualMode();
                        CheckACFinita(600);
        //                RSCCode = RSCCODE.None;
                    }
                    break;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Soppr)])
            {
                TCSButtonReleased[(int)(TCSCommandEvent.Soppr)] = false;
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.SopprConfirmation)])
            {
                TCSButtonReleased[(int)(TCSCommandEvent.SopprConfirmation)] = false;
            }
        }

        protected void CheckSPAD()
        {
            if (normalSignalPassed && (previousSignalAspect == Aspect.Stop || previousSignalAspect == Aspect.Permission) &&
                AttivazioneSR != ATTIVAZIONESR.SignalPassed)
            {
                SCMTSpad = true;
                TriggerSoundWarning1();
                SCMTEmergencyBraking = true;
                RSCCode = RSCCODE.AC;
                RSCCodeConfirmationTimer.Stop();
                RequestToggleManualMode();
                SetCabDisplayControl((int)(TCSCommandEvent.SR) - 1, 1);
                SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
            }
        }

        protected void UpdatePrericonoscimento()
        {
            switch (RSCPrericonoscimentoState)
            {
                case PRERICONOSCIMENTOSTATE.WaitingPre:
                    if (prericSignalPassed && RSCCode != RSCCODE.AC)
                    {
                        if (!RSCPrericonoscimento1Timer.Started)
                            RSCPrericonoscimento1Timer.Start();
                    }
                    if (TCSButtonPressed[(int)(TCSCommandEvent.Pulsante_Pre)] || Autopilot && normalSignalDistanceM / SpeedMpS() < 5)
                    {
                        TriggerSoundWarning2();
                        SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 0);
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Timer12secOn;
                        if (Autopilot) AutopilotButtonPressTimer.Start();
                        if (normalSignalDistanceM > 100) RSCPrericonoscimentoTimer.Start();
                        if (RSCPrericonoscimento1Timer.Started)
                            RSCPrericonoscimento1Timer.Stop();
                    }
                    if (RSCPrericonoscimento1Timer.Triggered)
                    {
                        RSCPrericonoscimento1Timer.Stop();
                        TriggerSoundWarning1();
                        SCMTEmergencyBraking = true;
                        RSCMinimumLengthM = Math.Max(GetTrackNodeOffset(), RSCMinimumLengthWithoutSwitchesM);
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.AckMissed;
                    }
                    break;
                case PRERICONOSCIMENTOSTATE.Timer12secOn:
                    {
                        if (TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Pre)])
                        {
                            TriggerSoundPenalty2();
                            TCSButtonReleased[(int)(TCSCommandEvent.Pulsante_Pre)] = false;
                        }
                        if (AutopilotButtonPressTimer.Triggered)
                        {
                            AutopilotButtonPressTimer.Stop();
                            TriggerSoundPenalty2();
                        }
                        if (prericSignalPassed && RSCCode != RSCCODE.AC)
                        {
                            RSCPrericonoscimentoTimer.Stop();
                            RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.PreActive;
                            RSCMinimumLengthM = Math.Max(GetTrackNodeOffset(), RSCMinimumLengthWithoutSwitchesM);
                            RSCPrericonoscimento1Timer.Start();
                        }
                        if (RSCPrericonoscimentoTimer.Triggered)
                        {
                            RSCPrericonoscimentoTimer.Stop();
                            TriggerSoundWarning1();
                            SCMTEmergencyBraking = true;
                            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                            RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Timer12secExpired;
                            //                           SetCabDisplayControl((int)(TCSCommandEvent.Pulsante_Pre) - 1, 1);
                            //                           RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingPre;
                        }
                    }
                    break;
                case PRERICONOSCIMENTOSTATE.PreActive:
                    if (RSCPrericonoscimento1Timer.Triggered)
                    {
                        RSCPrericonoscimento1Timer.Stop();
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Active;
                    }
                    break;
                case PRERICONOSCIMENTOSTATE.Active:
                    if (RSCCode != RSCCODE.AC)
                    {
                        TriggerSoundInfo1();
                        RSCCode = RSCCODE.AC;
                        RSCPresent = false;
                        RSCForceCod9 = false;
                        RSCForceCod = false;
                        SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
                    }
                    break;
                case PRERICONOSCIMENTOSTATE.AckMissed:
                    if (RSCCode != RSCCODE.AC)
                    {
                        RSCPresent = false;
                        RSCCode = RSCCODE.AC;
                        RSCForceCod9 = false;
                        RSCForceCod = false;
                        SetCabDisplayControl((int)(TCSCommandEvent.Ripetizioni_estese) - 1, ActiveTCS == TCS.SCMT ? 11 - (int)RSCCode : 0);
                    }
                    break;
                case PRERICONOSCIMENTOSTATE.Timer12secExpired:
                    if (prericSignalPassed && RSCCode != RSCCODE.AC)
                    {
                        RSCPrericonoscimentoTimer.Stop();
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.PreActive;
                        RSCMinimumLengthM = Math.Max(GetTrackNodeOffset(), RSCMinimumLengthWithoutSwitchesM);
                        RSCPrericonoscimento1Timer.Start();
                    }
                    break;
                default:
                    break;
            }
        }

        public override void HandleEvent(TCSEvent evt, string message)
        {
            if (message == String.Empty)
            {
                switch (evt)
                {
                    case TCSEvent.AlerterPressed:
                        ResetButtonPressed = true;
                        break;

                    case TCSEvent.AlerterReleased:
                        ResetButtonPressed = false;
                        break;
                    case TCSEvent.CircuitBreakerClosed:
                        SSBState = SSBSTATE.ReadyToOn;
                        CircuitBreakerOpened = false;
                        CircuitBreakerOpen = false;
                        break;
                    case TCSEvent.CircuitBreakerOpen:
                        SCMTState = SCMTSTATE.Off;
                        RSCState = RSCSTATE.Inactive;
                        SSBState = SSBSTATE.Off;
                        CircuitBreakerOpened = true;
                        CircuitBreakerOpen = true;
                        break;
                 }
            }
            else
            {
                var commandEvent = TCSCommandEvent.None;
                var messageIndex = 0;
                if (Int32.TryParse(message, out messageIndex))
                {
                    commandEvent = (TCSCommandEvent)(messageIndex + 1);
                    switch (evt)
                    {
                        case TCSEvent.GenericTCSButtonPressed:
                            TCSButtonPressed[(int)commandEvent] = true;
                            break;
                        case TCSEvent.GenericTCSButtonReleased:
                            TCSButtonPressed[(int)commandEvent] = false;
                            TCSButtonReleased[(int)commandEvent] = true;
                            break;
                    }
                }
            }
        }

        public override void Save (BinaryWriter outf)
        {
            outf.Write((int)TCSTransitionState);
            outf.Write((int)ActiveTCS);
            outf.Write((int)SSBState);
            outf.Write((int)SCMTState);
            outf.Write((int)RSCState);
            outf.Write((int)RSCCode);
            outf.Write((int)RSCPrericonoscimentoState);
            outf.Write((int)AttivazioneSR);
            outf.Write(RSCPresent);
            outf.Write(NoRSCZone);
            outf.Write(RSCFirstRun);
            outf.Write(RSCForceCod);
            outf.Write(RSCForceCod9);
            outf.Write(RSCForceNoCod);
            outf.Write((int)ETCSL2State);
            outf.Write(RadioLinkState);
            outf.Write(normalSignalDistanceM);
            outf.Write((int)POCState);
            outf.Write((int)PowerSupplySwitchState);
			outf.Write(SCMTVelocitaRilascio);
        }

        public override void Restore (BinaryReader inf)
        {
            TCSTransitionState = (TCSTRANSITIONSTATE)(inf.ReadInt32());
            ActiveTCS = (TCS)(inf.ReadInt32());
            SSBState = (SSBSTATE)(inf.ReadInt32());
            SCMTState = (SCMTSTATE)(inf.ReadInt32());
            RSCState = (RSCSTATE)(inf.ReadInt32());
            RSCCode = (RSCCODE)(inf.ReadInt32());
            RSCPrericonoscimentoState = (PRERICONOSCIMENTOSTATE)(inf.ReadInt32());
            AttivazioneSR = (ATTIVAZIONESR)(inf.ReadInt32());
            RSCPresent = inf.ReadBoolean();
            NoRSCZone = inf.ReadBoolean();
            RSCFirstRun = inf.ReadBoolean();
            RSCForceCod = inf.ReadBoolean();
            RSCForceCod9 = inf.ReadBoolean();
            RSCForceNoCod = inf.ReadBoolean();
            ETCSL2State = (ETCSL2STATE)(inf.ReadInt32());
            RadioLinkState = inf.ReadBoolean();
            normalSignalDistanceM = inf.ReadSingle();
            POCState = (POCSTATE)(inf.ReadInt32());
            PowerSupplySwitchState = (POWERSUPPLYTYPE)(inf.ReadInt32());
			SCMTVelocitaRilascio = inf.ReadSingle();
            ColdStart = false;
        }

        protected void UpdateSignal()
        {
            GetSignalData();

            normalSignalPassed = false;
            distanceSignalPassed = false;
            prericSignalPassed = false;
            infoSignalPassed = false;
            RSCPIPassed = false;
            PreviousDistanceSignalDistanceM = distanceSignalDistanceM;
            distanceSignalDistanceM = NextDistanceSignalDistanceM();
            PreviousSignalDistanceM = normalSignalDistanceM;
            var incIndex = 0;
            //if next normal signal is a virtual signal that restores line speed, it's not considered
            var nextNormalSignalMainHeadSignalType = NextGenericSignalFeatures("NORMAL", 0, float.MaxValue).MainHeadSignalTypeName;
            if (nextNormalSignalMainHeadSignalType == "riprvel" || nextNormalSignalMainHeadSignalType == "fsvirtfinelim")
                incIndex = 1;
            normalSignalDistanceM = NextSignalDistanceM(incIndex);
            PreviousInfoSignalDistanceM = infoSignalDistanceM;
            switch (infoSignalMainHeadSignalType)
            {
                case "startcod":
                case "prescod":
                case "startcod9":
                case "prescod9":
                case "endcod":
                case "asscod":
                case "avvstartertms":
                case "startertms":
                case "avvendertms":
                case "endertms":
                case "avvstartneutro":
                case "startneutro":
                case "endneutro":
                case "avvpoc3-25":
                case "avvpoc25-3":
                case "startabbpanto":
                case "endabbpanto":
				case "approccio10":
                    PreviousInfoSignalMainHeadSignalType = infoSignalMainHeadSignalType;
                    break;
                default:
                    break;
            }
            infoSignalDistanceM = NextGenericSignalDistanceM("INFO");
            infoSignalMainHeadSignalType = NextGenericSignalMainHeadSignalType("INFO");
            if (normalSignalDistanceM > 0 && normalSignalDistanceM > PreviousSignalDistanceM + 1.0f && SpeedMpS() > 0.1f)
            {
                normalSignalPassed = true;
                distanceSignalPassed = true;
            }
            if ((infoSignalDistanceM > PreviousInfoSignalDistanceM + 1.0f || (infoSignalMainHeadSignalType != PreviousInfoSignalMainHeadSignalType &&
                   PreviousInfoSignalMainHeadSignalType != "")) && SpeedMpS() > 0)
            {
                infoSignalPassed = true;
            }

            if (ActiveTCS == TCS.SCMT)
            {

                if (normalSignalDistanceM > 0)
                {
                    if (normalSignalDistanceM > PreviousSignalDistanceM + 1.0f && SpeedMpS() > 0)
                    {
                        if (normalSignalDistanceM != float.MaxValue && AutodetectSignalFilesType)
                        {
                            SignalFilesType = DoesNextNormalSignalHaveRepeaterHead() ? SIGNALFILESTYPE.SerriniBaldi : SIGNALFILESTYPE.PriviteraBono;
                        }
                        normalSignalPassed = true;
						if (PreviousInfoSignalMainHeadSignalType == "approccio10")
                        PreviousInfoSignalMainHeadSignalType = "";
                        if (SCMTVelocitaRilascio != 30f)
                        TriggerGenericSound(Event.GenericEvent7);
                        SCMTVelocitaRilascio = 30f;
                        prericSignalPassed = true;
                        distanceSignalPassed = true;
                        if (RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.Invalid && RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.Active &&
                            RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.AckMissed)
                        // Check if there is another signal in between
                        {
                            // cerca primo scambio dopo segnale successivo
                            if (normalSignalDistanceM != float.MaxValue)
                            {
                                nextDivergingSwitchDistanceM = NextDivergingSwitchDistanceM(normalSignalDistanceM + RSCNextDivergingSwitchMaxDistanceM);
                                if (nextDivergingSwitchDistanceM > normalSignalDistanceM && GetTrackNodeOffset() > normalSignalDistanceM) prericSignalPassed = false;
                            }
                        }
                        RSCEndsThisSection = false;
                        RSCEndsNextSection = false;
                        RSCEndsNextNextSection = false;
                        RSCEndsNextNextNextSection = false;
                        if (RSCState == RSCSTATE.Active) TestForRSCEnd();
                    }
                }


                if (infoSignalPassed)
                {
                    // check if it is a signal which is interesting for us
                    switch (PreviousInfoSignalMainHeadSignalType)
                    {
                        case "startcod":
                        case "prescod":
                            RSCForceCod = true;
                            RSCForceCod9 = false;
                            RSCForceNoCod = false;
                            RSCPIPassed = true;
                            PreviousInfoSignalMainHeadSignalType = "";
                            break;
                        case "startcod9":
                        case "prescod9":
                            RSCForceCod = false;
                            RSCForceCod9 = true;
                            RSCForceNoCod = false;
                            RSCPIPassed = true;
                            PreviousInfoSignalMainHeadSignalType = "";
                            break;
                        case "endcod":
                        case "asscod":
                            RSCForceCod = false;
                            RSCForceCod9 = false;
                            RSCForceNoCod = true;
                            PreviousInfoSignalMainHeadSignalType = "";
                            break;
						case "approccio10":
                        if (SCMTVelocitaRilascio != 10f && SCMTNextSignalSpeedLimitKpH == 0)
                            TriggerGenericSound(Event.GenericEvent6);
                        if (SCMTNextSignalSpeedLimitKpH == 0)
                            SCMTVelocitaRilascio = 10f;
                        break;
                        default:
                            break;
                    }
                }

                OldRemainingLengthM = RemainingLengthM;
                RemainingLengthM = GetTrackNodeOffset();
                // presenza RSC testata per lunghezza senza scambi
                if (RemainingLengthM >= RSCMinimumLengthWithoutSwitchesM && RemainingLengthM > OldRemainingLengthM && SpeedKpH > 0.1f && !RSCForceNoCod
                    && infoSignalMainHeadSignalType != "startcod" && infoSignalMainHeadSignalType != "startcod9" && 
                    !(InfoSignals[0].MainHeadSignalTypeName == "asscod" && InfoSignals[0].DistanceM < 400))
                {
                    RSCPIPassed = true;
                    NoRSCZone = false;
                    IntervalDistanceM = 0f;
                }
                // presenza RSC segnalata dopo passaggio segnale
                else if (RSCPresent && (normalSignalPassed || distanceSignalPassed) && SpeedKpH > 0.1f && !RSCForceNoCod)
                    RSCPIPassed = true;

                // presenza RSC testata dopo passaggio segnale per lunghezza senza scambi in rientro da deviata
                else if (!NoRSCZone && !RSCPresent && (normalSignalPassed || distanceSignalPassed) && SpeedKpH > 0.1f && !RSCForceNoCod)
                {
                    CheckRSCPresent();
                    if (RSCPresent)
                        RSCPIPassed = true;
                }
                // presenza RSC segnalata dopo x metri in tratta lunga senza scambi
                else if (RemainingLengthM >= RSCMinimumLengthWithoutSwitchesM && SpeedKpH > 0.1f && !RSCForceNoCod)
                {
                    IntervalDistanceM += OldRemainingLengthM - RemainingLengthM;
                    if (IntervalDistanceM > RSCMinimumLengthWithoutSwitchesM)
                    {
                        IntervalDistanceM = 0f;
                        if (RSCPresent)
                            RSCPIPassed = true;
                    }
                }

                if ((RSCForceCod9 || RSCForceCod) && RSCState != RSCSTATE.Active && normalSignalPassed)
                {
                    // Check wheter there are no other signals up to next diverging or converging switch
                    // and in such case set the RSC code as AC
                    if (NextDivergingSwitchDistanceM(350) < normalSignalDistanceM || NextTrailingDivergingSwitchDistanceM(350) < normalSignalDistanceM)
                    {
                        RSCPIPassed = true;
                        RSCForceCod9 = false;
                        RSCForceCod = false;
                        // Behaves like when entering AC zone
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.PreActive;
                        if (!RSCPrericonoscimento1Timer.Started) RSCPrericonoscimento1Timer.Start();
                    }
                }

                if (RSCState == RSCSTATE.Active)
                    UpdatePrericonoscimento();
                UpdateParams();
            }

            if (distanceSignalDistanceM != 0 && distanceSignalDistanceM > PreviousDistanceSignalDistanceM + 1.0f && SpeedMpS() > 0)
            {
                distanceSignalPassed = true;
            }
         }

        protected void TestForRSCEnd()
        {
            if (TrueNormalSignals[0].DistanceM == float.MaxValue || TrueNormalSignals[0].DistanceM < 0 || CurrentTrainMUDirection() == Direction.Reverse)
            {
                RSCEndsThisSection = true;
                return;
            }
            var maxTestingDistanceM = TrueNormalSignals[3].DistanceM;
            if (TrueNormalSignals[1].DistanceM == float.MaxValue || TrueNormalSignals[1].DistanceM < 0 || CurrentTrainMUDirection() == Direction.Reverse)
            {
                RSCEndsNextSection = true;
                maxTestingDistanceM = TrueNormalSignals[0].DistanceM;
            }
            else if (TrueNormalSignals[2].DistanceM == float.MaxValue || TrueNormalSignals[2].DistanceM < 0 || CurrentTrainMUDirection() == Direction.Reverse)
            {
 //               RSCEndsNextNextSection = true;
                maxTestingDistanceM = TrueNormalSignals[1].DistanceM;
            }

            var isig = 0;
            do
            {
                genericSignalFeatures = NextGenericSignalFeatures("INFO", isig, maxTestingDistanceM);
                if (genericSignalFeatures.MainHeadSignalTypeName == "endcod")
                {
                    if (genericSignalFeatures.DistanceM < TrueNormalSignals[0].DistanceM + 0.5f)
                        RSCEndsThisSection = true;
                    else if (genericSignalFeatures.DistanceM < TrueNormalSignals[1].DistanceM + 0.5f) RSCEndsNextSection = true;
                    else RSCEndsNextNextSection = true;
                    return;
                }
                isig++;
            }
            while (genericSignalFeatures.DistanceM < float.MaxValue);
            return;
        }

        protected void UpdateParams()
        {

            //if next normal signal is a virtual signal that restores line speed, it's not considered

            for (int ind = 0; ind <= 2; ind++)
                incIndex[ind] = 0;

            if (CurrentTrainMUDirection() != Direction.Reverse)
            {
                // discard fake normal signals
                var validNormalSignalsFound = 0;
                var signalMainHeadSignalType = "";
                var isig = 0;
                do
                {
                    signalMainHeadSignalType = NextGenericSignalFeatures("NORMAL", isig, float.MaxValue).MainHeadSignalTypeName;
                    if (signalMainHeadSignalType == "riprvel" || signalMainHeadSignalType == "fsvirtfinelim")
                        incIndex[validNormalSignalsFound]++;
                    else validNormalSignalsFound++;
                    isig++;
                }
                while (signalMainHeadSignalType != "" && validNormalSignalsFound < RequiredNormalSignals);
                for (int ind = 0; ind <= 1; ind++)
                    incIndex[ind + 1] += incIndex[ind];
            }

            nextSignalSpeedLimitKpH = (float)Math.Round(MpS.ToKpH(NextSignalSpeedLimitMpS(incIndex[0])), 0);
            previousSignalAspect = nextSignalAspect;
            nextSignalAspect = NextSignalAspect(incIndex[0]);
            var nextSignalAspect1 = NextSignalAspect(1 + incIndex[1]);
            var nextSignalSpeedLimit1MpS = NextSignalSpeedLimitMpS(1 + incIndex[1]);
            if (RSCCode == RSCCODE.AC && RSCPrericonoscimentoState != PRERICONOSCIMENTOSTATE.AckMissed)
            {
                if (normalSignalPassed)
                    RSCMinimumLengthM = 600f;
                CheckACFinita(RSCMinimumLengthM);
            }
            if (RSCCode != RSCCODE.AC && !SCMTSpad && ((SCMTState == SCMTSTATE.Active || SCMTState == SCMTSTATE.Predisposizione) && (normalSignalPassed || distanceSignalPassed)
                || SCMTState == SCMTSTATE.ActiveAndRSC || RSCState == RSCSTATE.Active || SCMTState == SCMTSTATE.Active && normalSignalDistanceM > 4000))
            {
                RSCNextSignalAllowedSpeedKpH = (nextSignalSpeedLimitKpH == -3.6f || nextSignalSpeedLimitKpH == -4f) ? MpS.ToKpH(LineSpeedMpS()) : nextSignalSpeedLimitKpH;
                currentPostSpeedLimitKpH = MpS.ToKpH(CurrentPostSpeedLimitMpS());
                RSCPreviousCode = RSCCode;
                RSCTempNewCode = RSCCODE.None;
                switch (SignalFilesType)
                {
                    case SIGNALFILESTYPE.SerriniBaldi:
                        {
                            switch (nextSignalAspect)
                            {
                                case Aspect.Stop:
                                    RSCNextSignalAllowedSpeedKpH = 0;
                                    RSCTempNewCode = RSCCODE.C75;
                                    break;
                                case Aspect.Clear_2:
                                    //                                    if ((nextSignalAspect1 != Aspect.Clear_2 && !(nextSignalAspect1 == Aspect.Approach_1 && nextSignalSpeedLimit1MpS == -1) &&
                                    //                                       !(nextSignalAspect1 == Aspect.Restricted && MpS.ToKpH(nextSignalSpeedLimit1MpS) != 30)) || NextSignalDistanceM(1) == float.MaxValue)
                                    if (NextSignalDistanceM(1 + incIndex[1]) == float.MaxValue || RSCEndsThisSection)
                                    {
                                        if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9 && !RSCForceCod) || HeavyFreightTrain || RSCForceCod || RSCEndsThisSection)
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180KpH;
                                            RSCTempNewCode = RSCCODE.C180;
                                        }
                                        else
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180StarKpH;
                                            RSCTempNewCode = RSCCODE.C180S;
                                        }
                                    }
                                    else if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9) || RSCForceCod || nextSignalAspect1 != Aspect.Clear_2
                                        || NextSignalDistanceM(1 + incIndex[1]) == float.MaxValue
                                        || NextPostDistanceM(0) < 1500 && NextPostSpeedLimitMpS(0) <= 130 || DoesStartFromTerminalStation() || HeavyFreightTrain
                                        || RSCEndsNextSection)// Un modo per selezionare se i codici 270* e 270** esistono in quel punto
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270KpH;
                                        RSCTempNewCode = RSCCODE.C270;
                                    }
                                    else if (currentPostSpeedLimitKpH <= 205 || NextSignalAspect(2 + incIndex[2]) != Aspect.Clear_2
                                        || NextSignalDistanceM(2 + incIndex[2]) == float.MaxValue || RSCEndsNextNextSection)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270StarKpH;
                                        RSCTempNewCode = RSCCODE.C270S;
                                    }
                                    else
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270DoubleStarKpH;
                                        RSCTempNewCode = RSCCODE.C270SS;
                                    }
                                    break;
                                case Aspect.Clear_1:
                                    if (HeavyFreightTrain || (currentPostSpeedLimitKpH <= 130 && !RSCForceCod9) || RSCForceCod)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 100;
                                        RSCTempNewCode = RSCCODE.C120;
                                    }
                                    else
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 100;
                                        RSCTempNewCode = RSCCODE.C120S;
                                    }
                                    Prericonoscimento();
                                    break;
                                case Aspect.Approach_3:
                                    if (nextSignalSpeedLimitKpH != 30)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 60f;
                                        RSCTempNewCode = RSCCODE.C120;
                                        Prericonoscimento();
                                    }
                                    break;
                                case Aspect.Approach_2:
                                    RSCTempNewCode = RSCCODE.C120;
                                    if (nextSignalSpeedLimitKpH == 30)
                                        RSCNextSignalAllowedSpeedKpH = 30f;
                                    else if (nextSignalSpeedLimitKpH == 60)
                                        RSCNextSignalAllowedSpeedKpH = 60f;
                                    else if (nextSignalSpeedLimitKpH == 0)
                                        RSCNextSignalAllowedSpeedKpH = 0f;
                                    Prericonoscimento();
                                    break;
                                case Aspect.Approach_1:
                                    if (nextSignalSpeedLimitKpH == 30)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 30f;
                                        RSCTempNewCode = RSCCODE.C120;
                                        Prericonoscimento();
                                    }
                                    else
                                    {
                                        if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9) || HeavyFreightTrain || nextSignalAspect1 == Aspect.Approach_3 ||
                                            nextSignalAspect1 == Aspect.Approach_2 || nextSignalAspect1 == Aspect.Stop)
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180KpH;
                                            RSCTempNewCode = RSCCODE.C180;
                                        }
                                        else
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180StarKpH;
                                            RSCTempNewCode = RSCCODE.C180S;
                                        }
                                    }
                                    break;
                                case Aspect.Restricted:
                                    RSCTempNewCode = RSCCODE.C180;
                                    if (nextSignalSpeedLimitKpH == -1)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180KpH;
                                    }
                                    else if (nextSignalSpeedLimitKpH == 30)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = nextSignalSpeedLimitKpH;
                                    }
                                    else
                                        RSCNextSignalAllowedSpeedKpH = nextSignalSpeedLimitKpH;
                                    break;
                                case Aspect.StopAndProceed:
                                    RSCTempNewCode = RSCCODE.C75;
                                    break;
                                default:
                                    break;
                            }
                            break;
                        }
                    case SIGNALFILESTYPE.PriviteraBono:
                        {
                            switch (nextSignalAspect)
                            {
                                case Aspect.Stop:
                                    RSCNextSignalAllowedSpeedKpH = 0;
                                    RSCTempNewCode = RSCCODE.C75;
                                    break;
                                case Aspect.Clear_2:
                                    if (NextSignalDistanceM(1 + incIndex[1]) == float.MaxValue || RSCEndsThisSection)
                                    {
                                        if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9 && !RSCForceCod) || HeavyFreightTrain || RSCEndsThisSection || RSCForceCod)
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180KpH;
                                            RSCTempNewCode = RSCCODE.C180;
                                        }
                                        else
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180StarKpH;
                                            RSCTempNewCode = RSCCODE.C180S;
                                        }
                                    }
                                    else if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9) || NextSignalAspect(1 + incIndex[1]) != Aspect.Clear_2
                                        || NextSignalDistanceM(1 + incIndex[1]) == float.MaxValue
                                        || (NextPostDistanceM(0) < 1500 && NextPostSpeedLimitMpS(0) <= 130 && !RSCForceCod9) || RSCForceCod ||
                                        DoesStartFromTerminalStation() || HeavyFreightTrain || RSCEndsNextSection)// Un modo per selezionare se i codici 270* e 270* esistono in quel punto
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270KpH;
                                        RSCTempNewCode = RSCCODE.C270;
                                    }
                                    else if (currentPostSpeedLimitKpH <= 205 || NextSignalAspect(2 + incIndex[2]) != Aspect.Clear_2
                                        || NextSignalDistanceM(2 + incIndex[2]) == float.MaxValue || RSCEndsNextNextSection)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270StarKpH;
                                        RSCTempNewCode = RSCCODE.C270S;
                                    }
                                    else
                                    {
                                        RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode270DoubleStarKpH;
                                        RSCTempNewCode = RSCCODE.C270SS;
                                    }
                                    break;
                                case Aspect.Clear_1:
                                    if (HeavyFreightTrain || (currentPostSpeedLimitKpH <= 130 && !RSCForceCod9) || RSCForceCod)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 100;
                                        RSCTempNewCode = RSCCODE.C120;
                                    }
                                    else
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 100;
                                        RSCTempNewCode = RSCCODE.C120S;
                                    }
                                    Prericonoscimento();
                                    break;
                                case Aspect.Approach_3:
                                    RSCTempNewCode = RSCCODE.C120;
                                    if (nextSignalSpeedLimitKpH == 30)
                                        RSCNextSignalAllowedSpeedKpH = 30f;
                                    Prericonoscimento();
                                    break;
                                case Aspect.Approach_2:
                                    if (nextSignalSpeedLimitKpH == 60)
                                        RSCNextSignalAllowedSpeedKpH = 60f;
                                    RSCTempNewCode = RSCCODE.C120;
                                    Prericonoscimento();
                                    break;
                                case Aspect.Approach_1:
                                    RSCTempNewCode = RSCCODE.C120;
                                    if (nextSignalSpeedLimitKpH == 30)
                                        RSCNextSignalAllowedSpeedKpH = 30f;
                                    Prericonoscimento();
                                    break;
                                case Aspect.Restricted:
                                    if (nextSignalSpeedLimitKpH == 30)
                                    {
                                        RSCNextSignalAllowedSpeedKpH = 30f;
                                        RSCTempNewCode = RSCCODE.C75;
                                        //                                        Prericonoscimento();
                                    }
                                    else
                                    {
                                        if ((currentPostSpeedLimitKpH <= 180 && !RSCForceCod9) || HeavyFreightTrain || RSCForceCod ||
                                            nextSignalAspect1 == Aspect.Approach_2 || nextSignalAspect1 == Aspect.Approach_1 || nextSignalAspect1 == Aspect.Stop)
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180KpH;
                                            RSCTempNewCode = RSCCODE.C180;
                                        }
                                        else
                                        {
                                            RSCNextSignalAllowedSpeedKpH = RSCMaxSignalSpeedCode180StarKpH;
                                            RSCTempNewCode = RSCCODE.C180S;
                                        }
                                    }
                                    break;
                                case Aspect.StopAndProceed:
                                    RSCTempNewCode = RSCCODE.C75;
                                    break;
                                default:
                                    break;
                            }
                            break;
                        }
                }
                if (RSCState != RSCSTATE.Active) RSCTempNewCode = RSCCODE.None;
                if (RSCTempNewCode != RSCCode && !RSCCodeConfirmationTimer.Started)
                    RSCCodeConfirmationTimer.Start();
                if (RSCCodeConfirmationTimer.Triggered)
                {
                    RSCCodeConfirmationTimer.Stop();
                    RSCCode = RSCTempNewCode;
                }
            }
        }

        protected void Prericonoscimento()
        {
            if (RSCState == RSCSTATE.Active)
            {
                if (RSCPrericonoscimentoState == PRERICONOSCIMENTOSTATE.Invalid)
                {
                    // cerca primo scambio dopo segnale successivo
                    if (normalSignalDistanceM == float.MaxValue) return;
                    nextDivergingSwitchDistanceM = NextDivergingSwitchDistanceM(normalSignalDistanceM + RSCNextDivergingSwitchMaxDistanceM);
                    if (nextDivergingSwitchDistanceM < normalSignalDistanceM) return;
                    else if (GetTrackNodeOffset() != float.MaxValue)
                    {
                        // to be improved
                        RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingRic;
                    }
                    // setta flag intero prenotazione prericonoscimento
                    else RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.WaitingRic;
                }
            }
        }

        protected void CheckACFinita(float rSCMinimumLengthM)
        {
            if (RSCCheckACFinitaTimer.Triggered)
            {
                RSCPrericonoscimentoState = PRERICONOSCIMENTOSTATE.Invalid;
                RSCFirstCodeReceived = true;
                if (!NoRSCZone) RSCPresent = true;
                RSCCode = RSCCODE.None;
                RSCCheckACFinitaTimer.Stop();
                return;
            }
            if (GetTrackNodeOffset() >= rSCMinimumLengthM ||
                ((NextDivergingSwitchDistanceM(1000) > normalSignalDistanceM || NextTrailingDivergingSwitchDistanceM(1000) > normalSignalDistanceM) &&
                (RSCForceCod || RSCForceCod9)))
            {
                if (!RSCCheckACFinitaTimer.Started)
                    RSCCheckACFinitaTimer.Start();
            }
        }

        protected void GetSignalData()
        {
            for (int i = 0; i < TrueNormalSignals.Length; i++)
            {
                TrueNormalSignals[i].DistanceM = float.MaxValue;
                TrueNormalSignals[i].MainHeadSignalTypeName = "";
            }
            var k = 0;
            for (int i = 0; i < NormalSignals.Length; i++)
            {
                NormalSignals[i] = NextGenericSignalFeatures("NORMAL", i, 12000.0f);
                var signalTypeName = NormalSignals[i].MainHeadSignalTypeName.ToLower();
                if (signalTypeName != "riprvel" && signalTypeName != "fsvirtfinelim")
                {
                    TrueNormalSignals[k].MainHeadSignalTypeName = signalTypeName;
                    TrueNormalSignals[k].Aspect = NormalSignals[i].Aspect;
                    TrueNormalSignals[k].SpeedLimitMpS = NormalSignals[i].SpeedLimitMpS;
                    TrueNormalSignals[k].DistanceM = NormalSignals[i].DistanceM;
                    TrueNormalSignals[k].AltitudeM = NormalSignals[i].AltitudeM;
                    k++;
                }
                VirtualSignals[i] = NextGenericSignalFeatures("REPEATER", i, 12000.0f);
                if (NormalSignals[i].DistanceM == float.MaxValue)
                    break;
            }
            for (int i = 0; i < InfoSignals.Length; i++)
            {
                InfoSignals[i] = NextGenericSignalFeatures("INFO", i, 12000.0f);
                if (InfoSignals[i].DistanceM == float.MaxValue)
                    break;
            }
            for (int i = 0; i < TunnelData.Length; i++)
            {
                TunnelData[i] = NextTunnel(i);
                if (TunnelData[i].DistanceM == float.MaxValue)
                    break;
            }
        }


        // methods for ETCS2

        protected void UpdateETCSL2State()
        {
            if (SpeedMpS() == 0)
            {
                switch (ETCSL2State)
                {
                    case ETCSL2STATE.Off:
                        if (SCMTAutotestTimer.Triggered)
                        {
                            SCMTAutotestTimer.Stop();
                            ETCSL2State = ETCSL2STATE.Autotest;
                            AutotestPhase = AUTOTESTPHASE.InitialWait;
                            SCMTAutotestTimer.Setup(SCMTAutotestTimerDelayS[0]);
                        }
                        break;
                    case ETCSL2STATE.Start:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Bottone_Start)])
                        {
                            TriggerGenericSound(Event.GenericEvent2);
                            ETCSL2State = ETCSL2STATE.ConfermaStart;
                            TCSButtonPressed[(int)(TCSCommandEvent.Bottone_Start)] = false;
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.ConfermareLiv2;
                        }
                        break;
                    case ETCSL2STATE.ConfermaStart:
                        if (TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)])
                        {
                            TriggerGenericSound(Event.GenericEvent2);
                            ETCSL2State = ETCSL2STATE.AttesaFullSupervision;
                            TCSButtonPressed[(int)(TCSCommandEvent.Conferma_Infobox)] = false;
                            AttesaFullSupervisionTimer.Start();
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                            RadioLinkState = true;
                        }
                        break;
                    case ETCSL2STATE.AttesaFullSupervision:
                        if (AttesaFullSupervisionTimer.Triggered)
                        {
                            AttesaFullSupervisionTimer.Stop();
                            ETCSL2State = ETCSL2STATE.FullSupervision;
                        }
                        break;


                }
            }
            if (TCSButtonReleased[(int)(TCSCommandEvent.Bottone_Start)])
            {
                TCSButtonReleased[(int)(TCSCommandEvent.Bottone_Start)] = false;
            }
        }

        void UpdateETCSIndicationSpeedCurve(float delayForIndicationCurve)
        {
            ETCSSignalIndicationSpeedCurveKpH =
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSignalTargetDistanceM,
                                MpS.FromKpH(ETCSSignalIndicationTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForIndicationCurve,
                                SafeDecelerationMpS2
                            )),
                            ETCSSignalIndicationTargetSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH
                    ),
                    SCMTCurrentSignalSpeedLimitKpH
                );

            ETCSSpeedPostIndicationSpeedCurveKpH =
                Math.Min(
                    Math.Min(
                        Math.Max(
                            MpS.ToKpH(SpeedCurve(
                                SCMTSpeedPostTargetDistanceM,
                                MpS.FromKpH(SCMTSpeedPostTargetSpeedKpH),
                                SCMTDeclivity,
                                SCMTDelayBeforeBrakingEstablishedS + delayForIndicationCurve,
                                SafeDecelerationMpS2
                            )),
                            SCMTSpeedPostTargetSpeedKpH
                        ),
                        SCMTTrainSpeedLimitKpH
                    ),
                    SCMTCurrentSpeedPostSpeedLimitKpH
                );

        }

        void UpdateETCSL2OverspeedMonitor()
        {
            switch (SCMTOverspeedMonitorState)
            {
                case MonitorState.Disabled:
                    if (SpeedControlSystemEnabled)
                    {
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                    }
                    break;

                case MonitorState.StandBy:
                    if (!SpeedControlSystemEnabled)
                    {
                        SCMTOverspeedMonitorState = MonitorState.Disabled;
                    }
                    else
                    {
                        if (SCMTOverspeed)
                        {
                            TriggerGenericSound(Event.GenericEvent5);
                            SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                            SCMTOverspeedMonitorState = MonitorState.Emergency;
                        }
                        else if (SCMTOverspeedWarning)
                        {
                            TriggerGenericSound(Event.GenericEvent4);
                            SCMTOverspeedMonitorState = MonitorState.Alarm;
                        }

                    }
                    break;
                case MonitorState.Alarm:
                    if (SCMTOverspeed)
                    {
                        TriggerSoundAlert2();
                        TriggerGenericSound(Event.GenericEvent5);
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 2);
                        SCMTOverspeedMonitorState = MonitorState.Emergency;
                    }
                    else if (SCMTOverspeedReadyToReset)
                    {
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                        TriggerSoundAlert2();
                    }
                    break;
                case MonitorState.Emergency:
                    if (SCMTOverspeedReadyToReset)
                    {
                        SCMTOverspeedMonitorState = MonitorState.ReadyToReset;
                        if (Autopilot) AutopilotTimer.Start();
                    }
                    break;
                case MonitorState.ReadyToReset:
                    if (ThrottlePercent() == 0 || Autopilot && AutopilotTimer.Triggered)
                    {
                        AutopilotTimer.Stop();
                        AutopilotButtonPressTimer.Start();
                        SetCabDisplayControl((int)(TCSCommandEvent.Visualizz_RF) - 1, 0);
                        SCMTOverspeedMonitorState = MonitorState.StandBy;
                        TriggerGenericSound(Event.GenericEvent2);
                        TriggerSoundAlert2();
                    }
                    else
                    {

                    }
                    break;
            }
        }


        protected void UpdatePOCState()
        {
            switch (POCState)
            {
                case POCSTATE.Zone3kV:
                case POCSTATE.Zone25kV:
                    if (infoSignalPassed && (PreviousInfoSignalMainHeadSignalType == "avvpoc3-25" || PreviousInfoSignalMainHeadSignalType == "avvpoc25-3"))
                    {
                        SetPantographsDown();
                        SetCircuitBreakerOpeningOrder(true);
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 1); // Non stopping area
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 1); // pantos lowering
                        TriggerGenericSound(Event.GenericEvent3);
                        if (PreviousInfoSignalMainHeadSignalType == "avvpoc3-25")
                        {
                            POCState = POCSTATE.POCTo25Announced;
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.NuovaTensione25kV;
                        }
                        else
                        {
                            POCState = POCSTATE.POCTo3Announced;
                            messaggioInfoboxAttivo = MESSAGGIINFOBOX.NuovaTensione3kV;
                        }
                    }
                    break;
                case POCSTATE.POCTo25Announced:
                    if (ArePantographsDown() || Autopilot)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 2); // pantos down
                        POCState = POCSTATE.POCTo25PantosDown;
                    }
                    break;
                case POCSTATE.POCTo25PantosDown:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "startabbpanto")
                    {
                        POCState = POCSTATE.POCTo25Start;
                    }
                    break;
                case POCSTATE.POCTo25Start:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "endabbpanto")
                    {
                        POCState = POCSTATE.POCTo25End;
                        TargetDistanceToRisePantosM = DistanceM() + TrainLengthM();
                    }
                    break;
                case POCSTATE.POCTo25End:
                    if (DistanceM() >= TargetDistanceToRisePantosM)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 0);
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 2); //  panto down
                        POCState = POCSTATE.POCTo25WaitPowerSwitchMoved;
                    }
                    break;
                case POCSTATE.POCTo25WaitPowerSwitchMoved:
                    if (Autopilot)
                        PowerSupplySwitchState = POWERSUPPLYTYPE._25kV;
                    if (PowerSupplySwitchState == POWERSUPPLYTYPE._25kV)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 3); //  panto rising
                        SetPantographUp(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 0]);
                        POCState = POCSTATE.POCTo25WaitPantosUp;
                    }
                    break;
                case POCSTATE.POCTo25WaitPantosUp:
                    if (GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 0]) == PantographState.Up)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 0);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        SetCircuitBreakerOpeningOrder(false);
                        SetCircuitBreakerClosingOrder(true);
                        POCState = POCSTATE.POCTo25WaitCircuitBreakerClosed;
                        if (Autopilot)
                        {
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 0]);
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 1]);
                        }
                    }
                    break;
                case POCSTATE.POCTo25WaitCircuitBreakerClosed:
                    if (!CircuitBreakerOpen)
                    {
                        SetCircuitBreakerClosingOrder(false);
                        POCState = POCSTATE.Zone25kV;
                    }
                    break;
                case POCSTATE.POCTo3Announced:
                    if (ArePantographsDown() || Autopilot)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 2); // pantos down
                        POCState = POCSTATE.POCTo3PantosDown;
                    }
                    break;
                case POCSTATE.POCTo3PantosDown:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "startabbpanto")
                    {
                        POCState = POCSTATE.POCTo3Start;
                    }
                    break;
                case POCSTATE.POCTo3Start:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "endabbpanto")
                    {
                        POCState = POCSTATE.POCTo3End;
                        TargetDistanceToRisePantosM = DistanceM() + TrainLengthM();
                    }
                    break;
                case POCSTATE.POCTo3End:
                    if (DistanceM() >= TargetDistanceToRisePantosM)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 0);
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 2); //  panto down
                        POCState = POCSTATE.POCTo3WaitPowerSwitchMoved;
                    }
                    break;
                case POCSTATE.POCTo3WaitPowerSwitchMoved:
                    if (Autopilot)
                        PowerSupplySwitchState = POWERSUPPLYTYPE._3kV;
                    if (PowerSupplySwitchState == POWERSUPPLYTYPE._3kV)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 3); //  panto rising
                        SetPantographUp(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 0]);
                        POCState = POCSTATE.POCTo3WaitPantosUp;
                    }
                    break;
                case POCSTATE.POCTo3WaitPantosUp:
                    if (GetPantographState(PantoIndex[(int)POWERSUPPLYTYPE._3kV, 0]) == PantographState.Up)
                    {
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 0);
                        messaggioInfoboxAttivo = MESSAGGIINFOBOX.Void;
                        SetCircuitBreakerOpeningOrder(false);
                        SetCircuitBreakerClosingOrder(true);
                        POCState = POCSTATE.POCTo3WaitCircuitBreakerClosed;
                        if (Autopilot)
                        {
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 0]);
                            SetPantographDown(PantoIndex[(int)POWERSUPPLYTYPE._25kV, 1]);
                        }
                    }
                    break;
                case POCSTATE.POCTo3WaitCircuitBreakerClosed:
                    if (!CircuitBreakerOpen)
                    {
                        SetCircuitBreakerClosingOrder(false);
                        POCState = POCSTATE.Zone3kV;
                    }
                    break;
            }
        }

        protected void UpdatePCFState()
        {
            switch (PCFState)
            {
                case PCFSTATE.OutOfPCF:
                    if (infoSignalPassed && (PreviousInfoSignalMainHeadSignalType == "avvstartneutro"))
                    {
                        SetTractionAuthorization(false);
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 1); // Non stopping area
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 3); // avviso zona neutra
                        TriggerGenericSound(Event.GenericEvent3);
                        PCFState = PCFSTATE.PCFAnnounced;
                        StaccoAttaccoCarichiTimer.Start();
                    }
                    break;
                case PCFSTATE.PCFAnnounced:
                    if (StaccoAttaccoCarichiTimer.Triggered)
                    {
                        StaccoAttaccoCarichiTimer.Stop();
                        PCFState = PCFSTATE.PCFStart;
                        SetCircuitBreakerOpeningOrder(true);
                    }
                    break;
                case PCFSTATE.PCFStaccoCarichi:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "startneutro")
                    {
                        PCFState = PCFSTATE.PCFStart;
                    }
                    break;
                case PCFSTATE.PCFStart:
                    if (infoSignalPassed && PreviousInfoSignalMainHeadSignalType == "endneutro")
                    {
                        PCFState = PCFSTATE.PCFEnd;
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB4) - 1, 0);
                        SetCabDisplayControl((int)(TCSCommandEvent.CampoB3) - 1, 0);
                        TargetDistanceToRisePantosM = DistanceM() + TrainLengthM();
                    }
                    break;
                case PCFSTATE.PCFEnd:
                    if (DistanceM() >= TargetDistanceToRisePantosM)
                    {
                        SetCircuitBreakerOpeningOrder(false);
                        SetCircuitBreakerClosingOrder(true);
                        PCFState = PCFSTATE.PCFWaitCircuitBreakerClosed;
                    }
                    break;
                case PCFSTATE.PCFWaitCircuitBreakerClosed:
                    if (!CircuitBreakerOpen)
                    {
                        SetCircuitBreakerClosingOrder(false);
                        PCFState = PCFSTATE.PCFWaitAttaccoCarichi;
                        StaccoAttaccoCarichiTimer.Start();
                    }
                    break;
                case PCFSTATE.PCFWaitAttaccoCarichi:
                    if (StaccoAttaccoCarichiTimer.Triggered)
                    {
                        StaccoAttaccoCarichiTimer.Stop();
                        SetTractionAuthorization(false);
                        PCFState = PCFSTATE.OutOfPCF;
                    }
                    break;
            }
        }

        protected void UpdateETCSL2()
        {
            SCMTTrainLengthM = (float)Math.Ceiling((double)(TrainLengthM() / 100f)) * 100f;
            if (HeavyFreightTrain)
                SCMTDelayBeforeBrakingEstablishedS = 12f + SCMTTrainLengthM / 200f;
            else
                SCMTDelayBeforeBrakingEstablishedS = 2f + 2f * SCMTTrainLengthM * SCMTTrainLengthM * 0.00001f;
            SCMTNextSignalSpeedLimitKpH = MpS.ToKpH(NormalSignals[0].SpeedLimitMpS);
            if (SCMTNextSignalSpeedLimitKpH == -3.6f && NormalSignals[0].DistanceM != float.MaxValue)
                SCMTNextSignalSpeedLimitKpH = TrainSpeedLimitKpH;
            SCMTSignalTargetSpeedKpH = Math.Max(SCMTNextSignalSpeedLimitKpH == -3.6f ? SCMTTrainSpeedLimitKpH : SCMTNextSignalSpeedLimitKpH, ReleaseSpeed);
            ETCSSignalIndicationTargetSpeedKpH = SCMTNextSignalSpeedLimitKpH == -3.6f ? SCMTTrainSpeedLimitKpH : SCMTNextSignalSpeedLimitKpH;
            if (NormalSignals[0].Aspect == Aspect.Stop)
            {
                SCMTNextSignalSpeedLimitKpH = 0;
                SCMTSignalTargetSpeedKpH = ReleaseSpeed;
                ETCSSignalIndicationTargetSpeedKpH = 0;
            }
            var incIndex = 0;
            //if next normal signal is a virtual signal that restores line speed, it's not considered
            var nextNormalSignalMainHeadSignalType = NextGenericSignalFeatures("NORMAL", 0, float.MaxValue).MainHeadSignalTypeName;
            if (nextNormalSignalMainHeadSignalType == "riprvel" || nextNormalSignalMainHeadSignalType == "fsvirtfinelim")
                incIndex = 1;
            SCMTSignalTargetDistanceM = NormalSignals[incIndex].DistanceM;
            var previousCurrentSpeedLimitMpS = Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH);

            SCMTCurrentSignalSpeedLimitKpH = MpS.ToKpH(CurrentSignalSpeedLimitMpS());
            if (SCMTCurrentSignalSpeedLimitKpH == -3.6f) SCMTCurrentSignalSpeedLimitKpH = TrainSpeedLimitKpH;
            if (normalSignalPassed)
                SpeedHookTimer.Start();
            // speed reduction shown up to second signal ahead
            // TODO: consider also speedposts
            if (SpeedHookTimer.Triggered && SCMTNextSignalSpeedLimitKpH < SCMTCurrentSignalSpeedLimitKpH && SCMTNextSignalSpeedLimitKpH != 0)
            {
                var nextNextSignalSpeedLimitKpH = MpS.ToKpH(NormalSignals[1].SpeedLimitMpS);
                if (nextNextSignalSpeedLimitKpH >= -1f && NormalSignals[1].DistanceM != float.MaxValue && nextNextSignalSpeedLimitKpH < SCMTNextSignalSpeedLimitKpH)
                {
                    SCMTNextSignalSpeedLimitKpH = nextNextSignalSpeedLimitKpH;
                    SCMTSignalTargetDistanceM = NormalSignals[incIndex + 1].DistanceM;
                }
            }
            if (SCMTNextSignalSpeedLimitKpH == 0) SCMTSignalTargetDistanceM = Math.Max(SCMTSignalTargetDistanceM - 200, 0);

            // Update current speed limit when speed is below the target or when the train approaches the signal
            /*               if (NextSignalDistanceM(0) <= 5f)
                            {
                                if (nextSignalSpeedLimitKpH > 0f && nextSignalSpeedLimitKpH < SCMTTrainSpeedLimitKpH)
                                    SCMTCurrentSignalSpeedLimitKpH = nextSignalSpeedLimitKpH;
                                else
                                    SCMTCurrentSignalSpeedLimitKpH = SCMTTrainSpeedLimitKpH;
                            }*/


            // Speed post speed limit preparation
            SCMTNextSpeedPostSpeedLimitKpH = (NextPostSpeedLimitMpS(0) > 0 ? MpS.ToKpH(NextPostSpeedLimitMpS(0)) : SCMTTrainSpeedLimitKpH);
            var currentPostSpeedLimitMpS = CurrentPostSpeedLimitMpS();
            SCMTCurrentSpeedPostSpeedLimitKpH = currentPostSpeedLimitMpS > 500 ? SCMTTrainSpeedLimitKpH : MpS.ToKpH(currentPostSpeedLimitMpS);
            SCMTSpeedPostTargetSpeedKpH = SCMTNextSpeedPostSpeedLimitKpH;
            SCMTSpeedPostTargetDistanceM = NextPostDistanceM(0);
            var nextSpeedLimitKpH = Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH);
            SetNextSpeedLimitMpS(MpS.FromKpH(nextSpeedLimitKpH + ETCSSpeedLimitToleranceKpH));
            var currentSpeedLimitKpH = Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH);
            UpdateSCMTSpeedCurve(ETCSDelayForEmergencyCurve, ETCSDelayForWarningCurve);
            if (nextSpeedLimitKpH < currentSpeedLimitKpH)
                UpdateETCSIndicationSpeedCurve(ETCSDelayForIndicationCurve);
            SetCurrentSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTSignalAlertSpeedCurveKpH, SCMTSpeedPostAlertSpeedCurveKpH) - SCMTCurrentAlertSpeedKpH + ETCSSpeedLimitToleranceKpH));
            SetInterventionSpeedLimitMpS(MpS.FromKpH(Math.Min(SCMTSignalEmergencySpeedCurveKpH, SCMTSpeedPostEmergencySpeedCurveKpH) + ETCSSpeedLimitToleranceKpH));
            if (previousCurrentSpeedLimitMpS < currentSpeedLimitKpH)
                TriggerGenericSound(Event.GenericEvent3);

        }

        private void BuildUpPlanningProfile()
        {
            UpdateIndicationBar();
            var MaxDistanceAheadM = UpdateSpeedTargets();
            UpdateIndicationMarker(MaxDistanceAheadM);
            UpdateGradientProfile(MaxDistanceAheadM);
            PopulateAnnouncements(MaxDistanceAheadM);
        }

        private void UpdateIndicationBar()
        {
            if (Math.Min(SCMTNextSignalSpeedLimitKpH, SCMTNextSpeedPostSpeedLimitKpH) + 1 < Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH))
            {
                var CurrentMaxSpeedMpS = MpS.FromKpH(CurrentMaxSpeedKpH);
                //
                if (SCMTNextSignalSpeedLimitKpH + 1 < CurrentMaxSpeedKpH && SCMTNextSpeedPostSpeedLimitKpH + 1 < CurrentMaxSpeedKpH)
                {
                    // check for distance
                    if (SCMTSpeedPostTargetDistanceM > SCMTSignalTargetDistanceM)
                    {
                        // signal speed is considered
                        ETCSStatus.IndicationMarkerDistanceM = SCMTSignalTargetDistanceM -
                            DistanceCurve(CurrentMaxSpeedMpS, MpS.FromKpH(ETCSSignalIndicationTargetSpeedKpH), SCMTDeclivity, SCMTDelayBeforeBrakingEstablishedS + ETCSDelayForIndicationCurve, SafeDecelerationMpS2);
                    }
                    else
                    {
                        // speedpostSpeed is reducing
                        ETCSStatus.IndicationMarkerDistanceM = SCMTSpeedPostTargetDistanceM -
                            DistanceCurve(CurrentMaxSpeedMpS, MpS.FromKpH(SCMTSpeedPostTargetSpeedKpH), SCMTDeclivity, SCMTDelayBeforeBrakingEstablishedS + ETCSDelayForIndicationCurve, SafeDecelerationMpS2);
                    }
                }
                else if (SCMTNextSignalSpeedLimitKpH + 1 < CurrentMaxSpeedKpH)
                {
                    // signal speed is reducing
                    ETCSStatus.IndicationMarkerDistanceM = SCMTSignalTargetDistanceM -
                         DistanceCurve(CurrentMaxSpeedMpS, MpS.FromKpH(ETCSSignalIndicationTargetSpeedKpH), SCMTDeclivity, SCMTDelayBeforeBrakingEstablishedS + ETCSDelayForIndicationCurve, SafeDecelerationMpS2);
                }
                else
                {
                    // speedpost speed is reducing
                    ETCSStatus.IndicationMarkerDistanceM = SCMTSpeedPostTargetDistanceM -
                        DistanceCurve(CurrentMaxSpeedMpS, MpS.FromKpH(SCMTSpeedPostTargetSpeedKpH), SCMTDeclivity, SCMTDelayBeforeBrakingEstablishedS + ETCSDelayForIndicationCurve, SafeDecelerationMpS2);
                }
                if (ETCSStatus.IndicationMarkerDistanceM < 0)
                    ETCSStatus.IndicationMarkerDistanceM = 0;
                else
                {

                }
            }
            else ETCSStatus.IndicationMarkerDistanceM = 0;
        }

        private float UpdateSpeedTargets()
        {
            float maxDistanceAheadM = 0;
            ETCSStatus.SpeedTargets.Clear();
            ETCSStatus.SpeedTargets.Add(new PlanningTarget(0, MpS.FromKpH(Math.Min(SCMTCurrentSignalSpeedLimitKpH, SCMTCurrentSpeedPostSpeedLimitKpH))));
            for (int i = 0; i < 5; i++)
            {
                maxDistanceAheadM = NextSignalDistanceM(i);
                float speedLimMpS = NextSignalSpeedLimitMpS(i);
                if (NextSignalAspect(i) == Aspect.Stop) break;
                if (speedLimMpS >= 0) ETCSStatus.SpeedTargets.Add(new PlanningTarget(maxDistanceAheadM, speedLimMpS));
            }
            float prevDist = 0;
            float prevSpeed = 0;
            for (int i = 0; i < 10; i++)
            {
                float distanceM = NextPostDistanceM(i);
                if (distanceM > maxDistanceAheadM) break;
                float speed = NextPostSpeedLimitMpS(i);
                if (speed == prevSpeed || distanceM - prevDist < 10) continue;
                ETCSStatus.SpeedTargets.Add(new PlanningTarget(distanceM, speed));
                prevDist = distanceM;
                prevSpeed = speed;
            }
            ETCSStatus.SpeedTargets.Sort((x, y) => x.DistanceToTrainM.CompareTo(y.DistanceToTrainM));
            ETCSStatus.SpeedTargets.Add(new PlanningTarget(maxDistanceAheadM, 0));

            return maxDistanceAheadM;
        }

        private void UpdateIndicationMarker(float maxDistanceAheadM)
        {

        }

        private void UpdateGradientProfile(float maxDistanceAheadM)
        {
            ETCSStatus.GradientProfile.Clear();
            var gradientPerMille = (int)(CurrentGradientPercent() * 10);
            ETCSStatus.GradientProfile.Add(new GradientProfileElement(0, gradientPerMille));
            var precedingGradientPerMille = gradientPerMille;
            var startingAltitudeM = AltitudeM(); 
            var startingDistanceM = 0f;
            for (int i = 0; i < NormalSignals.Length; i++)
            {
                if (NormalSignals[i].DistanceM <= startingDistanceM + 20f) continue;
                if (NormalSignals[i].DistanceM == float.MaxValue)
                {
                    ETCSStatus.GradientProfile.Add(new GradientProfileElement(startingDistanceM, 0)); // End of profile
                    break;
                }
                var endAltitudeM = NormalSignals[i].AltitudeM;
                var endDistanceM = NormalSignals[i].DistanceM;
                gradientPerMille = 0;
                if (Math.Abs(startingAltitudeM - endAltitudeM) > 1)
                    gradientPerMille = (int)((endAltitudeM - startingAltitudeM) / (endDistanceM - startingDistanceM) * 1000.0f);
                if (Math.Abs(gradientPerMille -  precedingGradientPerMille) > 1)
                    ETCSStatus.GradientProfile.Add(new GradientProfileElement(startingDistanceM, gradientPerMille));
                startingAltitudeM = endAltitudeM;
                startingDistanceM = endDistanceM;
                precedingGradientPerMille = gradientPerMille;
            }
            if (NormalSignals[NormalSignals.Length - 1].DistanceM < float.MaxValue &&
                startingDistanceM > ETCSStatus.GradientProfile.Last().DistanceToTrainM + 50f)
                ETCSStatus.GradientProfile.Add(new GradientProfileElement(startingDistanceM, 0)); // End of profile
        }

        private void PopulateAnnouncements (float maxDistanceAheadM)
        {
            ETCSStatus.PlanningTrackConditions.Clear();
            foreach (var infoSignal in InfoSignals)
            {
                if (infoSignal.DistanceM > maxDistanceAheadM && infoSignal.DistanceM >= 0)
                    break;
                switch (infoSignal.MainHeadSignalTypeName)
                {
                    case "startneutro":
                        ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.NonStoppingArea, false, infoSignal.DistanceM));
                        ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.NeutralSectionAnnouncement, false, infoSignal.DistanceM));
                        break;
                    case "startabbpanto":
                        ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.NonStoppingArea, false, infoSignal.DistanceM));
                        ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.LowerPantograph, false, infoSignal.DistanceM));
                        break;
                    case "avvstartertms":
                    case "startertms":
                    case "avvendertms":
                    case "endertms":
                    case "avvstartneutro":
                    case "endneutro":
                    case "avvpoc3-25":
                    case "avvpoc25-3":
                    case "endabbpanto":
                        break;
                    default:
                        break;
                }
            }

            // Tunnels
            foreach (var tunnel in TunnelData)
            {
                if (tunnel.DistanceM == float.MaxValue || tunnel.DistanceM >= maxDistanceAheadM && tunnel.DistanceM >= 0)
                    break;
                ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.Tunnel, false, tunnel.DistanceM));
            }

            // First station
            var nextStationDistanceM = NextStationDistanceM();
            if (NextStationDistanceM() < float.MaxValue)
                ETCSStatus.PlanningTrackConditions.Add(new PlanningTrackCondition(TrackConditionType.Station, false, nextStationDistanceM));
        }
    }
}