using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using Opc.Ua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace IOB_WIN_OPC_UA.IobOpc
{
    public class OpcUaOmronGhidini : OpcUaOmron
    {
        #region Public Constructors

        /// <summary>
        /// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la
        /// gestione specifica per Omron (es ICOEL) https://github.com/OPCFoundation/UA-.NETStandard
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IOBConf">Configurazione (legacy)</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public OpcUaOmronGhidini(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            // inizializzo classe base...
            sendKeyRichiesta = true;
            doByteRead = false;
            lgInfo($"Avviato OpcUaOmronGhidini | encodeReadData: {doByteRead}");
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Override processing x gestione custom forceReset tramite bit...
        /// </summary>
        /// <param name="task2exe"></param>
        public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe, string codTav)
        {
            lgInfo($"OpcUaOmronGhidini: call executeTasks | {task2exe.Count} task");
            // SOLO SE si tratta di reset...
            foreach (var item in task2exe)
            {
                // converto richiesta in enum...
                taskType tName = taskType.nihil;
                Enum.TryParse(item.Key, out tName);
                string iKey = item.Key;
                // se è DP aggiungo in chiave il valore della TAV richiesta... ad es setComm --> setComm#TAV_1
                if (!string.IsNullOrEmpty(codTav))
                {
                    iKey += $"#{codTav}";
                }
                // controllo sulla KEY...
                switch (tName)
                {
                    case taskType.forceResetPzCount:
                        resetContapezziPLC(codTav);
                        break;
                }
            }

            // uso metodo base x completare gestione
            return base.executeTasks(task2exe, codTav);
        }


        /// <summary>
        /// Effettua reset del contapezzi
        /// </summary>
        /// <returns></returns>
        public override bool resetContapezziPLC(string codTav)
        {
            bool answ = false;
            lgInfo("OpcUaGhidini.resetContapezziPLC - gestione segnale reset");
            if (memMap != null && memMap.mMapWrite != null)
            {
                string pKey = "forceResetPzCount";
                if (memMap.mMapWrite.ContainsKey(pKey))
                {
                    dataConf currMem = memMap.mMapWrite[pKey];
                    string addr = currMem.memAddr;
                    // imposto il valore e forzo la scrittura ...
                    memMap.mMapWrite[pKey].value = "true";
                    // preparo elenco parametri da inviare...
                    list2Write = new List<objItem>();
                    // aggiungo bit reset
                    objItem currData = new objItem()
                    {
                        uid = pKey,
                        reqValue = "true",
                        name = pKey
                    };
                    list2Write.Add(currData);
                    // scrivo!
                    plcWriteParams(ref list2Write);

                    // sctivo nuovo parametro x spegnere bit...
                    Thread.Sleep(500);
                    list2Write.Clear();
                    currData = new objItem()
                    {
                        uid = pKey,
                        reqValue = "false",
                        name = pKey
                    };
                    list2Write.Add(currData);
                    // scrivo!
                    plcWriteParams(ref list2Write);

                    // scriivo anche il reset del contapezzi forzato (SE configurato)
                }
            }
            return answ;
        }

        #endregion Public Methods

        #region Protected Methods

        /// <summary>
        /// Override x scrittura valori composto (stringa lotto impianto Ghidini Tav02)
        /// </summary>
        /// <param name="keyReq"></param>
        /// <param name="valReq"></param>
        /// <returns></returns>
        protected override bool ProcessOtherInfo(string keyReq, string valReq)
        {
            // fixme todo da provare !!!
            // la chiave in questo caso è direttamente "processOtherInfo"
            bool answ = false;
            if (memMap != null && memMap.mMapWrite != null)
            {
                string pKey = "processOtherInfo";
                if (memMap.mMapWrite.ContainsKey(pKey))
                {
                    dataConf currMem = memMap.mMapWrite[pKey];
                    string addr = currMem.memAddr;
                    string compoValue = currMem.ComposedVal;
                    // calcolo il valore da scrivere... provando a cercare i valori tra le chiavi in prodData...
                    foreach (var item in currProdData)
                    {
                        if (compoValue.Contains($"[[{item.Key}]]"))
                        {
                            compoValue = compoValue.Replace($"[[{item.Key}]]", item.Value);
                        }
                    }
                    // imposto il valore e forzo la scrittura ...
                    memMap.mMapWrite[pKey].value = compoValue;
                    // lo compongo da scrivere...
                    // preparo elenco parametri da inviare...
                    list2Write = new List<objItem>();
                    objItem currData = new objItem()
                    {
                        uid = pKey,
                        reqValue = compoValue,
                        name = pKey
                    };
                    list2Write.Add(currData);
                    // aggiungo bit popup
                    currData = new objItem()
                    {
                        uid = "showPopup",
                        reqValue = "true",
                        name = "showPopup"
                    };
                    list2Write.Add(currData);
                    // scrivo!
                    plcWriteParams(ref list2Write);

                    // sctivo nuovo parametro x spegnere popup...
                    try
                    {
                        // PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione
                        Task.Run(async () =>
                        {
                            await Task.Delay(500);
                        })
                        .GetAwaiter()
                        .GetResult();
                    }
                    catch (Exception ex)
                    {
                        lgError($"Errore in ProcessOtherInfo{Environment.NewLine}{ex.Message}");
                    }
                    list2Write.Clear();
                    currData = new objItem()
                    {
                        uid = "showPopup",
                        reqValue = "false",
                        name = "showPopup"
                    };
                    list2Write.Add(currData);
                    // scrivo!
                    plcWriteParams(ref list2Write);
                }
            }
            return answ;
        }

        #endregion Protected Methods

#if false
        /// <summary>
        /// effettua verifica del datablock icoel inviando eventualmente i dati variati
        /// </summary>
        /// <param name="startNodeId"></param>
        /// <param name="blockName"></param>
        /// <param name="currBlock"></param>
        /// <param name="newBlock"></param>
        /// <param name="forceSend"></param>
        /// <returns></returns>
        protected bool testSendDataBlock(NodeId startNodeId, string blockName, MesItemStatus currBlock, MesItemStatus newBlock, bool forceSend)
        {
            bool changed = false;
            // verifica globale blocchi old/new...
            if (!currBlock.Equals(newBlock))
            {
                // creo un nuovo monitoredItem se non ci fosse x ogni variabile dell'oggetto...
                changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Stato", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Stato}", forceSend);
                changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Velocita", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Velocita}", forceSend);
                changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Termico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Termico}", forceSend);
                changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_MagnetoTermico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.MagnetoTermico}", forceSend);
                changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_AvariaInverter", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.AvariaInverter}", forceSend);

                // spostare sotto in checkAndSendRaw ??? FIXME todo
                if (changed)
                {
                    lgTrace($"Invio variazione dataitem per {dataItemMem.Count} elementi");
                    sendDataItemListToServer(startNodeId);
                }
                else
                {
                    lgTrace("Nessuna variazione DataItem da trasmettere");
                }
            }
            return changed;
        }

        /// <summary>
        /// Verifico se salvare e inviare proprietà specificata
        /// </summary>
        /// <param name="MonIt"></param>
        protected bool testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
        {
            bool changed = false;
            DateTime locTStamp = DateTime.Now;
            string sVal = "";
            string descr = "";
            string uuid = calcID($"{MonIt.StartNodeId.Identifier}", $"{MonIt.DisplayName}");
            descr = itemTranslation("OPC", uuid);
            sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}";
            lgDebug($"TSP | {sVal}");

            changed = checkSaveValue(MonIt, NotifyValue, false);
            // cerco se non sia un dato filtrato in FLUXLOG...
            bool isFiltered = opcUaParams.fluxLogVeto.Contains(uuid);
            if (isFiltered)
            {
                lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false);
            }
            else
            {
                if (changed || forceSend)
                {
                    bool sent = accodaFLog(descr, sVal, qEncodeFLog(descr, $"{NotifyValue}"));
                    if (sent)
                    {    // traccio valore DynData x analisi
                        trackDynData(descr, $"{NotifyValue}");
                    }
                }
                else
                {
                    lgTrace($"TSP | NON ACCODATO sample per {uuid} - verifica variazione ha dato esito negativo", false);
                }
            }
            return changed;
        }
#endif
    }
}