#if false
using EgwProxy.SqlDb.DbModels; 
#endif
using IOB_UT_NEXT;
using IOB_UT_NEXT.Config;
using MapoSDK;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using static IOB_UT_NEXT.CustomObj;

namespace IOB_WIN_FILE.IobFile
{
    /// <summary>
    /// Adapter specializzato per SOITAAB x la SOLA lettura stato macchina da log, da accoppiare a
    /// adapter x IOB LANTEK x scrittura PODL
    /// - IN: LOGFile macchina diretto
    /// - OUT: --&gt; sigLog
    /// --&gt; fluxLog
    /// </summary>

    public class IobFileSoitaab : Iob.GenericNext
    {
        #region Public Constructors

        /// <summary>
        /// Costruttore dell'IOB FileBased SOITAAB
        /// </summary>
        /// <param name="caller">Form chiamante</param>
        /// <param name="IobConfFull">Configurazione (v 4.x)</param>
        public IobFileSoitaab(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull)
        {
            DateTime adesso = DateTime.Now;
            string VetoReadSec = getOptPar("VetoReadSec");

            setupSpecialParams();
            if (pathList.ContainsKey("path-remBase"))
            {
                logDirPath = pathList["path-remBase"];
            }
            // fix parametri veto
            if (!string.IsNullOrEmpty(VetoReadSec))
            {
                int.TryParse(VetoReadSec, out vetoReadFileSec);
            }

            string sFluxOnRead = getOptPar("sendFluxOnRead");
            if (!string.IsNullOrEmpty("sendFluxOnRead"))
            {
                bool.TryParse(sFluxOnRead, out sendFluxOnRead);
            }

            // init a zero....
            B_input = 0;

            // provo prima lettura logfile
            try
            {
                // eseguo subito un ciclo acquisizione log + processing
                bool acquireLog = getRemoteLog();
                if (acquireLog)
                {
                    bool sentSignLog = processSignLogTable(adesso);
                }
            }
            catch (Exception exc)
            {
                lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}");
            }

            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)
        {
            DateTime adesso = DateTime.Now;
            // NON fa nulla... anche se non dovrebbe richiamarlo
            Dictionary<string, string> taskDone = new Dictionary<string, string>();
            lastReadPLC = DateTime.Now;
            return taskDone;
        }

        /// <summary>
        /// Recupero dati dinamici...
        /// </summary>
        public override Dictionary<string, string> getDynData()
        {
            DateTime adesso = DateTime.Now;
            // dizionario vuoto / faccio direttamente accodamento in FluxLog
            Dictionary<string, string> outVal = new Dictionary<string, string>();
            // processo ed accodo!
            processFluxLogTable(adesso);
            lastReadPLC = DateTime.Now;
            return outVal;
        }

        /// <summary>
        /// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
        /// aggiornare x display in form</paramref>
        /// </summary>
        public override void readSemafori(ref newDisplayData currDispData)
        {
            DateTime adesso = DateTime.Now;
            if (connectionOk)
            {
                // controllo veto checkDB
                if (adesso > vetoDataRead)
                {
                    // predispongo prox veto...
                    vetoDataRead = adesso.AddSeconds(vetoReadFileSec);
                    // semaforo
                    currDispData.semIn = Semaforo.SV;
                    // verifico SignLog e processo
                    bool acquireLog = getRemoteLog();
                    bool sentSignLog = processSignLogTable(adesso);
                    // verifico ProdData e processo
                    bool sentProdData = processProdDataTable(adesso);
                }
            }
            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("FileSoitaab: 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;

                            // connessione OK se oltre al PING riesco a leggere la folder di base
                            if (Directory.Exists(logDirPath))
                            {
                                parentForm.commPlcActive = false;
                                connectionOk = true;
                            }
                            // refresh stato connessione!!!
                            if (connectionOk)
                            {
                                queueInEnabCurr = true;
                                if (adpRunning)
                                {
                                    lgInfo($"Connessione OK alla folder {logDirPath}");
                                    lastReadPLC = DateTime.Now;
                                }
                            }
                            else
                            {
                                lgError("Impossibile procedere, connessione mancante...");
                            }
                        }
                        catch (Exception exc)
                        {
                            lgFatal($"Errore nella connessione all'adapter IobFileSoitaab: {szStatusConnection}{Environment.NewLine}{exc}");
                            connectionOk = false;
                            lgInfo($"Eccezione in TryConnect, Adapter IobFileSoitaab NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
                        }
                    }
                    else
                    {
                        // loggo no risposta ping ...
                        connectionOk = false;
                        B_input = 0;
                        if (verboseLog || periodicLog)
                        {
                            lgInfo($"Attenzione: IobFileSoitaab 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 Fields

        protected bool sendFluxOnRead = false;
        protected int vetoReadFileSec = 3;

        #endregion Protected Fields

        #region Protected Properties

#if false
        /// <summary>
        /// Stato di sync delle tab gestite
        /// </summary>
        protected List<SyncStateModel> elencoSyncState { get; set; } = new List<SyncStateModel>(); 
#endif

        #endregion Protected Properties

        #region Protected Methods

        /// <summary>
        /// Conversione string row in log generico
        /// </summary>
        /// <param name="dailyLog"></param>
        /// <returns></returns>
        protected override GenLogRow convertToMachineLog(string dailyLog)
        {
            GenLogRow answ = new GenLogRow();
            if (!string.IsNullOrEmpty(dailyLog))
            {
                // preventivamente: doppio ";;" --> ";"
                dailyLog = dailyLog.Replace(";;", ";");
                var sSplit = dailyLog.Split(';');
                answ.dtRif = DateTime.ParseExact($"{sSplit[0]} {sSplit[1]}", "yyyy-M-d HH:mm:ss", null);
                answ.valString = $"{sSplit[2]};{sSplit[3]}";
            }
            return answ;
        }

        /// <summary>
        /// Recupera file log da analizzare
        /// </summary>
        /// <returns></returns>
        protected override bool getRemoteLog()
        {
            bool answ = false;
            bool pingOk = testPingMachine == IPStatus.Success;
            if (pingOk && Directory.Exists(logDirPath))
            {
                string filePath = Path.Combine(logDirPath, $"Report-{DateTime.Today.Day}.txt");
                bool hasDaily = File.Exists(filePath);
                // cerco file quotidiano...
                if (!hasDaily)
                {
                    var fileList = Directory.GetFiles(logDirPath, "Report-*.txt");
                    DateTime lastMod = DateTime.Today.AddYears(-1);
                    foreach (var cFile in fileList)
                    {
                        var tempFile = Path.Combine(logDirPath, cFile);
                        var lwTime = File.GetLastWriteTime(tempFile);
                        if (lwTime >= lastMod)
                        {
                            lastMod = lwTime;
                            filePath = tempFile;
                        }
                    }
                }
                // leggo file
                string rawVal = File.ReadAllText(Path.Combine(logDirPath, filePath));
                if (!string.IsNullOrEmpty(rawVal))
                {
                    // salvo in redis
                    string redKey = redisMan.redHash($"IOB:CurrData:{IOBConfFull.General.CodIOB}:LogFile:Act");
                    redisMan.setRSV(redKey, rawVal);
                    answ = true;
                }
            }
            return answ;
        }

        #endregion Protected Methods

        #region Private Properties

        private string logDirPath { get; set; } = "\\\\ecs900\\Logs";

        #endregion Private Properties

        #region Private Methods

        private static int decodeSoitaabLog(string val2test)
        {
            // di default NON EMERGENZA...
            int valInt = 128;
            switch (val2test)
            {
                case "START;1":
                    valInt = 1 + 2 + 128;
                    break;

                case "END;1":
                    valInt = 1 + 128;
                    break;

                case "START;0":
                case "END;0":
                case "HOLD;":
                    valInt = 1 + 16 + 128;
                    break;

                default:
                    break;
            }

            return valInt;
        }

        /// <summary>
        /// Esegue processing + invio dati tab SignLog
        /// </summary>
        /// <param name="adesso"></param>
        /// <returns></returns>
        private bool processFluxLogTable(DateTime adesso)
        {
            bool fatto = false;
            // leggo eventuali dati di fluxlog ed invio...
            if (fluxLogData != null && fluxLogData.Count > 0)
            {
                string sVal = "";
                foreach (var fLog2send in fluxLogData)
                {
                    var kvpFlux = fLog2send.valString.Split(';');
                    if (kvpFlux.Count() > 1)
                    {
                        sVal = $"[DYNDATA] |{fLog2send.dtRif:yyyy-MM-dd HH:mm:ss}|{kvpFlux[0]}|{kvpFlux[1]}";
                        // chiamo accodamento con dataora corretta...
                        bool sent = accodaFLog(kvpFlux[0], sVal, qEncodeFLog(fLog2send.dtRif, kvpFlux[0], kvpFlux[1]));
                        if (sent)
                        {
                            trackDynData(kvpFlux[0], kvpFlux[1]);
                        }
                    }
                }
                // svuoto coda invio fluxlog...
                fluxLogData = new List<GenLogRow>();
                // fatto!
                fatto = true;
            }
            return fatto;
        }

        /// <summary>
        /// Esegue processing + invio dati tab ProdData
        /// </summary>
        /// <param name="adesso"></param>
        /// <returns></returns>
        private bool processProdDataTable(DateTime adesso)
        {
            bool fatto = false;
            return fatto;
        }

        /// <summary>
        /// Esegue processing + invio dati tab SignLog
        /// </summary>
        /// <param name="adesso"></param>
        /// <returns></returns>
        private bool processSignLogTable(DateTime adesso)
        {
            bool fatto = false;
            // init oggetto x processing
            Dictionary<DateTime, int> sigLogFromFile = new Dictionary<DateTime, int>();
            // recupero i dati dai logs files, attuale e nuovo...
            string fileAct = redisMan.getRSV(redKeyLogfileAct);
            string fileLast = redisMan.getRSV(redKeyLogfileLast);
            // confronto con i dati dell'ultimo LOG FILE processato
            int lenghtAct = fileAct != null ? fileAct.Length : 0;
            int lenghtLast = fileLast != null ? fileLast.Length : 0;
            //if (lenghtAct > 0 && lenghtAct != lenghtLast)
            if (lenghtAct > 0)
            {
                List<GenLogRow> logNew = new List<GenLogRow>();
                List<GenLogRow> sigLogData = new List<GenLogRow>();
                List<GenLogRow> logLast = getGenLogFromMachineLog(fileLast);
                List<GenLogRow> logAct = getGenLogFromMachineLog(fileAct);
                // SE c'è prendo ultima riga inviata x confronto...
                var fileLastEnd = logLast.LastOrDefault();
                if (fileLastEnd != null)
                {
                    // ora prendo SOLAMENTE le righe nuove
                    logNew = logAct
                                    .Where(x => x.dtRif > fileLastEnd.dtRif)
                                    .OrderBy(x => x.dtRif)
                                    .ToList();
                }
                else
                {
                    logNew = logAct;
                }
                // solo SE ho nuovi dati....
                if (logNew.Count > 0)
                {
                    // processo le righe nuove e le accodo in redis come elenco FluxLog da inviare
                    // (appoggio in redis)... andando ad accodarle...
                    var currFLD = fluxLogData;
                    currFLD.AddRange(logNew);
                    fluxLogData = currFLD;

                    // estraggo solo info x sig log
                    sigLogData = logNew
                                .Where(x => x.valString.StartsWith("START") || x.valString.StartsWith("HOLD") || x.valString.StartsWith("END"))
                                .OrderBy(x => x.dtRif)
                                .ToList();

                    // processo le righe nuove trasformando al volo SOLO eventi...
                    foreach (var sLog2send in sigLogData)
                    {
                        /* -----------------------------------------------------
                        *   bitmap MAPO STANDARD 60
                        *   B0: 001 POWER_ON
                        *   B1: 002 RUN
                        *   B2: 004 pzCount
                        *   B3: 008 allarme
                        *   B4: 016 manuale
                        *   B5: 032 slowTC
                        *   B6: 064 WarmUpCoolDown
                        *   B7: 128 EmergArmata
                        *
                        ----------------------------------------------------- */

                        int valInt = decodeSoitaabLog(sLog2send.valString);

                        string currVal = getEncodSigLog(sLog2send.dtRif, valInt, counterSigIN);
                        // verifico non sia in veto invio iniziale...
                        if (queueInEnabCurr)
                        {
                            // --> accodo (valore già formattato)!
                            QueueIN.Enqueue(currVal);
                            // loggo!
                            lgTrace(string.Format("[QUEUE-IN] {0}", currVal));
                            counterSigIN++;
                            if (counterSigIN > 9999)
                            {
                                counterSigIN = 0;
                            }
                        }
                        else
                        {
                            lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
                            checkVetoQueueIn();
                        }
                    }

                    /* ----------------------------------------------------------
                     * processo righe x costruire una lista di eventi produzione:
                     * - elenco di eventi contapezzi
                     * - lista xODL come articolo + quantità prodotta (_1, _2, ...)
                     * - eventuale ultimo xODL che resta aperto
                     *
                     * successivamente processing + invio info raccolte
                     * - recupero PODL aperti
                     * - apertura/chiusura PODL esistenti
                     * - creazione PODL mancanti
                     * - dichiarazione contapezzi
                     *
                     * ---------------------------------------------------------- */

                    string sVal = "";
                    string descrArt = "";
                    List<ProdBatchData> elencoProdBatch = new List<ProdBatchData>();
                    ProdBatchData lastProdBatch = new ProdBatchData();

                    // ora processo TUTTO x il generico caso fluxLog......
                    foreach (var fLog2send in logNew)
                    {
                        // separo per ";"
                        var currData = fLog2send.valString.Split(';');
                        if (currData.Length > 1)
                        {
                            if (sendFluxOnRead)
                            {
                                // per prima cosa fluxLog a prescindere...
                                sVal = $"[LOGFILE]{currData[0]}|{currData[1]}";
                                // ...e chiamo accodamento
                                bool sent = accodaFLog(currData[0], sVal, qEncodeFLog(fLog2send.dtRif, currData[0], currData[1]));
                                if (sent)
                                {
                                    // traccio valore DynData x analisi
                                    trackDynData(currData[0], currData[1]);
                                }
                            }

                            switch (currData[0])
                            {
                                case "END":
                                    // se è un end --> è comunque NON + runnning
                                    isRunningState = false; // chiudo batch precedente + in elenco (SE era valido...)
                                    if (!string.IsNullOrEmpty(lastProdBatch.codArt))
                                    {
                                        lastProdBatch.dtEnd = fLog2send.dtRif;
                                        elencoProdBatch.Add(lastProdBatch);
                                    }
                                    // reset articolo..
                                    lastArtDescr = "";
                                    // nuovo batch VUOTO
                                    lastProdBatch = new ProdBatchData();
                                    contapezziPLC = 0;
                                    break;

                                case "START":
                                    isRunningState = currData[1] == "1";
                                    break;

                                case "PARTCODE":
                                    // inizio confronto articolo con precedente x capire se sia NUOVO
                                    descrArt = currData[1];
                                    // devo scartare i "doppi percorsi"
                                    if (descrArt != lastArtDescr)
                                    {
                                        // verifico se sia DAVVERO cambiato articolo... prendo
                                        // articolo senza indice (_1, _2, _3, ...)
                                        var artDataLast = lastArtDescr.Split('_');
                                        var artDataNew = descrArt.Split('_');
                                        // nuovo articolo (NON vuoto)
                                        if (!string.IsNullOrEmpty(artDataNew[0]) && artDataLast[0] != artDataNew[0])
                                        {
                                            // chiudo batch precedente + in elenco (SE era valido...)
                                            if (!string.IsNullOrEmpty(lastProdBatch.codArt))
                                            {
                                                // chiudo prod
                                                lastProdBatch.dtEnd = fLog2send.dtRif;
                                                elencoProdBatch.Add(lastProdBatch);
                                            }
                                            // nuovo batch
                                            lastProdBatch = new ProdBatchData()
                                            {
                                                codArt = artDataNew[0],
                                                dtStart = fLog2send.dtRif,
                                                numPz = 1
                                            };
                                        }
                                        // solo nuova copia articolo
                                        else
                                        {
                                            // aumento numPezzi batch
                                            lastProdBatch.numPz++;
                                        }

                                        // salvo nuovo art processato
                                        lastArtDescr = descrArt;
                                    }
                                    break;

                                default:
                                    break;
                            }
                        }
                    }

                    // recupero elenco PODL corrente...
                    List<PODLModel> reqPOdlList = MachineNextPodl();
                    // adesso processo tutti i prodBatch collezionati x invio
                    foreach (var item in elencoProdBatch)
                    {
                        int currIdxPOdl = 0;
                        // cerco se articolo sia in elenco PODL
                        var listPOdl = reqPOdlList
                                        .Where(x => x.CodArticolo.Equals(item.codArt, StringComparison.InvariantCultureIgnoreCase))
                                        .ToList();

                        // se trovato --> invio start/stop PODL
                        if (listPOdl != null && listPOdl.Count > 0)
                        {
                            var currPodl = listPOdl.FirstOrDefault();
                            if (currPodl != null)
                            {
                                currIdxPOdl = currPodl.IdxPromessa;
                                // elimino da elenco PODL
                                reqPOdlList.Remove(currPodl);
                            }
                        }
                        else
                        {
                            // se NON trovato --> creo PODL, poi avvio/chiudo
                            currIdxPOdl = TryCreatePodl(item.codArt, CodGruppoIob, item.numPz);
                        }
                        // se idxPOdl valido
                        if (currIdxPOdl > 0)
                        {
                            // mando apertura PODL
                            SendStartPodl(currIdxPOdl, item.dtStart);
                            // chiudo PODL...
                            if (item.dtEnd != null)
                            {
                                // invio pezzi 3 sec prima di chiusura
                                SendPzIncrAtDate(item.numPz, ((DateTime)item.dtEnd).AddSeconds(-3));
                                // chiudo
                                SendClosePOdl(currIdxPOdl, (DateTime)item.dtEnd);
                            }
                        }
                    }

                    // salvo in last il valore di act...
                    redisMan.setRSV(redKeyLogfileLast, fileAct);

                    // calcolo B_input valido
                    var lastRec = sigLogData.LastOrDefault();
                    if (lastRec != null)
                    {
                        // salvo in B_input ultimo valore letto...
                        int lastValInt = decodeSoitaabLog(lastRec.valString);
                        B_input = lastValInt;
                    }
                }
                fatto = true;
            }
            return fatto;
        }

        #endregion Private Methods
    }
}