using System;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using IniFiles;

namespace SCMCncLib
{
    #region ESA_DATA_STRUCTURES
    // public unsafe struct TS_REG
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TS_REG
    {
        byte board;
        byte iBaseReg;
        UInt16 wType;
        UInt32 iSharedId;
        UInt32 iNum;
        UInt32 iMax;
        UInt32 iSiz;
        UInt32 iOff;
        UInt32 ChkSum;
    }
    #endregion

    /// <summary>
    /// Class for EsaGv Nc device
    /// </summary>
    public class thdNcEsaGvKvara : thdNcBase
    {

        private UInt16 IOT_ByteAreaByteSize;
        private UInt16 IOT_WordAreaByteSize;
        private UInt16 IOT_DWordAreaByteSize;
        private UInt16 IOT_String_AreaByteSize;


        private string SysLink = "";
        private string DefCn = "";
        private int iChannel;
        private int iError;
        
        private string IOT_ByteAreaAddressName;
        private TS_REG IOT_ByteAreaAddress;

        private string IOT_WordAreaAddressName;
        private TS_REG IOT_WordAreaAddress;

        private string IOT_DWordAreaAddressName;
        private TS_REG IOT_DWordAreaAddress;

        private string IOT_StringA_AreaAddressName;
        private TS_REG IOT_StringA_AreaAddress;
        private string IOT_StringB_AreaAddressName;
        private TS_REG IOT_StringB_AreaAddress;
        private string IOT_StringC_AreaAddressName;
        private TS_REG IOT_StringC_AreaAddress;
        private string IOT_StringD_AreaAddressName;
        private TS_REG IOT_StringD_AreaAddress;
        
        /// <summary>
        /// Area IOT_Byte: memoria temp di UINT di 4 byte (32 bit)
        /// </summary>
        public UInt32[] PLC_MemoryAreaIOT_Byte_tmp;
        /// <summary>
        /// Area IOT_Word: memoria temp di UINT di 4 byte (32 bit)
        /// </summary>
        public UInt32[] PLC_MemoryAreaIOT_Word_tmp;
        /// <summary>
        /// memorie a 1 byte (8 bit) x area IOT.Byte
        /// </summary>
        public Byte[] PLC_MemoryAreaIOT_Byte;
        /// <summary>
        /// memorie a 2 byte (16 bit) x area IOT.Word
        /// </summary>
        public UInt16[] PLC_MemoryAreaIOT_Word;
        /// <summary>
        /// memorie a 4 byte (32 bit) x area IOT.DWord
        /// </summary>
        public UInt32[] PLC_MemoryAreaIOT_DWord;
        /// <summary>
        /// memorie TEMP x area IOT.String
        /// </summary>
        public UInt32[] PLC_MemoryAreaIOT_String_tmp;
        /// <summary>
        /// memorie a 1 byte (8 bit) x area IOT.String (singoli caratteri...)
        /// </summary>
        public Byte[] PLC_MemoryAreaIOT_String_row;
        /// <summary>
        /// memorie a 1 byte (8 bit) x area IOT.String (singoli caratteri...)
        /// </summary>
        public Byte[,] PLC_MemoryAreaIOT_String;

        // esa constants
        #region ESA_PLC_CONSTANTS
        private const UInt32 MAX_PATH = 256;
        #endregion

        #region ESA_DLL_IMPORT_FUNCTIONS
        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_ConvComunicationChannel@4")]
        private static extern int ConvComunicationChannel([MarshalAs(UnmanagedType.LPStr)] string sChannelType);

        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_init_board@8")]
        private static extern int init_board([MarshalAs(UnmanagedType.LPStr)]string defcn_name, int ChannelType);

        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_exit_board@0")]
        private static extern int exit_board();

        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_get_reg_by_name@8")]
        private static extern int get_reg_by_name([MarshalAs(UnmanagedType.LPStr)]string name, ref TS_REG pReg);

        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_read_regbuffer@16")]
        private static extern int read_regbuffer(ref TS_REG pReg, UInt16 Offset, UInt16 Count, [MarshalAs(UnmanagedType.LPArray)] UInt32[] pDst);

        [DllImport("KvCom3x", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "_write_regbuffer@16")]
        private static extern int write_regbuffer(ref TS_REG pReg, UInt16 Offset, UInt16 Count, [MarshalAs(UnmanagedType.LPArray)] UInt32[] pSrc);

        [DllImport("VKEnv", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern void ENVGetProfileString(
            [MarshalAs(UnmanagedType.LPStr)] string AppName,
            [MarshalAs(UnmanagedType.LPStr)] string KeyName,
            [MarshalAs(UnmanagedType.LPStr)] string Default,
            //[MarshalAs(UnmanagedType.LPArray)] byte[] ReturnedString,
            [MarshalAs(UnmanagedType.LPStr)] StringBuilder ReturnedString,
            UInt32 nSize);
        #endregion

        /// <summary>
        /// After calling this method the search path is:
        /// 1 - The directory from which the application loaded
        /// 2 - The directory specified by the lpPathName parameter
        /// 3 - The system directory (The name of this directory is System32)
        /// 4 - The 16-bit system directory (The name of this directory is System)
        /// 5 - The Windows directory
        /// 6 - The directories that are listed in the PATH environment variable
        /// </summary>
        /// <param name="lpPathName"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetDllDirectory(string lpPathName);


        /// <summary>
        /// Initializes a new instance of the <see cref="thdNcEsaGvKvara"/> class.
        /// </summary>
        /// <param name="fIni">The f ini.</param>
        public thdNcEsaGvKvara(IniFile fIni, int IAS_Byte, int IAS_Word, int IAS_DWord, int IAS_String) : base(fIni)
        {
            DeviceType = NC_DEVICE_TYPE.ESAGV_KVARA;

            StringBuilder tempS = new StringBuilder(Convert.ToInt32(MAX_PATH));

            // set executable path
            SetDllDirectory(fIni.ReadString("NC", "SysExe", "C:\\KVARA\\EXE"));

            // communication type
            string defValue = fIni.ReadString("NC", "SysLink", "SIMULATO");
            ENVGetProfileString("SYS", "LINK", defValue, tempS, MAX_PATH);
            //bufferH.Free();
            SysLink = tempS.ToString();

            // communication channel
            iChannel = ConvComunicationChannel(SysLink);
            // definition path
            defValue = fIni.ReadString("NC", "SysDefCn", "C:\\KVARA\\DISCOI");
            ENVGetProfileString("SYS", "DEFCN", defValue, tempS, MAX_PATH);
            DefCn = tempS.ToString() + "\\defcn";
            
            // nuove aree: da init mi passa le dimensioni delle memorie
            IOT_ByteAreaByteSize = Convert.ToUInt16(IAS_Byte);
            IOT_WordAreaByteSize = Convert.ToUInt16(IAS_Word);
            IOT_DWordAreaByteSize = Convert.ToUInt16(IAS_DWord);
            IOT_String_AreaByteSize = Convert.ToUInt16(IAS_String);

            // indirizzo area IOT...
            IOT_ByteAreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressByte", "IOT.BYTE");
            IOT_WordAreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressWord", "IOT.WORD");
            IOT_DWordAreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressDWord", "IOT.DWORD");
            IOT_StringA_AreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressStringA", "IOT.ProgramNameAreaA");
            IOT_StringB_AreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressStringB", "IOT.ProgramNameAreaB");
            IOT_StringC_AreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressStringC", "IOT.ProgramNameAreaC");
            IOT_StringD_AreaAddressName = fIni.ReadString("NC", "IOT_AreaAddressStringD", "IOT.ProgramNameAreaD");

            /**************************************************
             * Creazione buffers letture memoria
             **************************************************/
            // creo array x aree memoria IOT...
            PLC_MemoryAreaIOT_Byte_tmp = new UInt32[(int)Math.Ceiling((decimal)IOT_ByteAreaByteSize / 4)];
            PLC_MemoryAreaIOT_Byte = new Byte[IOT_ByteAreaByteSize];
            PLC_MemoryAreaIOT_Word_tmp = new UInt32[(int)Math.Ceiling((decimal)IOT_WordAreaByteSize / 2)];
            PLC_MemoryAreaIOT_Word = new UInt16[IOT_WordAreaByteSize];
            PLC_MemoryAreaIOT_DWord = new UInt32[IOT_DWordAreaByteSize];
            PLC_MemoryAreaIOT_String_tmp = new UInt32[(int)Math.Ceiling((decimal)IOT_String_AreaByteSize / 4)];
            PLC_MemoryAreaIOT_String_row = new Byte[IOT_String_AreaByteSize * 4]; // HARD CODED 4 aree consecutive in riga...
            PLC_MemoryAreaIOT_String = new Byte[4, IOT_String_AreaByteSize]; // HARD CODED 4 aree su vettore di 4 righe...

            if (!Connect())
                Disconnect();
        }

        /// <summary>
        /// Thread main execution
        /// </summary>
        public override void Execute()
        {
            while (!requestStop)
            {
                if (Connected)
                {
                    // load data from NC
                    ReadBuffer();
                    // if requested make a pause
                    if (PlcDelay > 0)
                        Thread.Sleep(PlcDelay);
                }
                else if (!Connected)
                {
                    // wait 5 sec and retry connection
                    Thread.Sleep(5000);
                    Connect();
                }
                else
                    Thread.Sleep(500);
            }

            if (Connected) Disconnect();
            Finished = true;
        }


        /// <summary>
        /// Leggo il buffer di memorie BYTE
        /// </summary>
        public void ReadBufferByte()
        {
            // 2017.02: modifiche x nuova versione PLC: leggo intero buffer BYTE
            iError = read_regbuffer(ref IOT_ByteAreaAddress, 0, IOT_ByteAreaByteSize, PLC_MemoryAreaIOT_Byte_tmp);
            // ...e copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_Byte_tmp, 0, PLC_MemoryAreaIOT_Byte, 0, IOT_ByteAreaByteSize);
            }
            catch
            { }
        }
        /// <summary>
        /// Leggo il buffer di memorie WORD
        /// </summary>
        public void ReadBufferWord()
        {
            // 2017.02: modifiche x nuova versione PLC: leggo intero buffer WORD
            iError = read_regbuffer(ref IOT_WordAreaAddress, 0, IOT_WordAreaByteSize, PLC_MemoryAreaIOT_Word_tmp);
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_Word_tmp, 0, PLC_MemoryAreaIOT_Word, 0, IOT_WordAreaByteSize * 2);
            }
            catch
            { }
        }
        /// <summary>
        /// Leggo il buffer di memorie DWORD
        /// </summary>
        public void ReadBufferDWord()
        {
            // leggo intero buffer DWORD
            iError = read_regbuffer(ref IOT_DWordAreaAddress, 0, IOT_DWordAreaByteSize, PLC_MemoryAreaIOT_DWord);
        }
        /// <summary>
        /// Leggo il buffer di memorie STRING
        /// </summary>
        public void ReadBufferString()
        {
            // leggo intero buffer StringA
            iError = read_regbuffer(ref IOT_StringA_AreaAddress, 0, IOT_String_AreaByteSize, PLC_MemoryAreaIOT_String_tmp);
            // ...e copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_String_tmp, 0, PLC_MemoryAreaIOT_String_row, 0, IOT_String_AreaByteSize);
            }
            catch (Exception exc)
            { }

            // leggo intero buffer StringB
            iError = read_regbuffer(ref IOT_StringB_AreaAddress, 0, IOT_String_AreaByteSize, PLC_MemoryAreaIOT_String_tmp);
            // ...e copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_String_tmp, 0, PLC_MemoryAreaIOT_String_row, IOT_String_AreaByteSize * 1, IOT_String_AreaByteSize);
            }
            catch (Exception exc)
            { }

            // leggo intero buffer StringC
            iError = read_regbuffer(ref IOT_StringC_AreaAddress, 0, IOT_String_AreaByteSize, PLC_MemoryAreaIOT_String_tmp);
            // ...e copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_String_tmp, 0, PLC_MemoryAreaIOT_String_row, IOT_String_AreaByteSize * 2, IOT_String_AreaByteSize);
            }
            catch (Exception exc)
            { }

            // leggo intero buffer StringD
            iError = read_regbuffer(ref IOT_StringD_AreaAddress, 0, IOT_String_AreaByteSize, PLC_MemoryAreaIOT_String_tmp);
            // ...e copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemoryAreaIOT_String_tmp, 0, PLC_MemoryAreaIOT_String_row, IOT_String_AreaByteSize * 3, IOT_String_AreaByteSize);
            }
            catch (Exception exc)
            { }

            // copio in vettore 2D...
            Buffer.BlockCopy(PLC_MemoryAreaIOT_String_row, 0, PLC_MemoryAreaIOT_String, 0, IOT_String_AreaByteSize * 4);

        }



        /// <summary>
        /// Reads the buffer.
        /// </summary>
        public void ReadBuffer()
        {
            ReadBufferByte();
            ReadBufferWord();
            ReadBufferDWord();
            ReadBufferString();
        }

        /// <summary>
        /// Connects to device.
        /// </summary>
        /// <returns>True if connection is ok</returns>
        public bool Connect()
        {
            // inizializzazione comunicazione
            iError = init_board(DefCn, iChannel);

            // effetto le vere letture!
            if (iError == 0)
                iError = get_reg_by_name(IOT_ByteAreaAddressName, ref IOT_ByteAreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_WordAreaAddressName, ref IOT_WordAreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_DWordAreaAddressName, ref IOT_DWordAreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_StringA_AreaAddressName, ref IOT_StringA_AreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_StringB_AreaAddressName, ref IOT_StringB_AreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_StringC_AreaAddressName, ref IOT_StringC_AreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(IOT_StringD_AreaAddressName, ref IOT_StringD_AreaAddress);

            Connected = (iError == 0);
            return Connected;
        }

        /// <summary>
        /// Disconnects device.
        /// </summary>
        public void Disconnect()
        {
            iError = exit_board();
        }

        /// <summary>
        /// Esa virtual Nc needs 2ms of multimedia timer to work in real time
        /// </summary>
        /// <returns></returns>
        public bool CheckMultimediaTimer()
        {
            int minimumPeriod = TimePeriod.MinimumPeriod;
            //int maximumPeriod = TimePeriod.MaximumPeriod;
            return ((minimumPeriod <= 2) && (minimumPeriod > 0));
        }

    }
}
