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

namespace ltrModulesNet
{
    public class ltr114api
    {
        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Init(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Open(ref TLTR114 hnd, uint saddr, ushort sport, string csn, int slot_num);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Close(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_IsOpened(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_GetConfig(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Calibrate(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_SetADC(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Start(ref TLTR114 hnd);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_Stop(ref TLTR114 hnd);

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


        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_GetFrame(ref TLTR114 hnd, uint[] buf);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_ProcessData(ref TLTR114 hnd, uint[] src, double[] dest, ref int size, CorrectionModes correction_mode, ProcFlags flags);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_ProcessDataTherm(ref TLTR114 hnd, uint[] src, double[] dest, double[] therm, ref int size, out int tcnt, CorrectionModes correction_mode, ProcFlags flags);


        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_FindFreqDivider(double adcFreq, out int resultDivider, out double resultAdcFreq);

        [DllImport("ltr114api.dll")]
        static extern _LTRNative.LTRERROR LTR114_FindInterval(double chFreq, double adcFreq, int LChCnt, CorrectionModes correctionMode, out  UInt16 resultInterval, out double resultChFreq);


        [DllImport("ltr114api.dll")]
        static extern IntPtr LTR114_GetErrorString(int ErrorCode);   


        public const int LTR114_CLOCK                 = 15000; /* тактовая частота модуля в кГц */
        public const int LTR114_ADC_DIVIDER           = 1875;  //делитель частоты для АЦП
        public const int LTR114_MAX_CHANNEL           = 16;    /* Максимальное число физических каналов */
        public const int LTR114_MAX_R_CHANNEL         = 8;     /* Максимальное число физических каналов для измерения сопротивлений */
        public const int LTR114_MAX_LCHANNEL          = 128;   /* Максимальное число логических каналов */

        public const int LTR114_ADC_RANGEQNT          = 3;     //кол-во диапазонов измерения напряжения
        public const int LTR114_R_RANGEQNT            = 3;     //кол-во диапазонов измерения сопротивлений
        public const int LTR114_AUTOCALIBR_STEPS      = 13;     //кол-во шагов для автокалибровки
        public const uint LTR114_MAX_SCALE_VALUE      = 8000000;  //код шкалы, соответствующий максимальному значению диапазона измерения

        public const int LTR114_FREQ_DIVIDER_MIN      = 2;

        public const int LTR114_SCALE_INTERVALS = 3;

        public enum ProcFlags : int
        {
            None = 0,
            Value = 1,
            AvgR = 2
        }

        public enum Ranges : byte
        {
            U_10 = 0,
            U_2 = 1,
            U_04 = 2,
            R_400 = 0,
            R_1200 = 1,
            R_4000 = 2
        }

        public enum MeasModes : byte
        {
            U = 0,
            R = 0x20,
            NR = 0x28
        }

        public enum CorrectionModes : int
        {
            None = 0,
            Init = 1,
            Auto = 2
        }

        public enum SyncModes : byte
        {
            None = 0,
            Internal = 1,
            Master = 2,
            External = 4
        }


        [Flags]
        public enum Features : byte
        {
            None = 0,
            StopSW = 1,
            Therm = 2,
            CbrDis = 4,   //запрет начальной калибровки
            ManualOSR = 8   //ручная настройка OSR
        }



        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct AUTO_CBR_COEF
        {
            public double Offset;                      /* смещение нуля */
            public double Gain;                        /* масштабный коэффициент */
        }

        /*измеренные значения шкалы на этампе автокалибровки*/
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct SCALE
        {
            public int Null;        //значение нуля                    
            public int Ref;         //значение +шкала
            public int NRef;       //значение -шкала
            public int Interm;
            public int NInterm;
        }


        /*информация для автокалибровки модуля по одному диапазону*/
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct CBRINFO
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_SCALE_INTERVALS)]
            AUTO_CBR_COEF[] _Coefs;  /*вычисленные на этапе автокалибровки значения Gain и Offset*/
            
            IntPtr TempScale;       /*массив временных измерений шкалы/нуля */

            int Null;        //значение нуля                    
            int Ref;         //значение +шкала
            int NRef;       //значение -шкала
            int Interm;
            int NInterm;

            int LastNull;        //значение нуля                    
            int LastRef;         //значение +шкала
            int LastNRef;       //значение -шкала
            int LastInterm;
            int LastNInterm;

            int HVal;
            int LVal;
        }


        //калибровочные коэффициенты из TINFO_LTR114
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct CBR_COEFS
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_ADC_RANGEQNT)]
            float[] _U;                      /*значения ИОН для диапазонов измерения напряжений*/

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_R_RANGEQNT)]
            float[] _I;                       /*значения токов для диапазонов измерения сопротивлений*/

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_ADC_RANGEQNT)]
            float[] _UIntr;
        }


        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct LCHANNEL
        {
            MeasModes _MeasMode;       /*режим измерения*/
            byte _Channel;       /*физический канал*/
            Ranges _Range;         /*диапазон измерения*/

            MeasModes MeasMode { get { return _MeasMode; } set { _MeasMode = value; } }
            byte Channel { get { return _Channel; } set { _Channel = value; } }
            Ranges Range { get { return _Range; } set { _Range = value; } }

            public LCHANNEL(MeasModes MeasMode, byte Channel, Ranges Range)
            {
                this._MeasMode = MeasMode;
                this._Channel = Channel;
                this._Range = Range;
            }
        }

        //информация о модуле
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct INFO
        {

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            char[] Name_;                          /* название модуля (строка) */
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            char[] Serial_;                        /* серийный номер модуля (строка) */
            ushort VerMCU_;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)]
            char[] Date_;                          /* дата создания ПО (строка) */
            byte VerPLD_;  //версия прошивки ПЛИС

            CBR_COEFS CbrCoef_;              /* калибровочные коэффициенты для каждого диапазона */

            public string Name { get { return new string(Name_).Split('\0')[0]; } }
            public string Serial { get { return new string(Serial_).Split('\0')[0]; } }
            public ushort VerMCU { get { return VerMCU_; } }
            public string VerMCUStr { get { return ((byte)((VerMCU_ & 0xFF00) >> 8)).ToString() + '.' + ((byte)(VerMCU_ & 0xFF)).ToString(); } }
            public string Date { get { return new string(Date_).Split('\0')[0]; } }
            public byte VerPLD { get { return VerPLD_; } }
            public string VerPLDStr { get { return VerPLD_.ToString(); } }

            public CBR_COEFS CbrCoef { get { return CbrCoef_; } }
        };



        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct TLTR114
        {
            int size_;                               /* размер структуры в байтах */
            public _LTRNative.TLTR Channel;                           /* описатель канала связи с модулем */

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_ADC_RANGEQNT)]
            CBRINFO[] AutoCalibrInfo_;      /* данные для вычисления калибровочных коэф. для каждого диапазона */
            public int LChQnt;  // количество активных логических каналов 

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LTR114_MAX_LCHANNEL)]
            public LCHANNEL[] LChTbl;        // управляющая таблица с настройками логических каналов

            public UInt16 Interval;                          //длина межкадрового интервала
            public Features SpecialFeatures;                   //дополнительные возможности модуля (подключение термометра, блокировка коммутации)

            //значение передискр. АЦП - вычисляется в соответствии с частотой дискретизации
            public byte AdcOsr;

            public SyncModes SyncMode;               /*режим синхронизации 
                                                  000 - нет синхронизации
                                                  001 - внутренняя синхронизация
                                                  010 - внутренняя синхронизация - ведущий
                                                  100 - внешняя синхронизация (ведомый)
                                                  */

            public int FreqDivider;                       // делитель частоты АЦП (2..8000)
            // частота дискретизации равна F = LTR114_CLOCK/(LTR114_ADC_DIVIDER*FreqDivider)

            public int FrameLength;                       //размер данных, передаваемых модулем за один кадр 
            //устанавливается после вызова LTR114_SetADC
            public bool Active;
            IntPtr Reserved;

            public INFO ModuleInfo;                 /* информация о модуле LTR114 */
        };


        TLTR114 module;

        public ltr114api() 
        {
            LTR114_Init(ref module);	
        }

        ~ltr114api()
        {
            LTR114_Close(ref module);
        }

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

        public virtual _LTRNative.LTRERROR Open (uint saddr, ushort sport, string csn, ushort cc)
		{
			return LTR114_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 LTR114_Close(ref module);
		}        

        public virtual _LTRNative.LTRERROR IsOpened ()
		{
			return LTR114_IsOpened(ref module);
		}
		
		public virtual _LTRNative.LTRERROR GetConfig ()
		{
			return LTR114_GetConfig(ref module);
		}

        public virtual _LTRNative.LTRERROR Calibrate ()
		{
			return LTR114_Calibrate(ref module);
		}

		
        public virtual _LTRNative.LTRERROR SetADC()
		{
			return LTR114_SetADC(ref module);
		}
		
		public virtual _LTRNative.LTRERROR Start()
		{
			return LTR114_Start(ref module);
		}
		
		public virtual _LTRNative.LTRERROR Stop()
		{
			return LTR114_Stop(ref module);
		}
                
		
		public virtual int Recv (uint [] Data, uint[] tstamp, uint size, uint timeout)
		{			
			return LTR114_Recv(ref module, Data, tstamp, size, timeout);
		}

 
        public virtual int Recv(uint[] Data, uint size, uint timeout)
        {
            return LTR114_Recv(ref module, Data, null, size, timeout);
        }
		
		public virtual _LTRNative.LTRERROR ProcessData (uint [] src, double [] dest,
            ref int size, CorrectionModes correction_mode, ProcFlags flags)
		{
            return LTR114_ProcessData(ref module, src, dest, ref size, correction_mode, flags);
		}

        public virtual _LTRNative.LTRERROR ProcessDataTherm (uint [] src, double [] dest, double [] therm,
            ref int size, out int tcnt,  CorrectionModes correction_mode, ProcFlags flags)
		{
            return LTR114_ProcessDataTherm(ref module, src, dest, therm, ref size, out tcnt, correction_mode, flags);
		}

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

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

        

        public static string GetErrorString(_LTRNative.LTRERROR err)
        {
            IntPtr ptr = LTR114_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 int LChQnt { get { return module.LChQnt; } set { module.LChQnt = value; } }


        public LCHANNEL[] LChTbl
        {
            get
            {
                LCHANNEL[] ret = new LCHANNEL[LTR114_MAX_LCHANNEL];
                for (int i = 0; i < ret.Length; i++)
                    ret[i] = module.LChTbl[i];
                return ret;
            }
            set
            {
                for (UInt32 i = 0; (i < value.Length) && (i < LTR114_MAX_LCHANNEL); i++)
                {
                    module.LChTbl[i] = value[i];
                }
            }
        }

        public void SetLChannel(int lch_idx, LCHANNEL lch)
        {
            module.LChTbl[lch_idx] = lch;
        }
        

        public void SetLChannel(int lch_idx,  byte Channel, MeasModes MeasMode, Ranges Range)   {
            module.LChTbl[lch_idx] = new LCHANNEL(MeasMode, Channel, Range);
        }

        public LCHANNEL GetLChannel(int lch_idx)
        {
            return module.LChTbl[lch_idx];
        }

        public UInt16 Interval { get { return module.Interval; } set { module.Interval = value; } }
        public Features SpecialFeatures { get { return module.SpecialFeatures; } set { module.SpecialFeatures = value; } }
        public byte AdcOsr { get { return module.AdcOsr; } set { module.AdcOsr = value; } }
        public SyncModes SyncMode { get { return module.SyncMode; } set { module.SyncMode = value; } }
        public int FreqDivider { get { return module.FreqDivider; } set { module.FreqDivider = value; } }
        public int FrameLength { get { return module.FrameLength; } }
        public bool Active { get { return module.Active; } }
        public INFO ModuleInfo { get { return module.ModuleInfo; } }

        public double AdcFreq { get { return (double)LTR114_CLOCK * 1000 / (LTR114_ADC_DIVIDER * module.FreqDivider); } }
        public double ChFreq { get { return AdcFreq / (module.LChQnt + module.Interval); } }

        public _LTRNative.LTRERROR FindFreqDivider(double adcFreq, out double resultAdcFreq)
        {
            return LTR114_FindFreqDivider(adcFreq, out module.FreqDivider, out resultAdcFreq);
        }

        public _LTRNative.LTRERROR FindFreqDivider(double adcFreq)
        {
            double tmp;
            return FindFreqDivider(adcFreq, out tmp);
        }

        public _LTRNative.LTRERROR FindInterval(double chFreq, CorrectionModes correctionMode, out double resultChFreq)
        {
            return LTR114_FindInterval(chFreq, AdcFreq, LChQnt, correctionMode, out  module.Interval, out resultChFreq);
        }

        public _LTRNative.LTRERROR FindInterval(double chFreq, CorrectionModes correctionMode)
        {
            double tmp;
            return LTR114_FindInterval(chFreq, AdcFreq, LChQnt, correctionMode, out  module.Interval, out tmp);
        }

    }
}
