using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
using NLog;

namespace MTC
{
    public class baseUtils
    {
        /// <summary>
        /// classe logger
        /// </summary>
        public static Logger lg;

        /// <summary>
        /// legge conf in formato char
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static char CRC(string key)
        {
            char answ = '-';
            try
            {
                answ = ConfigurationManager.AppSettings[key].ToCharArray()[0];
            }
            catch
            { }
            return answ;
        }
        /// <summary>
        /// legge conf in formato stringa
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string CRS(string key)
        {
            string answ = "";
            try
            {
                answ = ConfigurationManager.AppSettings[key].ToString();
            }
            catch
            { }
            return answ;
        }
        /// <summary>
        /// legge conf in formato INT
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static Int32 CRI(string key)
        {
            int answ = 0;
            try
            {
                answ = Convert.ToInt32(CRS(key));
            }
            catch
            { }
            return answ;
        }
        /// <summary>
        /// legge conf in formato BOOLean
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool CRB(string key)
        {
            bool answ = false;
            try
            {
                answ = Convert.ToBoolean(CRS(key));
            }
            catch
            { }
            return answ;
        }
        /// <summary>
        /// verifica se un dato bit sia alzato (come flag di strobe)
        /// </summary>
        /// <param name="value">valore da testare</param>
        /// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità AND</param>
        /// <returns></returns>
        public static bool IsSetAll(Strobe value, Strobe flag)
        {
            return ((value & flag) == flag);
        }
        /// <summary>
        /// verifica se un dato bit sia alzato (come flag di strobe)
        /// </summary>
        /// <param name="value">valore da testare</param>
        /// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità OR</param>
        /// <returns></returns>
        public static bool IsSetAny(Strobe value, Strobe flag)
        {
            return ((value & flag) != 0);
        }
        /// <summary>
        /// verifica se un dato bit sia alzato (come flag di strobe)
        /// </summary>
        /// <param name="value">valore da testare</param>
        /// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità AND</param>
        /// <returns></returns>
        public static bool IsSetAll(StatusBitMap value, StatusBitMap flag)
        {
            return ((value & flag) == flag);
        }
        /// <summary>
        /// verifica se un dato bit sia alzato (come flag di strobe)
        /// </summary>
        /// <param name="value">valore da testare</param>
        /// <param name="flag">valore cercato, può essere un singolo valore o un insieme in modalità OR</param>
        /// <returns></returns>
        public static bool IsSetAny(StatusBitMap value, StatusBitMap flag)
        {
            return ((value & flag) != 0);
        }
        /// <summary>
        /// formatta un numero in forma binaria 0/1
        /// </summary>
        /// <param name="valore"></param>
        /// <returns></returns>
        public static string binaryForm(int valore)
        {
            string answ = "";
            try
            {
                answ = string.Format(new BinaryFormatter(), "{0:B}", valore);
            }
            catch
            { }
            return answ;
        } 
        /// <summary>
        /// imposta un bit al valore richiesto duplicando il valore IN come OUT
        /// </summary>
        /// <param name="original">valore originale da aggiornare</param>
        /// <param name="bitBool">valore richiesto x il bit (0/1)</param>
        /// <param name="bitIndex">indice bit, 0 based (es: 0..31 per 32bit)</param>
        /// <returns></returns>
        public static byte[] setBitOnStFlag(byte[] original, bool bitBool, int bitIndex)
        {
            int bitVal = 0;
            if (bitBool) bitVal = 1;
            // risposta è identica ad originale...
            byte[] answ = original;
            // verifico se il bit è 0/1b
            if (bitVal <= 1 && bitVal >= 0)
            {
                // verifico se si possa aggiornare il bit richiesto (<= al totale dei bit...)
                if (bitIndex <= original.Length * 8 - 1)
                {
                    // calcolo byte
                    int byteIndex = bitIndex / 8;
                    // bit nel byte
                    int bitInByteIndex = bitIndex % 8;
                    // bit richiesto
                    byte mask = (byte)(bitVal << bitInByteIndex);
                    // imposto!
                    answ[byteIndex] |= mask;
                }
            }
            return answ;
        }
        /// <summary>
        /// Converte un bitarray a byte[]
        /// </summary>
        /// <param name="bits"></param>
        /// <returns></returns>
        public static byte[] ToByteArray(BitArray bits)
        {
            int numBytes = bits.Count / 8;
            if (bits.Count % 8 != 0) numBytes++;

            byte[] bytes = new byte[numBytes];
            int byteIndex = 0, bitIndex = 0;

            for (int i = 0; i < bits.Count; i++)
            {
                if (bits[i])
                    bytes[byteIndex] |= (byte)(1 << (7 - bitIndex));

                bitIndex++;
                if (bitIndex == 8)
                {
                    bitIndex = 0;
                    byteIndex++;
                }
            }

            return bytes;
        }

        /// <summary>
        /// Scrittura dictionary su file
        /// </summary>
        /// <param name="dictionary"></param>
        /// <param name="file"></param>
        public static void WriteBin(Dictionary<string, string> dictionary, string file)
        {
            using (FileStream fs = File.OpenWrite(file))
            using (BinaryWriter writer = new BinaryWriter(fs))
            {
                // Put count.
                writer.Write(dictionary.Count);
                // Write pairs.
                foreach (var pair in dictionary)
                {
                    writer.Write(pair.Key);
                    writer.Write(pair.Value);
                }
            }
        }
        /// <summary>
        /// Lettura dictionary da file
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static Dictionary<string, string> ReadBin(string file)
        {
            var result = new Dictionary<string, string>();
            // verifico file esista...
            if (!File.Exists(file))
            {
                FileStream fs = File.Create(file);
                fs.Close();
            }
            using (FileStream fs = File.OpenRead(file))
            using (BinaryReader reader = new BinaryReader(fs))
            {
                // Get count.
                int count = 0;
                try
                {
                    count = reader.ReadInt32();
                }
                catch
                { }
                // Read in all pairs.
                for (int i = 0; i < count; i++)
                {
                    string key = reader.ReadString();
                    string value = reader.ReadString();
                    result[key] = value;
                }
            }
            return result;
        }

        /// <summary>
        /// Scrittura dictionary su file
        /// </summary>
        /// <param name="dictionary"></param>
        /// <param name="file"></param>
        public static void WritePlain(Dictionary<string, string> dictionary, string file)
        {
            string dirPath = file.Substring(0, file.LastIndexOf('\\'));
            // verifico directory
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            string[] lines = dictionary.OrderBy(i => i.Key).Select(kvp => kvp.Key + ":" + kvp.Value).ToArray();
            try
            {
                File.WriteAllLines(file, lines);
            }
            catch (Exception exc)
            {
                lg.Info(exc, string.Format("Errore in scrittura file {0}", file));
            }
        }
        /// <summary>
        /// Lettura dictionary da file
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static Dictionary<string, string> ReadPlain(string file)
        {
            // inizializzo num righe lette...
            int numRow = 0;
            var result = new Dictionary<string, string>();
            // verifico file esista...
            if (!File.Exists(file))
            {
                FileStream fs = File.Create(file);
                fs.Close();
            }
            try
            {
                string[] lines = File.ReadAllLines(file);
                result = lines.Select(l => l.Split(':')).ToDictionary(a => a[0], a => a[1]);
                numRow = result.Count;
            }
            catch
            { }
            // se leggesse un valore NON coerente (senza righe) restituisce un file vuoto...
            if (numRow == 0)
            {
                result = new Dictionary<string, string>();
            }
            return result;
        }
        
    }
    /// <summary>
    /// Oggetto timing x archiviazione dati perfomances
    /// </summary>
    public class TimeRec
    {
        /// <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>
        /// Tempo medio chiamata
        /// </summary>
        public double avgMsec
        {
            get
            {
                return totMsec.TotalMilliseconds / numCall;
            }
        }
        /// <summary>
        /// Totale Msec accumulati
        /// </summary>
        public TimeSpan totMsec;
        /// <summary>
        /// Classe record timing
        /// </summary>
        public TimeRec()
        {
            codCall = "";
            numCall = 0;
            totMsec = new TimeSpan(0);
        }
        /// <summary>
        /// Classe record timing
        /// </summary>
        /// <param name="codice"></param>
        /// <param name="ticks"></param>
        public TimeRec(string codice, long nTicks)
        {
            codCall = codice;
            numCall = 1;
            totMsec = new TimeSpan(nTicks);
        }
    }
    public static class TimingData
    {
        public static List<TimeRec> results = new List<TimeRec>();

        /// <summary>
        /// aggiorno vettore aggiungendo risultato
        /// </summary>
        /// <param name="codice"></param>
        /// <param name="ticks"></param>
        public static void addResult(string codice, long ticks)
        {
            if (results.Count == 0)
            {
                results.Add(new TimeRec(codice, ticks));
            }
            int indice = -1;
            for (int i = 0; i < results.Count; i++)
            {
                // se il codice è quello cercato...
                if (results[i].codCall == codice) 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(codice, ticks));
            }
        }
        /// <summary>
        /// Resetta i dati registrati (ad avvio adapter...)
        /// </summary>
        public static void resetData()
        {
            results = new List<TimeRec>();
        }
    }


    public enum tipoAdapter
    {
        /// <summary>
        /// Adapter DB
        /// </summary>
        DB,
        /// <summary>
        /// Adapter generico/demo
        /// </summary>
        DEMO,
        /// <summary>
        /// Adapter ESAGV (SCM)
        /// </summary>
        ESAGV,
        /// <summary>
        /// adapter FANUC (CMS)
        /// </summary>
        FANUC,
        /// <summary>
        /// Adapter non specificato
        /// </summary>
        ND,
        /// <summary>
        /// Adapter OSAI
        /// </summary>
        OSAI,
        /// <summary>
        /// Adapter SIEMENS (CMS)
        /// </summary>
        SIEMENS
    }

    public enum gatherCycle
    {
        /// <summary>
        /// lettura dati ad alta frequenza
        /// </summary>
        HF,
        /// <summary>
        /// lettura dati standard
        /// </summary>
        MF,
        /// <summary>
        /// lettura dati bassa freq
        /// </summary>
        LF,
        /// <summary>
        /// lettura dati bassissima priorità (re-sync stato allarmi)
        /// </summary>
        VLF
    }

    /// <summary>
    /// informazioni di produzione
    /// </summary>
    public struct prodData
    {
        public string Operator;

        public bool Status;
        public int AccTime;
        public int Power;
        public string FuncMode;
        public bool EmrStop;
        public string MessageCode;
        public string MessageText;

    }

    public struct PathData
    {
        public int PathSel;
        public string PathType; // LAVOR/ASSERV
        public string RunMode;
        public string ExeMode;
        public int pzTot;
        public string ProgramName;
        public string ProgrRow;
        public string PartId;
        public string ActiveAxes;
        public string CodG_Act;
        public string SubMode;

        public int PathFeedrate;
        public int PathFeedrateOver;
        public int PathRapidOver;
        public int PathJogOver;
        public int PathSpindleOver_01;
        public int PathSpindleOver_02;
        public int PathSpindleOver_03;
        public int PathSpindleOver_04;
        public position PathPosAct;
    }


    public struct UnOpData
    {
        public int UnOpSel;
        public int UnOpToolId;
        public int UnOpNumCU;
        public string UnOpStatus;
        public int UnOpVitaRes;
        public int UnOpSpeed;
        public int UnOpLoad;
        public int UnOpAccTime;
    }

    public struct AxisData
    {
        public int AxisSel;
        public string AxisMainProc;
        public bool AxisIsMaster;
        public string AxisMastId;
        public string AxisType;
        public string AxisDir;
        public int AxisLoad;
        public int AxisPosAct;
        public int AxisPosTgt;
        public int AxisFeedAct;
        public int AxisFeedOver;
        public string AxisAccel;
        public string AxisAccTime;
        public string AxisBattery;
        public string DistDone;
        public string InvDDone;
    }

    /// <summary>
    /// Vettore completo posizione (X-Y-Z con versori i-j-k)
    /// </summary>
    public class position
    {
        public float x;
        public float y;
        public float z;
        public float i;
        public float j;
        public float k;
        public position()
        {
            x = 0;
            y = 0;
            z = 0;
            i = 0;
            j = 0;
            k = 0;
        }
    }

    /// <summary>
    /// Dati Prod SCM (per decodifica)
    /// </summary>
    public class datiProdSCM
    {
        public string area;
        public string fileName;
        public string dimensioni;
        public DateTime start;
        public DateTime stop;
        public TimeSpan tEff;
        public TimeSpan tTot;
        public int qta;
        public TimeSpan tMed;
        public datiProdSCM()
        {
            area = "";
            fileName = "";
            dimensioni = "";
            start = DateTime.Now;
            stop = DateTime.Now;
            tEff = new TimeSpan(0);
            tTot = new TimeSpan(0);
            qta = 0;
            tMed = new TimeSpan(0);
        }
        /// <summary>
        /// crea un nuovo oggetto a partire da un array di stringhe
        /// </summary>
        /// <param name="valori"></param>
        public datiProdSCM(string[] valori)
        {
            try
            {
                area = valori[0];
                fileName = valori[1];
                dimensioni = string.Format("{0}x{1}x{2}", valori[3], valori[4], valori[5]);
                start = DateTime.Today.AddHours(Convert.ToInt16(valori[6])).AddMinutes(Convert.ToInt16(valori[7])).AddSeconds(Convert.ToInt16(valori[8]));

                stop = DateTime.Today.AddHours(Convert.ToInt16(valori[9])).AddMinutes(Convert.ToInt16(valori[10])).AddSeconds(Convert.ToInt16(valori[11]));
                // se ore == 0 --> aggiungo 1 gg!!!
                if (Convert.ToInt16(valori[9]) == 0) stop.AddDays(1);

                tEff = new TimeSpan(Convert.ToInt16(valori[12]), Convert.ToInt16(valori[13]), Convert.ToInt16(valori[14]));

                tTot = new TimeSpan(Convert.ToInt16(valori[15]), Convert.ToInt16(valori[16]), Convert.ToInt16(valori[17]));

                qta = Convert.ToInt16(valori[18]);

                tMed = new TimeSpan(Convert.ToInt16(valori[19]), Convert.ToInt16(valori[20]), Convert.ToInt16(valori[22]), Convert.ToInt16(valori[23]));
            }
            catch
            { }
        }
    }
    /// <summary>
    /// Allarme (per decodifica)
    /// </summary>
    public class allarme
    {
        public string codNum;
        public string gruppo;
        public string livello;
        public string descrizione;
        public allarme()
        {
            codNum = "";
            gruppo = "";
            livello = "";
            descrizione = "";
        }
        public allarme(string _codNum, string _gruppo, string _livello, string _descrizione)
        {
            codNum = _codNum;
            gruppo = _gruppo;
            livello = _livello;
            descrizione = _descrizione;
        }
    }


    /// <summary>
    /// Dato generico (per decodifica)
    /// </summary>
    public class otherData
    {
        public string codNum;
        public string memAddr;
        public string varName;
        public string dataType;
        public otherData()
        {
            codNum = "";
            memAddr = "";
            varName = "";
            dataType = "";
        }
        public otherData(string _codNum, string _memAddr, string _varName, string _dataType)
        {
            codNum = _codNum;
            memAddr = _memAddr;
            varName = _varName;
            dataType = _dataType;
        }
    }


    /// <summary>
    /// Strobe: contiene il set di strobe di comunicazione
    /// 
    /// rif: http://stackoverflow.com/questions/17209054/parse-bits-in-a-byte-to-enum
    /// </summary>
    [Flags]
    public enum Strobe : int
    {
        NONE = 0,
        M_CODE = 1 << 0,
        S_CODE = 1 << 1,
        T_CODE = 1 << 2,
        PZ_OK = 1 << 3,
        PZ_KO = 1 << 4,
        FEED_SPEED = 1 << 5,
        POS_ACT = 1 << 6,
        SP07 = 1 << 7,
        SP08 = 1 << 8,
        SP09 = 1 << 9,
        SP10 = 1 << 10,
        SP11 = 1 << 11,
        SP12 = 1 << 12,
        SP13 = 1 << 13,
        SP14 = 1 << 14,
        SP15 = 1 << 15,
        SP16 = 1 << 16,
        SP17 = 1 << 17,
        SP18 = 1 << 18,
        SP19 = 1 << 19,
        SP20 = 1 << 20,
        SP21 = 1 << 21,
        SP22 = 1 << 22,
        SP23 = 1 << 23,
        SP24 = 1 << 24,
        SP25 = 1 << 25,
        SP26 = 1 << 26,
        SP27 = 1 << 27,
        SP28 = 1 << 28,
        SP29 = 1 << 29,
        SP30 = 1 << 30,
        SP31 = 1 << 31
    }
    /// <summary>
    /// StFlag8: set di 8 bit (1 word) contente semaforo di variabili
    /// </summary>
    [Flags]
    public enum StFlag8 : int
    {
        NONE = 0,
        B0 = 1 << 0,
        B1 = 1 << 1,
        B2 = 1 << 2,
        B3 = 1 << 3,
        B4 = 1 << 4,
        B5 = 1 << 5,
        B6 = 1 << 6,
        B7 = 1 << 7
    }
    /// <summary>
    /// StFlag32: set di 32 bit (4 word) contente semaforo di variabili
    /// </summary>
    [Flags]
    public enum StFlag32 : int
    {
        NONE = 0,
        B00 = 1 << 0,
        B01 = 1 << 1,
        B02 = 1 << 2,
        B03 = 1 << 3,
        B04 = 1 << 4,
        B05 = 1 << 5,
        B06 = 1 << 6,
        B07 = 1 << 7,
        B08 = 1 << 8,
        B09 = 1 << 9,
        B10 = 1 << 10,
        B11 = 1 << 11,
        B12 = 1 << 12,
        B13 = 1 << 13,
        B14 = 1 << 14,
        B15 = 1 << 15,
        B16 = 1 << 16,
        B17 = 1 << 17,
        B18 = 1 << 18,
        B19 = 1 << 19,
        B20 = 1 << 20,
        B21 = 1 << 21,
        B22 = 1 << 22,
        B23 = 1 << 23,
        B24 = 1 << 24,
        B25 = 1 << 25,
        B26 = 1 << 26,
        B27 = 1 << 27,
        B28 = 1 << 28,
        B29 = 1 << 29,
        B30 = 1 << 30,
        B31 = 1 << 31
    }
    /// <summary>
    /// StatusBitMap: contiene il set di semafori/flag x status + allarmi (x classi)
    /// </summary>
    [Flags]
    public enum StatusBitMap : int
    {
        NONE = 0,
        ESTOP = 1 << 0,
        RM_AUTO = 1 << 1,
        RM_MANUAL = 1 << 2,
        RM_MDI = 1 << 3,
        RM_EDIT = 1 << 4,
        EM_RUN = 1 << 5,
        EM_READY = 1 << 6,
        EM_STOP = 1 << 7,
        EM_FEEDHOLD = 1 << 8,
        HM = 1 << 9,
        ST11 = 1 << 10,
        ST12 = 1 << 11,
        ST13 = 1 << 12,
        ST14 = 1 << 13,
        ST15 = 1 << 14,
        ST16 = 1 << 15,
        AL01 = 1 << 16,
        AL02 = 1 << 17,
        AL03 = 1 << 18,
        AL04 = 1 << 19,
        AL05 = 1 << 20,
        AL06 = 1 << 21,
        AL07 = 1 << 22,
        AL08 = 1 << 23,
        AL09 = 1 << 24,
        AL10 = 1 << 25,
        AL11 = 1 << 26,
        AL12 = 1 << 27,
        AL13 = 1 << 28,
        AL14 = 1 << 29,
        AL15 = 1 << 30,
        AL16 = 1 << 31
    }
}