using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;

namespace IOB_WIN_FORM.Iob
{
    public class PingWatchDog : Generic
    {
        #region Public Constructors

        private int bInOff = 0;
        private int bInOn = 1;
        private int vetoCheckSec = 5;

        /// <summary>
        /// Estende l'init della classe base
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public PingWatchDog(AdapterForm caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            lgInfo("NEW IobPing (WatchDog)");
            // init datetime counters
            DateTime adesso = DateTime.Now;
            lastPzCountSend = adesso;
            lastWarnODL = adesso;
            vetoCheckStatus = adesso;
            // 2023.09.05 imposto anche primo ping e check disconnected...
            lastPING = adesso;
            lastDisconnCheck = adesso;
            PoweroffTimeoutSec = IOBConfFull.Device.PoweroffTimeOutSec;
            // imposto i valori da inviare al controllo stato...
            bInOff = IOBConfFull.Special.PingConf.B_PowerOff;
            bInOn = IOBConfFull.Special.PingConf.B_PowerOn;
            vetoCheckSec = IOBConfFull.Special.PingConf.VetoCheckSec;
            // fix coda ping
            QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan);
            lgDebug($"L'adapter effettuera' PING di controllo all'indirizzo {IOBConfFull.Device.Connect.IpAddr} per forzare stato poweroff dopo {PoweroffTimeoutSec} sec");
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// metodo controllo ping lento, override AutoODL
        /// </summary>
        public override void processDisconnectedTask()
        {
            // effettuo check ping!
            DateTime adesso = DateTime.Now;
            if (adesso.Subtract(lastPING).TotalSeconds >= PoweroffTimeoutSec)
            {
                // invio poweroff!
                newDisplayData currDispData = new newDisplayData();
                readSemafori(ref currDispData);
                // SOLO SE ho poweroff da inviare...
                if (B_output == 0)
                {
                    accodaSigIN(ref currDispData);
                    // reset last ping...
                    lastPING = DateTime.Now;
                }
            }
        }

        /// <summary>
        /// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
        /// aggiornare x display in form</paramref>
        /// </summary>
        public override void readSemafori(ref newDisplayData currDispData)
        {
            Stopwatch sw = new Stopwatch();
            DateTime adesso = DateTime.Now;
            //lgInfo($"readSemafori | {adesso}");
            sw.Start();
            byte[] MemBlock = new byte[2];
            // faccio comunque ping se non ne ho collezionati a sufficienza...
            if (QueuePing.Count < maxQueuePing)
            {
                // in primis salvo data ping comunque...
                lastPING = DateTime.Now;
                // salvo esito ping
                bool pingOK = testPingMachine == IPStatus.Success;
                addTest(pingOK);
            }

            if (adesso.Subtract(lastPING).TotalSeconds >= vetoCheckSec)
            {
                try
                {
                    currDispData.semIn = Semaforo.SV;
                    // in primis salvo data ping comunque...
                    lastPING = DateTime.Now;
                    // salvo esito ping
                    bool pingOK = testPingMachine == IPStatus.Success;
                    addTest(pingOK);

                    // se passa il ping faccio il resto...
                    if (pingStatusOk())
                    {
                        connectionOk = true;
                        lastReadPLC = DateTime.Now;
                        lastWatchDog = DateTime.Now;
                    }
                    else
                    {
                        connectionOk = false;
                    }

                    if (connectionOk)
                    {
                        B_input = bInOn;
                    }
                    else
                    {
                        B_input = bInOff;
                    }
                    // annullo lettura bit signal IN pre/post x evitare invio automatico...
                    B_output = B_input;
                    B_previous = B_input;
                }
                catch
                {
                    currDispData.semIn = Semaforo.SR;
                }
            }
            sw.Stop();
            // fermo per una quota del tempo di attesa previsto
            Task.Delay(vetoCheckSec - (int)sw.ElapsedMilliseconds + 20);
        }

        public override void startAdapter(bool resetQueue)
        {
            base.startAdapter(resetQueue);
            // 2023.09.05 imposto anche primo ping e check disconnected...
            DateTime adesso = DateTime.Now;
            lastWatchDog = adesso;
            //lastPING = adesso;
            lastReadPLC = adesso;
            lastDisconnCheck = adesso;
            // faccio un primo check POST ritardo
            tryConnect();
        }

        /// <summary>
        /// Override connessione
        /// </summary>
        public override void tryConnect()
        {
            bool doLog = (verboseLog || periodicLog);
            lgDebug($"PING: tryConnect step 01 | connectionOk: {connectionOk}");
            if (!connectionOk)
            {
                //// resetto coda...
                //QueuePing = new DataQueue("000", "QueuePing", false);
                // controllo che il ping sia stato tentato almeno pingTestSec fa...
                if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
                {
                    if (doLog)
                    {
                        lgInfo("PING: ConnKO - tryConnect");
                    }
                    lgDebug("PING: tryConnect step 04");

                    lgDebug("PING: Reset QueuePing");

                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    bool pingOK = testPingMachine == IPStatus.Success;
                    addTest(pingOK);
                    sw.Stop();
                    trackReadData(4, sw.Elapsed.TotalSeconds);

                    // se passa il ping faccio il resto...
                    if (pingStatusOk())
                    {
                        // in primis salvo data ping...
                        lastPING = DateTime.Now;
                        connectionOk = true;
                        queueInEnabCurr = true;
                        lgInfo("PING OK");
                    }
                    else
                    {
                        // loggo no risposta ping ...
                        lgError("PING KO");
                    }
                }
            }
        }

        /// <summary>
        /// Override disconnessione
        /// </summary>
        public override void tryDisconnect()
        {
            lgInfo("Richiesta disconnessione adapter PING!");
            connectionOk = false;
            queueInEnabCurr = false;
        }

        #endregion Public Methods

        #region Protected Fields

        protected int PoweroffTimeoutSec = 100;

        /// <summary>
        /// Veto controllo status x log...
        /// </summary>
        protected DateTime vetoCheckStatus = DateTime.Now;

        #endregion Protected Fields

        #region Protected Methods

        protected void addTest(bool pingOk)
        {
            int score = pingOk ? 1 : 0;
            // controllo: se era spenta e risulta ping ok --> reset coda!
            if (B_input == 0 && pingOk)
            {
                B_input = 1;
                QueuePing = new DataQueue(IOBConfFull.General.FilenameIOB, "QueuePing", false, redisMan);
                lgTrace($"QueuePing resetted on addTest");
            }
            QueuePing.Enqueue($"{score}");
            while (QueuePing.Count > maxQueuePing)
            {
                string res = "";
                QueuePing.TryDequeue(out res);
            }
        }

        /// <summary>
        /// Calcola status ping:
        /// - se ha ‹ 50% coda richiesta --› true
        /// - se ha › 50% coda richiesta --› true se è maggior parte a 1 (true)
        /// </summary>
        /// <returns></returns>
        protected bool pingStatusOk()
        {
            bool answ = true;
            long numVal = QueuePing.Count;
            if (numVal > maxQueuePing / 2)
            {
                var listaValori = QueuePing.ToList();
                long numOk = listaValori.Where(x => x == "1").Count();
                long numKo = numVal - numOk;
                answ = numOk >= numKo;
                lgTrace($"PING ok per: {numOk} > {numKo}");
            }
            else
            {
                lgTrace("PING ok per mancanza dati minimi test");
            }
            return answ;
        }

        #endregion Protected Methods
    }
}