using System;
using System.Collections.Generic;
//using System.Windows.Threading;
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 ReadNumber;
        private UInt16 WriteNumber;
        private UInt16 VAreaByteSize;
        private UInt16 EOKAreaByteSize;
        private UInt16 ETKAreaByteSize; 
        private UInt16 AlarmByteSize;


        private string SysLink = "";
        private string DefCn = "";
        private int iChannel;
        private int iError;


        private string StatusAddressName;
        private TS_REG StatusAddress;

        private string CommandsAddressName;
        private TS_REG CommandsAddress;

        private string VAreaAddressName;
        private TS_REG VAreaAddress;

        private string EOKAreaAddressName;
        private TS_REG EOKAreaAddress;
        
        private string ETKAreaAddressName;
        private TS_REG ETKAreaAddress; 
        
        private string PlcErrAreaAddressName;
        private TS_REG PlcErrAreaAddress;

        /// <summary>
        /// Memorie a 4 byte (32 bit)
        /// </summary>
        public UInt32[] PLC_MemoryRead;
        public UInt32[] PLC_MemoryWrite;

        /// <summary>
        /// Area V: memoria temp di UINT di 4 byte (32 bit)
        /// </summary>
        public UInt32[] PLC_MemAreaV_tmp;
        /// <summary>
        /// memorie a 1 byte (8 bit) x area EOK (status e valori vari)
        /// </summary>
        public uint[] PLC_MemoryAreaEOK;
        /// <summary>
        /// memorie a 1 byte (32 bit) x area ETK (status e valori vari)
        /// </summary>
        public UInt32[] PLC_MemoryAreaETK;
        /// <summary>
        /// Area Allarmi: memoria temp di UINT di 4 byte (32 bit)
        /// </summary>
        public UInt32[] PLC_MemAreaAlarm_tmp;
        /// <summary>
        /// memorie a 4 byte (32 bit) x area V (status e valori vari)
        /// </summary>
        public Byte[] PLC_MemoryAreaV;
        /// <summary>
        /// memorie a 1 byte (8 bit) x area Allarmi (V[300] - 256 bit)
        /// </summary>
        public Byte[] PLC_MemoryAreaAllarmi;

        // 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) : 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";
            //StrCat(DefCn, "\\defcn");

            
            ReadNumber = 265;
            WriteNumber = 265;
            VAreaByteSize = 32;
            ETKAreaByteSize = 8; 
            EOKAreaByteSize = 32;  // era 14aggiunto sino a 31 x avere 91...
            AlarmByteSize = 32;

            CommandsAddressName = fIni.ReadString("NC", "CommandsAddress", "SIMREAD[0]");
            EOKAreaAddressName = fIni.ReadString("NC", "EOKAreaAddress", "EOK");
            ETKAreaAddressName = fIni.ReadString("NC", "ETKAreaAddress", "ETK");
            PlcErrAreaAddressName = fIni.ReadString("NC", "PlcErrAreaAddress", "V");
            StatusAddressName = fIni.ReadString("NC", "StatusAddress", "SIMWRITE[0]");
            VAreaAddressName = fIni.ReadString("NC", "VAreaAddress", "V");

            /**************************************************
             * Creazione buffers letture memoria
             **************************************************/

            // create read buffer 32bit
            PLC_MemoryRead = new UInt32[ReadNumber];
            // create write buffer 32bit
            PLC_MemoryWrite = new UInt32[WriteNumber];

            // creazione buffers a 32bit
            PLC_MemAreaV_tmp = new UInt32[(int)Math.Ceiling((decimal)VAreaByteSize / 4)];

            PLC_MemAreaAlarm_tmp = new UInt32[(int)Math.Ceiling((decimal)AlarmByteSize / 4)];

            // creo array duali in bytes...
            PLC_MemoryAreaV = new Byte[VAreaByteSize];
            PLC_MemoryAreaAllarmi = new Byte[AlarmByteSize];

            // creazione veri buffer già 32bit
            PLC_MemoryAreaEOK = new uint[EOKAreaByteSize];
            PLC_MemoryAreaETK = new UInt32[ETKAreaByteSize];

            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>
        /// Reads the buffer.
        /// </summary>
        public void ReadBuffer()
        {
            // leggo da posizione memorie V/allarmi come TS_REG
            iError = read_regbuffer(ref PlcErrAreaAddress, 300, AlarmByteSize, PLC_MemAreaAlarm_tmp); 

            // lettura buffer dal CN
            iError = read_regbuffer(ref StatusAddress, 0, ReadNumber, PLC_MemoryRead);

            // leggo da posizione memorie V come TS_REG
            iError = read_regbuffer(ref VAreaAddress, 68, VAreaByteSize, PLC_MemAreaV_tmp);

            // leggo da posizione memorie EOK TS_REG
            iError = read_regbuffer(ref EOKAreaAddress, 60, EOKAreaByteSize, PLC_MemoryAreaEOK);

            // copio in array a byte...
            try
            {
                Buffer.BlockCopy(PLC_MemAreaV_tmp, 0, PLC_MemoryAreaV, 0, VAreaByteSize);
            }
            catch
            { }

            try
            {
                Buffer.BlockCopy(PLC_MemAreaAlarm_tmp, 0, PLC_MemoryAreaAllarmi, 0, AlarmByteSize);
            }
            catch
            { }
        }
        
        /// <summary>
        /// Connects to device.
        /// </summary>
        /// <returns>True if connection is ok</returns>
        public bool Connect()
        {
            // inizializzazione comunicazione
            iError = init_board(DefCn, iChannel);

            // inizializzo i registri di lettura e scrittura
            if (iError == 0)
                iError = get_reg_by_name(CommandsAddressName, ref CommandsAddress);
            if (iError == 0)
                iError = get_reg_by_name(EOKAreaAddressName, ref EOKAreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(PlcErrAreaAddressName, ref PlcErrAreaAddress);
            if (iError == 0)
                iError = get_reg_by_name(StatusAddressName, ref StatusAddress);
            if (iError == 0)
                iError = get_reg_by_name(VAreaAddressName, ref VAreaAddress);

            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));
        }

    }
}
