using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using Newtonsoft.Json;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using static IOB_UT_NEXT.CustomObj;

namespace IOB_WIN_OPC_UA.IobOpc
{
    public class OpcUaSiemensSW : OpcUaSiemens
    {
        #region Public Constructors

        /// <summary>
        /// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
        /// gestione specifica per macchine SW (Donati Lavorazioni)
        /// https://github.com/OPCFoundation/UA-.NETStandard
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public OpcUaSiemensSW(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            sendKeyRichiesta = true;
            lgInfo("Avvio OpcUa Siemens versione SW Machines");
        }

        #endregion Public Constructors

        #region Public Methods

#if false
        /// <summary>
        /// Effettua reset del contapezzi sulla tavola richiesta
        /// </summary>
        /// <returns></returns>
        public override bool resetContapezziPLC(string codTav)
        {
            bool answ = false;
            // verifico se richiesta è per tav 1 o 2...
            Dictionary<string, string> task2add = new Dictionary<string, string>();
            task2add.Add("forceResetPzCount#TAV1", $"{0}");
            executeTasks(task2add);
            return answ;
        } 
#endif

        #endregion Public Methods

        #region Protected Methods

        /// <summary>
        /// Effettua decodifica aree memoria alla bitmap usata x MAPO
        /// </summary>
        protected override void decodeToBaseBitmap()
        {
            DateTime adesso = DateTime.Now;
            CheckLiveData(adesso);
            // init a zero...
            B_input = 0;

            /* -----------------------------------------------------
             *  STATE MACHINE 60 STD / SIMULA + gestione doppia tavola
             *------------------------------------------------------
             * bitmap MAPO
             * B0: POWER_ON
             * B1: RUN
             * B2: pzCount
             * B3: allarme
             * B4: manuale
             * B5: SlowTC (NON gestito qui)
             * B6: warm-up / cool-down / setup
             * B7: emergenza ARMATA (1=ok, 0 = premuta)
             * B8: TAV 1 attiva (A)
             * B9: TAV 2 attiva (U)
             ---------------------------------------------------- */

            // se valido il check ping lo eseguo... altrimenti lo do x buono
            bool checkPing = !opcUaParams.pingAsPowerOn;
            string currRun = "N.A.";

            // test ping come base + volte prima di dire che non risponde...
            int currTryPing = maxTryPing;
            while (currTryPing > 0 && !checkPing)
            {
                // Esegui il test una volta sola per iterazione
                checkPing = (testPingMachine == IPStatus.Success);

                if (!checkPing)
                {
                    currTryPing--;
                    if (currTryPing > 0)
                    {
                        // Aumenta leggermente il tempo di attesa minimo.
                        // Sotto i 15-30ms, Windows non è preciso e consuma molta CPU per il timer hardware.
                        int waitTime = new Random().Next(pingServerMsTimeout, pingServerMsTimeout * 2);
                        Thread.Sleep(waitTime);
                    }
                }
            }
            // bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"...
            bool powerOnOk = checkPing && hasPowerOn;
            // procedo SOLO SE mi da ping OK...
            if (checkPing)
            {
                B_input = powerOnOk ? 1 : 0;

                procRunMode(ref currRun);

                // salvo running come = working...
                isRunning = isWorking;

                // se ho emergenza premuta --> emergenza!
                if (hasEStopArmed)
                {
                    B_input += (1 << 7);
                }
                // se ho emergenza premuta --> emergenza!
                if (isWarmUpCoolDown)
                {
                    B_input += (1 << 6);
                }
                // se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM!
                if (hasError)
                {
                    B_input += (1 << 3);
                }
                if (isWorking)
                {
                    // RUN = LAVORA!
                    B_input += (1 << 1);
                }
                else if (powerOnOk && (!isReady || isManual))
                {
                    // se NON ready --> manual
                    B_input += (1 << 4);
                }
                // verifica speciale dei BIT della tavola corrente
                if (opcUaParams.isMulti)
                {
                    if (hasActiveTav1)
                    {
                        B_input += (1 << 8);
                    }
                    if (hasActiveTav2)
                    {
                        B_input += (1 << 9);
                    }
                }
            }
            else
            {
                lgError($"Errore: test ping non superato | B_input: {B_input}");
            }
            // se abilitato watchdog...
            if (opcUaParams.WatchDog.IsEnabled)
            {
                if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2)
                {
                    lastWatchDogPLC = adesso;
                    WatchDog++;
                    WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog;

                    lgTrace($"WatchDog val: {WatchDog}");
                    try
                    {
                        WriteValue commWriteVal = new WriteValue();
                        commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite);
                        commWriteVal.AttributeId = Attributes.Value;
                        commWriteVal.Value = new DataValue();
                        commWriteVal.Value.Value = WatchDog;

                        List<WriteValue> nodes2Write = new List<WriteValue>();
                        nodes2Write.Add(commWriteVal);

                        UA_ref.WriteNodes(nodes2Write);
                    }
                    catch (Exception exc)
                    {
                        lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
                    }
                }
            }
            else
            {
                lgTrace("WatchDog disabilitato");
            }

            // controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto
            if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2)
            {
                tryDisconnect();
            }

            // solo se non ho veto check
            int vFactor = 2;
            if (vetoCheckStatus < adesso)
            {
                lgDebug($"Stato variabili checkPing: {testPingMachine}");
                // imposto veto per vetoSeconds...
                vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
            }

            // log opzionale!
            if (verboseLog)
            {
                lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
            }
        }

        /// <summary>
        /// Effettua sync dati inviando info per la doppia tavola
        /// </summary>
        protected override void ProcessDataSync()
        {
            lgInfo("--------------------------------------------");
            lgInfo($"executeTasks --> syncDbData --> processDataSync");
            lgInfo("--------------------------------------------");
            // effettua sync scrivendo cablate le info x art/num pezzi + dati ODL
            var taskGet = iobGetSendOdlDP();
        }

        #endregion Protected Methods

        #region Private Methods

        /// <summary>
        /// Recupero dati da server x invio dati ODL delle 2 tavole A/U
        /// </summary>
        /// <returns></returns>
        private bool iobGetSendOdlDP()
        {
            bool answ = false;

            return answ;
        }

        /// <summary>
        /// Override x scrittura valori tipo IMPULSO (es reset contapezzi impulsivo impianti SW)
        /// </summary>
        /// <param name="keyReq">chaive  memwrite da usare</param>
        /// <param name="valReq">valore da scrivere</param>
        /// <returns></returns>
        protected override bool ProcessOtherInfo(string keyReq, string valReq)
        {
            bool answ = false;
            if (memMap != null && memMap.mMapWrite != null)
            {
                // preparo elenco parametri da inviare...
                var list2Write = new List<objItem>();
                // aggiungo bit popupz
                objItem currData = new objItem()
                {
                    uid = keyReq,
                    reqValue = valReq,
                    name = keyReq
                };
                list2Write.Add(currData);
                // scrivo!
                plcWriteParams(ref list2Write);
                lgInfo($"processOtherInfo | eseguito plcWriteParams per keyReq: {keyReq} | valReq: {valReq}");
                answ = true;
            }
            return answ;
        }

        #endregion Private Methods
    }
}