using System;
using System.Runtime.InteropServices;
using System.Text;

namespace ltrModulesNet
{
    public class ltr12api
    {
        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_Init(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_Close(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_GetConfig(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_GetFrame(ref TLTR12 module, uint[] buf, uint tout);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_Open(ref TLTR12 module, uint saddr, ushort sport, string csn, int slot_num);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_IsOpened(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_ProcessData(ref TLTR12 module, uint[] src, double[] dest,
                                                            ref int size, ProcFlags flags);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_SetADC(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_Start(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_Stop(ref TLTR12 module);

        [DllImport("ltr12api.dll")]
        static extern int LTR12_Recv(ref TLTR12 hnd, uint[] buf, uint[] tmark, uint size, uint timeout); //Прием данных от модуля

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_CalcAdcFreq(ref ADCFREQ_CFG adcCfg, out double resultAdcFreq);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FindAdcFreqParams(double adcFreq, out ADCFREQ_CFG adcCfg, out double resultAdcFreq);
        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FindAdcFreqParams(double adcFreq, out ADCFREQ_CFG adcCfg, IntPtr resultAdcFreq);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FillAdcFreqParams(ref TLTR12 module, double adcFreq, out double resultAdcFreq);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FillAdcFreqParams(ref TLTR12 module, double adcFreq, IntPtr resultAdcFreq);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FillLChannel(ref TLTR12 module, uint lchNum, byte phyChNum, ChModes mode);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_MeasAdcZeroOffset(ref TLTR12 hnd, uint flags);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_SearchFirstFrame(ref TLTR12 hnd, uint[] data, uint size,
                                                                 out uint index);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FlashUserDataRead(ref TLTR12 hnd, uint addr, byte[] data, uint size);

        [DllImport("ltr12api.dll")]
        static extern _LTRNative.LTRERROR LTR12_FlashUserDataWrite(ref TLTR12 hnd, uint addr, byte[] data, uint size);

        // функции вспомагательного характера
        [DllImport("ltr12api.dll")]
        static extern IntPtr LTR12_GetErrorString(int ErrorCode);


        public const int LTR12_CHANNELS_CNT = 32; /* Максимальное число измерительных каналов в одном модуле */
        public const int LTR12_DIFF_CHANNELS_CNT = 16; /* Максимальное число измерительных разностных каналов в одном модуле */ 
        public const int LTR12_RANGES_CNT = 1; /* Количество диапазонов измерения */

        public const double LTR12_ADC_RANGE_MA = 20; /* Предел диапазона измерения модуля в мА */
        public const double LTR12_R_IN_NOM = 25; /* Номинальное значение входного сопротивления в Омах */

        public const int LTR12_MAX_LCHANNELS_CNT = 128; /* Максимальное количество каналов в логической таблице */
        public const int LTR12_NAME_SIZE = 8; /* Размер поля с названием модуля */
        public const int LTR12_SERIAL_SIZE = 16; /* Размер поля с серийным номером модуля */
        public const int LTR12_FIRMDATE_SIZE = 14; /* Размер поля с информацией о дате создания прошивки контроллера модуля */

        public const int LTR12_ADC_SCALE_CODE_MAX = 8000; /* Код АЦП, соответствующий номинальному значению диапазона */
        public const double LTR12_IN_CLOCK_FREQ = 15000000; /* Значение входной тактовой частоты  контроллера модуля в Гц */
        public const int LTR12_MAX_ADC_DIVIDER = 65535; /* Максимальное допустимое значение поля делителя частоты АЦП */
        public const int LTR12_MIN_ADC_DIVIDER = 2; /* Минимальное допустимое значение поля делителя частоты АЦП */

        public const double LTR12_MAX_ADC_FREQ = 15000000; /* Максимальное значение частоты АЦП в Гц */

        public const int LTR12_FLASH_USERDATA_SIZE = 2048; /* Размер пользовательской области flash-памяти */

        /* Источник запуска сбора данных модулем */
        public enum AcqStartSources : uint
        {
            INT = 0,
            EXT_RISE = 1,
            EXT_FALL = 2
        }

        public enum ConvStartSources : uint
        {
            INT = 0,
            EXT_RISE = 1,
            EXT_FALL = 2
        }

        public enum ChModes : byte
        {
            COMM = 0,
            DIFF = 1,
            ZERO = 2,
            DIFF_X_Y0 = 3,
            DIFF_Y_X0 = 4
        }

        [Flags]
        public enum ProcFlags : uint
        {
            CALIBR = 0x00000001,
            CONV_UNIT = 0x00000002,
            ZERO_OFFS_COR = 0x00010000,
            ZERO_OFFS_AUTORECALC = 0x00020000,
            NONCONT_DATA = 0x00000100
        }
        


        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct CBR_INFO
        {
            float Offset_;                           /*  Код смещения нуля. Общий для всех каналов модуля */
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR12_CHANNELS_CNT)]
            float[] ChScale_;                        /* Индивидуальный коэффициент шкалы для каждого канала модуля */
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
            float[] Reserved_;

            public float Offset { get { return Offset_; } }
            public float ChScale(int ch) { return ChScale_[ch]; }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct INFO
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR12_NAME_SIZE)]
            char[] Name_;                          /* название модуля (строка) */
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR12_SERIAL_SIZE)]
            char[] Serial_;                        /* серийный номер модуля (строка) */

            ushort FwVer_;                    /* версия ПО модуля (младший байт - минорная, старший - мажорная */

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR12_FIRMDATE_SIZE)]
            char[] FwDate_;                          /* дата создания ПО (строка) */
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            byte[] Reserved_;

            CBR_INFO Cbr_;


            public string Name { get { return new string(Name_).Split('\0')[0]; } }
            public string Serial { get { return new string(Serial_).Split('\0')[0]; } }
            public ushort FirmwareVersion { get { return FwVer_; } }
            public string FirmwareVersionStr { get { return ((byte)((FwVer_ & 0xFF00) >> 8)).ToString() + '.' + ((byte)(FwVer_ & 0xFF)).ToString(); } }
            public string FirmwareDate { get { return new string(FwDate_).Split('\0')[0]; } }
            public CBR_INFO Cbr { get { return Cbr_; } }            
        };

        /** Параметры состояния модуля */
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct STATE
        {
            [MarshalAs(UnmanagedType.U1)]
            bool Configured_;
            [MarshalAs(UnmanagedType.U1)]
            bool Run_;
            uint FrameWordsCount_;
            double AdcFreq_;
            double FrameFreq_;
            double AdcZeroOffsetCode_;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            uint[] Reserved_;

            public bool Configured { get { return Configured_; } }
            public bool Run { get { return Run_; } }            
            public uint FrameWordsCount { get { return FrameWordsCount_; } }
            public double AdcFreq { get { return AdcFreq_; } }
            public double FrameFreq { get { return FrameFreq_; } }
            public double AdcZeroOffsetCode { get { return AdcZeroOffsetCode_; } }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct LCHANNEL
        {
            byte PhyChannel_;
            ChModes Mode_;
            ushort Reserved_;

            public LCHANNEL(byte phy_ch, ChModes mode)
            {
                PhyChannel_ = phy_ch;
                Mode_ = mode;
                Reserved_ = 0;
            }
            public byte PhyChannel { get { return PhyChannel_; } set { PhyChannel_ = value; } }
            public ChModes Mode { get { return Mode_; } set { Mode_ = value; } }
        }


        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct ADCFREQ_CFG
        {
            [MarshalAs(UnmanagedType.U1)]
            bool Is400KHz_;
            ushort Prescaler_;        
            int Divider_;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            uint[] Reserved_;

            public bool Is400KHz { get { return Is400KHz_; } set { Is400KHz_ = value; } }
            public ushort Prescaler { get { return Prescaler_; } set { Prescaler_ = value; } }
            public int Divider { get { return Divider_; } set { Divider_ = value; } }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct CONFIG
        {
            ADCFREQ_CFG AdcFreqParams_;
            AcqStartSources AcqStartSrc_;
            ConvStartSources ConvStartSrc_;            
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            uint[] Reserved_;

            uint BgLChCnt_;
            uint LChCnt_;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR12_MAX_LCHANNELS_CNT)]
            LCHANNEL[] LChTbl_;

            public ADCFREQ_CFG AdcFreqParams { get { return AdcFreqParams_; } set { AdcFreqParams_ = value; } }
            public AcqStartSources AcqStartSrc { get { return AcqStartSrc_; } set { AcqStartSrc_ = value; } }
            public ConvStartSources ConvStartSrc { get { return ConvStartSrc_; } set { ConvStartSrc_ = value; } }
            public uint BgLChCnt { get { return BgLChCnt_; } set { BgLChCnt_ = value; } }
            public uint LChCnt { get { return LChCnt_; } set { LChCnt_ = value; } }
            public LCHANNEL[] LChTbl { get { return LChTbl_; } set { LChTbl_ = value; } }

            public double AdcFreq {
                set {
                    LTR12_FindAdcFreqParams(value, out AdcFreqParams_, IntPtr.Zero);
                }
                get {
                    double freq;
                    LTR12_CalcAdcFreq(ref AdcFreqParams_ , out freq);
                    return freq;
                }
            }
            public void SetLChannel(byte lchNum, byte phyChNum, ChModes mode)
            {
                LChTbl_[lchNum] = new LCHANNEL(phyChNum, mode);
            }

        } 


        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct TLTR12
        {
            int _size;
            public _LTRNative.TLTR Channel;
            IntPtr _internal;
            CONFIG _cfg;
            STATE _stat;
            INFO _info;

            /** Настройки модуля. Заполняются пользователем перед вызовом LTR210_SetADC(). */
            public CONFIG Cfg { get { return _cfg; } set { _cfg = value; } }
            /** Состояние модуля и рассчитанные параметры. Поля изменяются функциями
                библиотеки. Пользовательской программой могут использоваться
                только для чтения. */
            public STATE State { get { return _stat; } }
            public INFO ModuleInfo { get { return _info; } }
        };

        TLTR12 module;


        public CONFIG Cfg { get { return module.Cfg; } set { module.Cfg = value; } }
        public STATE State { get { return module.State; } }
        public INFO ModuleInfo { get { return module.ModuleInfo; } }

        public void SetLChannel(uint lchNum, byte phyChNum, ChModes mode) {
            LTR12_FillLChannel(ref module, lchNum, phyChNum, mode);
        }

        public void FillAdcFreqParams(double adcFreq, out double resultAdcFreq)
        {
            LTR12_FillAdcFreqParams(ref module, adcFreq, out resultAdcFreq);
        }

        public void FillAdcFreqParams(double adcFreq)
        {
            LTR12_FillAdcFreqParams(ref module, adcFreq, IntPtr.Zero);
        }


        public ltr12api()
        {
            LTR12_Init(ref module);
        }

        ~ltr12api()
        {
            LTR12_Close(ref module);
        }


        public virtual _LTRNative.LTRERROR Init()
        {
            return LTR12_Init(ref module);
        }

        public virtual _LTRNative.LTRERROR Open(uint saddr, ushort sport, string csn, ushort cc)
        {
            return LTR12_Open(ref module, saddr, sport, csn, cc);
        }

        public virtual _LTRNative.LTRERROR Open(string csn, ushort cc)
        {
            return Open(_LTRNative.SADDR_DEFAULT, _LTRNative.SPORT_DEFAULT, csn, cc);
        }

        public virtual _LTRNative.LTRERROR Open(ushort cc)
        {
            return Open("", cc);
        }

        public virtual _LTRNative.LTRERROR Close()
        {
            return LTR12_Close(ref module);
        }

        public virtual _LTRNative.LTRERROR IsOpened()
        {
            return LTR12_IsOpened(ref module);
        }

        public virtual _LTRNative.LTRERROR GetConfig()
        {
            return LTR12_GetConfig(ref module);
        }

        public virtual _LTRNative.LTRERROR SetADC()
        {
            return LTR12_SetADC(ref module);
        }

        public virtual _LTRNative.LTRERROR Start()
        {
            return LTR12_Start(ref module);
        }

        public virtual _LTRNative.LTRERROR Stop()
        {
            return LTR12_Stop(ref module);
        }


        public virtual int Recv(uint[] Data, uint[] tstamp, uint size, uint timeout)
        {
            return LTR12_Recv(ref module, Data, tstamp, size, timeout);
        }

        /* такой вариант оставлен только для совместимости .... */
        public virtual int Recv(uint[] Data, uint size, uint[] tstamp, uint timeout)
        {
            return LTR12_Recv(ref module, Data, tstamp, size, timeout);
        }

        public virtual int Recv(uint[] Data, uint size, uint timeout)
        {
            return LTR12_Recv(ref module, Data, null, size, timeout);
        }

        public virtual _LTRNative.LTRERROR ProcessData(uint[] src, double[] dest,
            ref int size, ProcFlags flags)
        {
            return LTR12_ProcessData(ref module, src, dest, ref size, flags);
        }

        public virtual _LTRNative.LTRERROR GetFrame(uint[] buf, uint tout)
        {
            return LTR12_GetFrame(ref module, buf, tout);
        }


        public virtual _LTRNative.LTRERROR SetDefaultTimeout(uint timeout)
        {
            return _LTRNative.LTR_SetTimeout(ref module.Channel, timeout);
        }

        public static string GetErrorString(_LTRNative.LTRERROR err)
        {
            IntPtr ptr = LTR12_GetErrorString((int)err);
            string str = Marshal.PtrToStringAnsi(ptr);
            Encoding srcEncodingFormat = Encoding.GetEncoding("windows-1251");
            Encoding dstEncodingFormat = Encoding.UTF8;
            return dstEncodingFormat.GetString(Encoding.Convert(srcEncodingFormat, dstEncodingFormat, srcEncodingFormat.GetBytes(str)));
        }

        public virtual _LTRNative.LTRERROR SearchFirstFrame(uint[] data, uint size, out uint index)
        {
            return LTR12_SearchFirstFrame(ref module, data, size, out index);
        }

        public virtual _LTRNative.LTRERROR MeasAdcZeroOffset()
        {
            return LTR12_MeasAdcZeroOffset(ref module, 0);
        }

        public _LTRNative.LTRERROR FlashUserDataRead(uint addr, byte[] data, uint size)
        {
            return LTR12_FlashUserDataRead(ref module, addr, data, size);
        }

        public _LTRNative.LTRERROR FlashUserDataWrite(uint addr, byte[] data, uint size)
        {
            return LTR12_FlashUserDataWrite(ref module, addr, data, size);
        }    
    }
}
