using EgwProxy.Icoel;
using EgwProxy.Icoel.SizerService;
using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;

namespace IOB_WIN_WS.IobWs
{
    /// <summary>
    /// Adapter specializzato per ICOEL e le chiamate tramite WS Soap al Sizer, con libreria EgwProxy.Icoel
    /// </summary>
    public class IcoelSoap : Iob.GenericNext
    {
        #region Public Constructors

        /// <summary>
        /// Costruttore dell'IOB Icoel SOAP
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IOBConf">Configurazione (legacy)</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public IcoelSoap(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            /* --------------------------------------
             * todo's
             * --------------------------------------
             * - init obj comunicazione da conf e nuget
             * - test comunicazione
             * - estensione IOB come OPT_PAR di OVERRIDE (x inviare dati di un unico iOB da più IOB programs)
             * - gestione processCustomTaskLF
             *   - x lettura dei 2 batch correnti (sx/dx)
             *   - calcolo batch in corso/chiusi da date inizio/fine
             *   - trasmettere a MP/IO risultato valutazioni
             * - gestione executeTasks
             *   - task di invio batch configurato in coda
             *   - task di recupero info anagrafiche (grower, variety, layout,...)
             * - contapezzi (SE ha senso con sizer oppure saltare)
             */

            IcoelSizer = new Connector(IOBConfFull.Device.Connect.IpAddr, IOBConfFull.Device.Connect.Port);
            lastPING = DateTime.Now.AddHours(-1);
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Implementazione custom esecuzione task specifici
        /// </summary>
        /// <param name="task2exe"></param>
        /// <returns></returns>
        public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe, string codTav)
        {
            /*---------------------------------------
             * fixme todo fare !!!
             * gestione execute task SPECIFICI x il sizer:
             * - recupero anagrafice variety/layout (attivi)
             * - recupero grower
             * - invio batch da accodare
             * - recupero dati da sizer (OVE disponibili)
             * - recupero batch corrente (modalità force/resync?)
             *
             *---------------------------------------*/

            // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti...
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            if (task2exe != null)
            {
                // controllo se memMap != null...
                if (memMap != null)
                {
                    bool taskOk = false;
                    string taskVal = "";
                    // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
                    foreach (var item in task2exe)
                    {
                        taskOk = false;
                        taskVal = "";
                        // converto richiesta in enum...
                        taskType tName = taskType.nihil;
                        Enum.TryParse(item.Key, out tName);
                        // controllo sulla KEY...
                        switch (tName)
                        {
                            case taskType.setSupplier:

                                string cmdArgs = item.Value;
                                taskVal = executeSetSupplier(cmdArgs);
                                break;

                            case taskType.setParameter:
                                // richiedo da URL i parametri WRITE da popolare
                                lgInfo("Chiamata setParameter --> processMemWriteRequests");
                                taskVal = processMemWriteRequests();
                                // se restituiscce "" faccio altra prova...
                                if (string.IsNullOrEmpty(taskVal))
                                {
                                    // i parametri me li aspetto come stringa composta paramName|paramvalue
                                    if (item.Value.Contains("|"))
                                    {
                                        string[] paramsJob = item.Value.Split('|');
                                        taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
                                    }
                                    else
                                    {
                                        taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
                                    }
                                }
                                break;

                            default:
                                taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
                                lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}");
                                break;
                        }
                        // aggiungo task!
                        taskDone.Add(item.Key, taskVal);
                    }
                }
                else
                {
                    lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!");
                }
            }

            return taskDone;
        }

        /// <summary>
        /// Recupero dati dinamici...
        /// </summary>
        public override Dictionary<string, string> getDynData()
        {
            // valore non presente in vers default... se gestito fare override
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            // recupero da libreria il dizionario dei performance parameters...
            perfParamDict = IcoelSizer.GetPerfMeters();
            lgTrace("SOAP: effettuata chiamata IcoelSizer.GetPerfMeters()");
            foreach (var item in perfParamDict)
            {
                saveValue(ref outVal, item.Key, item.Value);
            }
            // ora leggo i batch data
            batchDataDict = IcoelSizer.GetCurrBatchData();
            lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrBatchData()");
            foreach (var item in batchDataDict)
            {
                saveValueString(ref outVal, item.Key, item.Value);
            }

            lastReadPLC = DateTime.Now;
            return outVal;
        }

        /// <summary>
        /// Effettua processing CUSTOM x Icoel:
        /// - recupera elenco batch delle 2 linee
        /// - invia al sistema
        /// </summary>
        public override void processCustomTaskLF()
        {
            lgInfo($"Richiesto processCustomTaskLF");
            var currBatch = IcoelSizer.GetCurrentBatch();
            lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()");
            if (currBatch != null)
            {
                // verifico se i batch siano variati... e quindi da inviare...
                bool doSend = (currBatchList == null || currBatchList.Count == 0);
                if (currBatch.Count > 0 && !doSend)
                {
                    foreach (var item in currBatch)
                    {
                        // se variato ID è cambiato
                        doSend = doSend || (currBatchList[item.Key].Id != item.Value.Id);
                    }
                }
                // se devo inviare impacchetto dati
                if (doSend)
                {
                    accodaRawData(rawTransfType.IcoelBatch, currBatch);
                    currBatchList = currBatch;
                }
            }

            lastReadPLC = 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)
        {
            if (connectionOk)
            {
                B_input = 1;
                currDispData.semIn = Semaforo.SV;
                if (currBatchList == null)
                {
                    // se nullo --> provo a leggere!
                    var currBatch = IcoelSizer.GetCurrentBatch();
                    lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()");
                    if (currBatch != null)
                    {
                        currBatchList = currBatch;
                        accodaRawData(rawTransfType.IcoelBatch, currBatch);
                    }
                }
                else
                {
                    //se i perf parameters mi danno valire > 0 x quantità frutta...
                    if (perfParamDict != null && perfParamDict.Count > 0)
                    {
                        if (perfParamDict.ContainsKey("VelFruttiMinuto"))
                        {
                            if (perfParamDict["VelFruttiMinuto"] > 0)
                            {
                                B_input += (1 << 1);
                            }
                            // metto manuale
                            else
                            {
                                B_input += (1 << 4);
                            }
                        }
                    }

                    //usato altro criterio x stato verde da vel frutti minuto
#if false
                    // se ho batch NON chiusi (data = minValue) allora lavora
                    if (currBatchList[1].EndTime == DateTime.MinValue || currBatchList[1].EndTime == DateTime.MinValue)
                    {
                        B_input = 3;
                    }
#endif
                }
                // accodo NON emergenza
                B_input += (1 << 7);
            }
            else
            {
                B_input = 0;
                currDispData.semIn = Semaforo.SR;
            }
        }

        /// <summary>
        /// Override connessione
        /// </summary>
        public override void tryConnect()
        {
            if (!connectionOk)
            {
                // controllo che il ping sia stato tentato almeno pingTestSec fa...
                if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
                {
                    if (verboseLog || periodicLog)
                    {
                        lgInfo("IcoelSoap: ConnKO - tryConnect");
                    }
                    // in primis salvo data ping...
                    lastPING = DateTime.Now;
                    // se passa il ping faccio il resto...
                    if (testPingMachine == IPStatus.Success)
                    {
                        string szStatusConnection = "";
                        try
                        {
                            // ora provo connessione...
                            parentForm.commPlcActive = true;
                            // recupero elenco batch... se != vuoto -_> connesso!
                            var batchList = IcoelSizer.GetCurrentBatch();
                            lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()");
                            if (batchList != null && batchList.Count > 0)
                            {
                                lgInfo($"szStatusConnection IcoelSoap, recuperato elenco di {batchList.Count} batch");
                                parentForm.commPlcActive = false;
                                connectionOk = true;
                            }
                            // refresh stato connessione!!!
                            if (connectionOk)
                            {
                                queueInEnabCurr = true;
                                if (adpRunning)
                                {
                                    lgInfo("Connessione OK");
                                }
                            }
                            else
                            {
                                lgError("Impossibile procedere, connessione mancante...");
                            }
                        }
                        catch (Exception exc)
                        {
                            lgFatal($"Errore nella connessione all'adapter IcoelSoap: {szStatusConnection}{Environment.NewLine}{exc}");
                            connectionOk = false;
                            lgInfo($"Eccezione in TryConnect, Adapter IcoelSoap NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
                        }
                    }
                    else
                    {
                        // loggo no risposta ping ...
                        connectionOk = false;
                        if (verboseLog || periodicLog)
                        {
                            lgInfo($"Attenzione: IcoelSoap controllo PING fallito per IP {IOBConfFull.Device.Connect.PingIpAddr}");
                        }
                    }
                }
            }
            else
            {
                needRefresh = true;
            }
        }

        public override void tryDisconnect()
        {
            // registro solo che è disconnesso
            connectionOk = false;
            queueInEnabCurr = false;
        }

        #endregion Public Methods

        #region Protected Properties

        /// <summary>
        /// Dizionario dati Batch correnti
        /// </summary>
        protected Dictionary<string, string> batchDataDict { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// elenco dei BAtch correntemente caricati x testing variazione
        /// </summary>
        protected Dictionary<int, Batch> currBatchList { get; set; }

        protected Connector IcoelSizer { get; set; }

        /// <summary>
        /// Dizionario parametri correnti
        /// </summary>
        protected Dictionary<string, double> perfParamDict { get; set; } = new Dictionary<string, double>();

        #endregion Protected Properties

        #region Protected Methods

        /// <summary>
        /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC
        /// </summary>
        /// <param name="updatedPar"></param>
        protected override void plcWriteParams(ref List<objItem> updatedPar)
        {
            foreach (var item in updatedPar)
            {
                // cerco se sia setSupplier
                if (item.uid == "setSupplier")
                {
                    string reqVal = !string.IsNullOrEmpty(item.reqValue) ? item.reqValue : item.value;
                    // chiamo
                    var taskVal = executeSetSupplier(reqVal);
                    bool fatto = taskVal.StartsWith("REQUEST");
                    // se fatto --> aggiorno!
                    if (fatto)
                    {
                        //item.value = item.reqValue;
                        item.reqValue = "";
                        item.lastRead = DateTime.Now;
                        item.UM = "";
                    }
                }
            }
        }

        /// <summary>
        /// recupera le variety ed i rispettivi layout e li invia al sistema MP/IO
        /// </summary>
        protected void refreshVarietyData()
        {
            ///determina se recuperare SOLO varietà attive o tutte
            bool soloAttive = false;
            var varList = IcoelSizer.GetVarietyList(soloAttive);
            lgTrace("SOAP: effettuata chiamata IcoelSizer.GetVarietyList()");
            if (varList != null && varList.Length > 0)
            {
                var varietyData = IcoelSizer.GetLayoutForVarietyList(varList);
                lgTrace("SOAP: effettuata chiamata IcoelSizer.GetLayoutForVarietyList()");
            }

            lastReadPLC = DateTime.Now;
        }

        #endregion Protected Methods

        #region Private Methods

        private string executeSetSupplier(string cmdArgs)
        {
            string taskVal;
            // loggo i parametri ricevuti da popolare
            lgInfo($"Chiamata setSupplier --> params payload: {cmdArgs}");
            // recupero batch corrente x preselezionare i dati guid layout/varietà...
            var currBatch = IcoelSizer.GetCurrentBatch();
            // recupero GUID x variety e layout di default
            var varGuid = currBatch[1].VarietyId;
            var layGuid = currBatch[1].LayoutId;
            // predispongo grower di default
            GrowerInfo GrowerData = new GrowerInfo();
            // le info sono una stringa separata da valore | di GrowerCode|GrowerName|varietyGuid|LayoutGuid;
            string[] reqParams = cmdArgs.Split('|');
            // se ho dati sistemo grower...
            if (reqParams.Length >= 2)
            {
                //GrowerData.GrowerCode = reqParams[0];
                //GrowerData.GrowerName = reqParams[1];
                // sistemo commenti
                var comments = new List<string>();
                comments.Add($"sent {DateTime.Now: yyyy.MM.dd HH:mm:ss}");
                comments.Add("Qty: no set");
                comments.Add("------------");
                //GrowerData.Comments = comments;

                GrowerData = new GrowerInfo()
                {
                    GrowerCode = reqParams[0],
                    GrowerName = reqParams[1],
                    Comments = comments
                };
                lgInfo($"Set grower data | GrowerCode: {GrowerData.GrowerCode} | GrowerName: {GrowerData.GrowerName}");
            }
            if (reqParams.Length >= 3)
            {
                Guid.TryParse(reqParams[2], out varGuid);
                lgInfo($"Set Variety data | VarGUID: {varGuid}");
            }
            if (reqParams.Length >= 4)
            {
                Guid.TryParse(reqParams[3], out layGuid);
                lgInfo($"Set Layout data | LayGUID: {layGuid}");
            }

            // invio richiesta!!!
            IcoelSizer.EnqueueBatch(GrowerData, varGuid, layGuid);
            lgInfo($"Request sent!");

            taskVal = $"REQUEST SET SUPPLIER | EnqueueBatch | GrowCode: {GrowerData.GrowerCode} | GrowName: {GrowerData.GrowerName} | Var: {varGuid} | Lay: {layGuid} ";
            // se restituiscce "" faccio altra prova...
            if (string.IsNullOrEmpty(taskVal))
            {
                // i parametri me li aspetto come stringa composta paramName|paramvalue
                if (cmdArgs.Contains("|"))
                {
                    string[] paramsJob = cmdArgs.Split('|');
                    taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
                }
                else
                {
                    taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {cmdArgs} doesnt contain pipe for splitting key/value";
                }
            }

            return taskVal;
        }

        #endregion Private Methods
    }
}