using MapoSDK;
using System;
using System.Collections.Generic;
using System.Linq;

namespace IOB_UT_NEXT
{
    /// <summary>
    /// informazioni di produzione
    /// </summary>
    public struct prodData
    {
        #region Public Fields

        public int AccTime;
        public bool EmrStop;
        public string FuncMode;
        public string MessageCode;
        public string MessageText;
        public string Operator;

        public int Power;
        public bool Status;

        #endregion Public Fields
    }

    /// <summary>
    /// Gestione Endianness
    /// </summary>
    public static class Endian
    {
        #region Public Methods

        /// <summary>
        /// Scambia MSB/LSB per 16bit
        /// </summary>
        /// <param name="inValue"></param>
        /// <returns></returns>
        public static UInt16 SwapUInt16(UInt16 inValue)
        {
            return (UInt16)(((inValue & 0xff00) >> 8) |
                     ((inValue & 0x00ff) << 8));
        }

        /// <summary>
        /// Scambia MSB/LSB per 32bit
        /// </summary>
        /// <param name="inValue"></param>
        /// <returns></returns>
        public static UInt32 SwapUInt32(UInt32 inValue)
        {
            return ((inValue & 0xff000000) >> 24) |
                     ((inValue & 0x00ff0000) >> 8) |
                     ((inValue & 0x0000ff00) << 8) |
                     ((inValue & 0x000000ff) << 24);
        }

        #endregion Public Methods
    }

    /// <summary>
    /// Elenco task di tipo FTP
    /// </summary>
    public class FtpTaskList
    {
        public List<FtpActConf> ListTask { get; set; } = new List<FtpActConf>();
    }

    /// <summary>
    /// Elenco task generici
    /// </summary>
    public class GenTaskList
    {
        public List<GenActConf> ListTask { get; set; } = new List<GenActConf>();
    }

    /// <summary>
    /// Gestione dati di timing
    /// </summary>
    public static class TimingData
    {
        #region Public Fields

        public static List<TimeRec> results = new List<TimeRec>();

        #endregion Public Fields

        #region Public Methods

        /// <summary>
        /// aggiorno vettore aggiungendo risultato
        /// </summary>
        /// <param name="caller">Codice chiamante</param>
        /// <param name="codice">Codice da registrare (univoco con chiamante)</param>
        /// <param name="ticks">Tempo esecuzione in ticks</param>
        public static void addResult(string caller, string codice, long ticks)
        {
            if (results.Count == 0)
            {
                results.Add(new TimeRec(caller, codice, ticks));
            }
            int indice = -1;
            for (int i = 0; i < results.Count; i++)
            {
                // se il codice è quello cercato...
                if (results[i].codCall == codice && results[i].classCall == caller)
                {
                    indice = i;
                }
            }
            // se c'è aggiorno...
            if (indice >= 0)
            {
                results[indice].numCall++;
                results[indice].totMsec = results[indice].totMsec.Add(new TimeSpan(ticks));
            }
            // altrimenti aggiungo...
            else
            {
                results.Add(new TimeRec(caller, codice, ticks));
            }
        }

        /// <summary>
        /// Resetta i dati registrati (ad avvio adapter...)
        /// </summary>
        public static void resetData()
        {
            results = new List<TimeRec>();
        }

        #endregion Public Methods
    }

    /// <summary>
    /// Cache a tempo valori INT
    /// </summary>
    public class CachedInt
    {
        #region Public Properties

        public DateTime ValidUntil { get; set; } = DateTime.Now;
        public int Value { get; set; } = 0;

        #endregion Public Properties
    }

    /// <summary>
    /// Cache a tempo valori String
    /// </summary>
    public class CachedString
    {
        #region Public Properties

        public DateTime ValidUntil { get; set; } = DateTime.Now;
        public string Value { get; set; } = "";

        #endregion Public Properties
    }

    /// <summary>
    /// Classe conf x item DynData
    /// </summary>
    public class DynDataItem
    {
        #region Public Fields

        /// <summary>
        /// DataOra scadenza invio forzato
        /// </summary>
        public DateTime DTScad = DateTime.Now;

        #endregion Public Fields

        #region Public Properties

        /// <summary>
        /// Valore effettivo da salvare
        /// </summary>
        public string actVal { get; set; } = "";

        public string func { get; set; } = "";
        public string key { get; set; } = "";
        public int maxVal { get; set; }
        public int minVal { get; set; }
        public string name { get; set; } = "";
        public int sPeriod { get; set; } = 60;
        public string unit { get; set; } = "";
        public string val { get; set; } = "";

        #endregion Public Properties
    }

    /// <summary>
    /// Configurazione per Eventi/Variabili
    /// </summary>
    public class EVData
    {
        #region Public Fields

        /// <summary>
        /// DataOra scadenza invio forzato
        /// </summary>
        public DateTime DTScad = DateTime.Now;

        #endregion Public Fields

        #region Public Properties

        /// <summary>
        /// Unità di misura
        /// </summary>
        public string UM { get; set; } = "num";

        /// <summary>
        /// Valore salvato
        /// </summary>
        public string Val { get; set; } = "";

        #endregion Public Properties
    }

    /// <summary>
    /// Elenco oggetti del monitoraggio (DynData, Status) per WPS
    /// </summary>
    public class MonitoredItemsConf
    {
        #region Public Properties

        public List<DynDataItem> DynData { get; set; }
        public srvData SrvData { get; set; }
        public List<StatusItem> Status { get; set; }

        #endregion Public Properties
    }

    /// <summary>
    /// Classe che contiene tutte le NUOVE informazioni da aggiornare sulla form
    /// </summary>
    public class newDisplayData
    {
        #region Public Properties

        /// <summary>
        /// Oggetto COUTNER generico (pezzi, portata...)
        /// </summary>
        public int counter { get; set; } = -9999;

        /// <summary>
        /// Bitmap attuale segnali letti
        /// </summary>
        public string currBitmap { get; set; } = "";

        /// <summary>
        /// Verifica se contenga valori (NON default/empty)
        /// </summary>
        public bool hasData
        {
            get
            {
                bool answ = false;
                // true se qualcosa NON E' come default
                if (!string.IsNullOrWhiteSpace(newInData) || !string.IsNullOrWhiteSpace(newSignalData) || !string.IsNullOrWhiteSpace(newFLogData) || !string.IsNullOrWhiteSpace(newUrlCallData) || !string.IsNullOrWhiteSpace(newLiveLogData) || counter > -9999 || !string.IsNullOrWhiteSpace(currBitmap) || semIn != Semaforo.ND || semOut != Semaforo.ND)
                {
                    answ = true;
                }
                return answ;
            }
        }

        /// <summary>
        /// Dati tipo FluxLog
        /// </summary>
        public string newFLogData { get; set; } = "";

        /// <summary>
        /// Dati tipo IN (RAW)
        /// </summary>
        public string newInData { get; set; } = "";

        /// <summary>
        /// Dati tipo LiveLog
        /// </summary>
        public string newLiveLogData { get; set; } = "";

        /// <summary>
        /// Dati tipo Signal
        /// </summary>
        public string newSignalData { get; set; } = "";

        /// <summary>
        /// Dati tipo UrlCall
        /// </summary>
        public string newUrlCallData { get; set; } = "";

        /// <summary>
        /// Stato semaforo IN verso PLC
        /// </summary>
        public Semaforo semIn { get; set; } = Semaforo.ND;

        /// <summary>
        /// Stato semaforo OUT verso MES
        /// </summary>
        public Semaforo semOut { get; set; } = Semaforo.ND;

        #endregion Public Properties
    }

    /// <summary>
    /// Dato generico (per decodifica)
    /// </summary>
    public class otherData
    {
        #region Public Fields

        public string codNum;
        public string dataType;
        public string memAddr;
        public string varName;

        #endregion Public Fields

        #region Public Constructors

        public otherData()
        {
            codNum = "";
            memAddr = "";
            varName = "";
            dataType = "";
        }

        public otherData(string _codNum, string _memAddr, string _varName, string _dataType)
        {
            codNum = _codNum;
            memAddr = _memAddr;
            varName = _varName;
            dataType = _dataType;
        }

        #endregion Public Constructors
    }

    /// <summary>
    /// Classe gestione valori campionati su periodo
    /// </summary>
    public class sampleVect
    {
        #region Public Constructors

        /// <summary>
        /// Inizializzo l'oggetto
        /// </summary>
        public sampleVect()
        {
            // init valori default...
            windSize = baseUtils.CRI("countWindSize") > 0 ? baseUtils.CRI("countWindSize") : 60;
            lTime = new List<DateTime>();
            lVal = new List<int>();
        }

        #endregion Public Constructors

        #region Public Properties

        /// <summary>
        /// Calcola il valore mediano...
        /// </summary>
        public double vcMedian
        {
            get
            {
                double answ = 0;
                // restituisce la mediana SE valida, altrimenti null...
                if (numElem > 2 && flWindSize > windSize)
                {
                    try
                    {
                        // calcolo mediana!
                        //answ = Statistics.Median(lVal.ToArray());

                        // rif: https://blogs.msmvps.com/deborahk/linq-mean-median-and-mode/
                        var sortedNumbers = lVal.OrderBy(n => n);
                        int numCount = lVal.Count;
                        int indice50 = lVal.Count / 2;
                        if ((numCount % 2) == 0)
                        {
                            answ = ((sortedNumbers.ElementAt(indice50) + sortedNumbers.ElementAt(indice50 - 1)) / 2);
                        }
                        else
                        {
                            answ = sortedNumbers.ElementAt(indice50);
                        }
                    }
                    catch
                    { }
                }
                return answ;
            }
        }

        /// <summary>
        /// Verifica se la vc sia valida (ovvero almeno 2 valori e intervallo &gt; window richiesta)
        /// </summary>
        public bool vcValid
        {
            get
            {
                return (flWindSize > windSize && numElem > 1);
            }
        }

        #endregion Public Properties

        #region Public Methods

        /// <summary>
        /// Aggiunge un valore alla serie ed eventualmente elimina i valori superflui a garantirne
        /// una finestra temporale valida
        /// </summary>
        /// <param name="tempo"></param>
        /// <param name="valore"></param>
        public void addValue(DateTime tempo, int valore)
        {
            lTime.Add(tempo);
            lVal.Add(valore);
            // verifico se siano da accorciare le serie... ovvero i 2 intervalli ENTRAMBI sono
            // superiori al periodo minimo (in tal caso riduco..
            while (flWindSize > windSize && slWindSize > windSize)
            {
                // elimino i 2 valori + vecchi
                lTime.RemoveAt(0);
                lVal.RemoveAt(0);
                // ora ricontrollo...
            }
        }

        #endregion Public Methods

        #region Protected Fields

        /// <summary>
        /// vettore valori temporali della serie
        /// </summary>
        protected List<DateTime> lTime;

        /// <summary>
        /// vettore valori puntuali della serie
        /// </summary>
        protected List<int> lVal;

        /// <summary>
        /// Dimensione finestra di campionamento (secondi)
        /// </summary>
        protected int windSize;

        #endregion Protected Fields

        #region Protected Properties

        /// <summary>
        /// Verifica ampiezza finestra valori First-Last
        /// </summary>
        protected double flWindSize
        {
            get
            {
                double answ = 0;
                if (numElem > 1)
                {
                    answ = lTime.Last().Subtract(lTime[0]).TotalSeconds;
                }
                return answ;
            }
        }

        /// <summary>
        /// Conteggio elementi
        /// </summary>
        protected int numElem
        {
            get
            {
                int answ = 0;
                try
                {
                    answ = lTime.Count;
                }
                catch
                { }
                return answ;
            }
        }

        /// <summary>
        /// Verifica ampiezza finestra valori Second-Last
        /// </summary>
        protected double slWindSize
        {
            get
            {
                double answ = 0;
                if (numElem > 2) // altrimenti SE non ne ho almeno 3 NON posso avere secondo/ultimo...
                {
                    answ = lTime.Last().Subtract(lTime[1]).TotalSeconds;
                }
                return answ;
            }
        }

        #endregion Protected Properties
    }

    /// <summary>
    /// Classe x descrivere status server MP
    /// </summary>
    public class ServerMpStatus
    {
        #region Public Properties

        /// <summary>
        /// IP server
        /// </summary>
        public string IP { get; set; }

        /// <summary>
        /// DataOra ultima comunicazione OUT (con MP Server)
        /// </summary>
        public DateTime lastUpdate { get; set; } = DateTime.Now.AddDays(-1);

        /// <summary>
        /// Status del server
        /// </summary>
        public bool online { get; set; } = false;

        #endregion Public Properties
    }

    /// <summary>
    /// Classe conf server html
    /// </summary>
    public class srvData
    {
        #region Public Properties

        public string baseUri { get; set; } = "";
        public string driverName { get; set; } = "";

        #endregion Public Properties
    }

    /// <summary>
    /// Classe conf x decodifica status
    /// </summary>
    public class StatusItem : DynDataItem
    {
        #region Public Fields

        public Dictionary<string, string> codeMapping;

        #endregion Public Fields
    }

    /// <summary>
    /// Oggetto timing x archiviazione dati perfomances
    /// </summary>
    public class TimeRec
    {
        #region Public Fields

        /// <summary>
        /// Classe chiamante della funzione (es codice univoco IOB)
        /// </summary>
        public string classCall;

        /// <summary>
        /// Codice univoco chiamata: tipo R4 (read 4 byte), W2 (write 2 Byte)
        /// </summary>
        public string codCall;

        /// <summary>
        /// Num chiamate totale
        /// </summary>
        public int numCall;

        /// <summary>
        /// Totale Msec accumulati
        /// </summary>
        public TimeSpan totMsec;

        #endregion Public Fields

        #region Public Constructors

        /// <summary>
        /// Classe record timing
        /// </summary>
        public TimeRec()
        {
            codCall = "";
            numCall = 0;
            totMsec = new TimeSpan(0);
        }

        /// <summary>
        /// Classe record timing
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="codice"></param>
        /// <param name="ticks"></param>
        public TimeRec(string caller, string codice, long nTicks)
        {
            classCall = caller;
            codCall = codice;
            numCall = 1;
            totMsec = new TimeSpan(nTicks);
        }

        #endregion Public Constructors

        #region Public Properties

        /// <summary>
        /// Tempo medio chiamata
        /// </summary>
        public double avgMsec
        {
            get
            {
                return totMsec.TotalMilliseconds / numCall;
            }
        }

        #endregion Public Properties
    }

    /// <summary>
    /// Configurazione per Variabili Casuali
    /// </summary>
    public class VCData
    {
        #region Public Fields

        /// <summary>
        /// Array dati per calcolo
        /// </summary>
        public List<double> dataArray;

        /// <summary>
        /// DataOra inizio periodo di elaborazione
        /// </summary>
        public DateTime DTStart;

        #endregion Public Fields

        #region Public Properties

        /// <summary>
        /// Tipologia di funzione da applicare
        /// </summary>
        public VC_func Funzione { get; set; } = VC_func.POINT;

        /// <summary>
        /// Periodo di riferimento
        /// </summary>
        public int Period { get; set; } = 60;

        /// <summary>
        /// UM parametro, impiegato anche x conversione (es epoch --&gt; datetime)
        /// </summary>
        public string UM { get; set; } = "";

        #endregion Public Properties
    }

    /// <summary>
    /// Generico evento log
    /// </summary>
    public class GenLogRow
    {
        public DateTime dtRif { get; set; } = DateTime.Now;

        public string valString { get; set; } = "";
    }


    /// <summary>
    /// Informazioni generiche su batch produzione rilevati
    /// </summary>
    public class ProdBatchData
    {
        public DateTime dtStart { get; set; } = DateTime.Now;
        public DateTime? dtEnd { get; set; } = null;

        public string codArt { get; set; } = "";
        public int numPz { get; set; } = 0;
    }
}