using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using GrayScaleClass;

namespace icDMDecNetPro
{
    public enum DM_Rejection_Reasons
    {
        DM_RR_OK = 0,   // Decoding is OK
        DM_RR_NON = 1,   // No rectangle has been found (default error value)
        DM_RR_NODATAMATRIX = 2,   // No Data Matrix has been found
        DM_RR_BYCRIT = 3,   // Poor image quality
        DM_RR_REEDSOLOMON = 5,   // Too many errors in Reed-Solomon code
        DM_RR_NOMEMORY = 99,  // Memory limit is over
        DM_RR_UNKNOWN = 100, // The exception occured
        DM_RR_DISCONNECTED = 200
    }
    public enum DM_Break_Reason
    {
        ALL_INSPECTED = 0,
        TIMEOUT = 1,
        TERMINATED = 2
    }
    public enum DM_CELL_COLOR
    {
        CL_BLACKONWHITE = 1,
        CL_WHITEONBLACK = 2,
        CL_ANY = 3
    }
    public enum DM_MIRROR_MODE
    {
        MM_NORMAL = 1,
        MM_MIRROR = 2,
        MM_ANY = 3
    }
    public enum DM_DECODER_SPEED
    {
        SP_ROBUST = 0,
        SP_FAST            = 1,
        SP_GRID_ADJUSTMENT = 2,
        SP_EQUALIZATION    = 3, //!< re-equalizing the regions of probable Data Matrix
        SP_EQUAL_GRADJ     = 4
       ,SP_ACCURATE = 5
       ,SP_ACCURATEPLUS = 6
    }
    public enum DM_SPEED
    {
       DMSP_ULTIMATEPLUS  = DM_DECODER_SPEED.SP_ACCURATEPLUS, //!< most careful and time-expensive
       DMSP_ULTIMATE      = DM_DECODER_SPEED.SP_ACCURATE,     //!< more careful and time-expensive
       DMSP_REGULAR       = DM_DECODER_SPEED.SP_EQUAL_GRADJ,  //!< recommended ratio "speed/quality"
       DMSP_EXPRESS       = DM_DECODER_SPEED.SP_ROBUST        //!< basic algorithm (more fast)
   }
    public enum DM_LABEL_MODE
    {
        LM_STANDARD  = 0, // ISO 16022
        LM_DOTPEEN   = 1,
        LM_FAX       = 2,
        LM_ST_DOT    = 3      //!< Combines Standard & Dotpeen
    }
    
    public enum DM_QUALITY_MASK
    {
        DM_QM_NO  = 0x0000,
        DM_QM_ALL = 0x7FFF,
        DM_PQM_ALL_v2 = 0xFFFF
   }
    public enum DM_FILTER_MODE
    {
        FM_NON        = 0, //!< no filter
        FM_SHARP1     = 1, //!< First  Filter Mode (recursive sharpening)
        FM_SHARP2     = 2, //!< Second Filter Mode (recursive sharpening)
        FM_SHARPMASK  = 3, //!< Sharpening Mask Filter
        FM_AUTO       = 4  //!< Auto selection of sharpening parameters
       ,FM_BWR        = 5  //!< Bar Width Reduction (spaces enlargement)
       ,FM_SM_BWR     = 6  //!< Sharpening Mask + Bar Width Reduction
    }
    public enum DMQZ_MODE{
        DMQZ_NORMAL   = 0 //!< allows QZ>= 5.7 pixels
       ,DMQZ_SMALL    = 1 //!< allows QZ>= 4.5 pixels, affects speed and robustness
    }

    public class TRowCols
    {
        protected float[ ] FRowCols = new float[ 8 ];
        protected bool FValid;
        public float this[ int ix ]
        {
            get
            {
                if ( ix < 0 ) return -1;
                if ( ix > 7 ) return -1;
                if ( !FValid ) return -1;
                return FRowCols[ ix ];
            }
        }
        public bool Valid { get { return FValid; } }
    }

    public class TDM_Quality
    {
        public Single symbol_contrast;
        public Single axial_nonuniformity;
        public Single grid_nonuniformity;
        public Single fixed_pattern_damage;   // average
        public Single unused_error_correction;
        public Single vertical_print_growth;
        public Single horizontal_print_growth;

        public Single symbol_contrast_grade;
        public Single axial_nonuniformity_grade;
        public Single grid_nonuniformity_grade;
        public Single fixed_pattern_damage_grade;
        public Single unused_error_correction_grade;
        public Single modulation_grade;
        public Single decode_grade; // (min of grades)
        public Single overall_grade; // N/A because we don't support several scans
    }

    public class TDM_Info
    {
        public TDM_Info (IntPtr DLLInfo)
        {
            SetInfo( DLLInfo );
        }
        unsafe private void SetInfo (IntPtr DLLInfo)
        {
            FInfo = (T_US_DM_Info*)DLLInfo;
            FRowCols.SetInfo( FInfo );
            FQuality.SetInfo( FInfo );
        }

//        #region PrivateFields
        unsafe private T_US_DM_Info* FInfo;
        private unsafe struct T_US_DM_Info
        { //result of decoding per each Data Matrix symbol
            public Single rowcols0;    // symbol corners
            public Single rowcols1;    // symbol corners
            public Single rowcols2;    // symbol corners
            public Single rowcols3;    // symbol corners
            public Single rowcols4;    // symbol corners
            public Single rowcols5;    // symbol corners
            public Single rowcols6;    // symbol corners
            public Single rowcols7;    // symbol corners
            public Int32 pchlen;	   // lenght of decoded byte array
            public Byte* pch;		   // pointer to that array
            public Int32 RSErr;		   // number of reed solomon errors
            public Int32 VDim, HDim;   // vertical and horizontal dimensions of Data Matrix
            public Int32 saTotalSymbolsNumber 	// structured append: total number of matrices
                         , saSymbolPosition		// current matrix index
                         , saFileID1			// file identificators
                         , saFileID2;
            public Int32 mirrored;      // = true if mirrored
            public Int32 dotpeenstage;  // = true if dotpeenstage
            public Int32 matrixcolor;   // detected color of Data Matrix
            //TDM_Quality quality;
            public Single symbol_contrast;
            public Single axial_nonuniformity;
            public Single grid_nonuniformity;
            public Single fixed_pattern_damage;   // average
            public Single unused_error_correction;
            public Single vertical_print_growth;
            public Single horizontal_print_growth;

            public Single symbol_contrast_grade;
            public Single axial_nonuniformity_grade;
            public Single grid_nonuniformity_grade;
            public Single fixed_pattern_damage_grade;
            public Single unused_error_correction_grade;
            public Single modulation_grade;
            public Single decode_grade; // (min of grades)
            public Single overall_grade; // N/A because we don't support several scans
        }

        class T_US_RowCols : TRowCols
        {
            unsafe public void SetInfo (T_US_DM_Info* FInfo)
            {
                FValid = false;
                if ( FInfo == (T_US_DM_Info*)null ) return;
                //if ( FInfo->rcFlag == 0 ) return;

                FRowCols[ 0 ] = FInfo->rowcols0;
                FRowCols[ 1 ] = FInfo->rowcols1;
                FRowCols[ 2 ] = FInfo->rowcols2;
                FRowCols[ 3 ] = FInfo->rowcols3;
                FRowCols[ 4 ] = FInfo->rowcols4;
                FRowCols[ 5 ] = FInfo->rowcols5;
                FRowCols[ 6 ] = FInfo->rowcols6;
                FRowCols[ 7 ] = FInfo->rowcols7;
                FValid = true;
            }
        }
        class T_US_Quality : TDM_Quality
        {
            unsafe public void SetInfo (T_US_DM_Info* FInfo)
            {
                if ( FInfo == (T_US_DM_Info*)null )
                {
                    symbol_contrast = -1;
                    axial_nonuniformity = -1;
                    fixed_pattern_damage = -1;
                    grid_nonuniformity = -1;
                    unused_error_correction = -1;
                    vertical_print_growth = -1;
                    horizontal_print_growth = -1;

                    symbol_contrast_grade = -1;
                    axial_nonuniformity_grade = -1;
                    grid_nonuniformity_grade = -1;
                    fixed_pattern_damage_grade = -1;
                    unused_error_correction_grade = -1;
                    modulation_grade = -1;
                    decode_grade = -1;
                    overall_grade = -1;
                }
                else
                {
                    symbol_contrast = FInfo->symbol_contrast;
                    axial_nonuniformity = FInfo->axial_nonuniformity;
                    fixed_pattern_damage = FInfo->fixed_pattern_damage;
                    grid_nonuniformity = FInfo->grid_nonuniformity;
                    unused_error_correction = FInfo->unused_error_correction;
                    vertical_print_growth = FInfo->vertical_print_growth;
                    horizontal_print_growth = FInfo->horizontal_print_growth;

                    symbol_contrast_grade = FInfo->symbol_contrast_grade;
                    axial_nonuniformity_grade = FInfo->axial_nonuniformity_grade;
                    grid_nonuniformity_grade = FInfo->grid_nonuniformity_grade;
                    fixed_pattern_damage_grade = FInfo->fixed_pattern_damage_grade;
                    unused_error_correction_grade = FInfo->unused_error_correction_grade;
                    modulation_grade = FInfo->modulation_grade;
                    decode_grade = FInfo->decode_grade;
                    overall_grade = FInfo->overall_grade;
                }
            }
        }

        private T_US_Quality FQuality = new T_US_Quality( );
        private T_US_RowCols FRowCols = new T_US_RowCols( );
        unsafe private bool IsInfo ( )
        {
            return ( FInfo != null );
        }

//        #endregion

        public int RSErr
        {
            get
            {
                if ( !IsInfo( ) )
                    return -1;

                unsafe { return FInfo->RSErr; };
            }
        }

        public int VDim
        {
            get
            {
                if ( !IsInfo( ) )
                    return -1;

                unsafe { return FInfo->VDim; };
            }
        }
        public int HDim
        {
            get
            {
                if ( !IsInfo( ) )
                    return -1;

                unsafe { return FInfo->HDim; };
            }
        }
        public int ActColor
        {
            get
            {
                if (!IsInfo())
                    return -1;

                unsafe { return FInfo->matrixcolor; };
            }
        }
        public bool Mirrored
        {
            get
            {
                if ( !IsInfo( ) ) return false;
                unsafe { if ( FInfo->mirrored == 0 ) return false; }
                return true;
            }
        }

        public bool DotPeen
        {
            get
            {
                if ( !IsInfo( ) ) return false;
                unsafe { if ( FInfo->dotpeenstage == 0 ) return false; }
                return true;
            }
        }

        private byte[ ] GetDecodedBytes (StringConvertor SC)
        {
            byte[ ] Result;
            unsafe
            {
                if ( IsInfo( ) )
                {
                    Result = SC.PCharToArray( FInfo->pch, FInfo->pchlen );
                }
                else
                {
                    return new byte[ 0 ];
                }
            }
            return Result;
        }

        public byte[ ] GetDecodedBytes ( )
        {
            StringConvertor SC = new StringConvertor( );
            return GetDecodedBytes( SC );
        }
        public String GetDecodedChars (int codepage)
        {
            StringConvertor SC = new StringConvertor( codepage );
            byte[ ] A = GetDecodedBytes( SC );
            return SC.ByteArrayToString( A );
        }
        public String GetDecodedChars ( )
        {
            StringConvertor SC = new StringConvertor( );
            byte[ ] A = GetDecodedBytes( );
            return SC.ByteArrayToString( A );
        }

        unsafe public String GetSymbologyID()
        {
            if (FInfo->pch != null)
            {
                char[] SIDch = new char[4];
                string SID;// = "123";
                SIDch[0] = (char)(FInfo->pch)[-3];
                SIDch[1] = (char)(FInfo->pch)[-2];
                SIDch[2] = (char)(FInfo->pch)[-1];
                SIDch[3] = (char)0;
                SID = new string(SIDch);
                return SID;
            }
            return "N/A";
        }
        ////////

        public TRowCols RowCols
        {
            get
            {
                return FRowCols;
            }
        }

        public TDM_Quality Quality
        {
            get { return FQuality; }
        }

    }
    // TDM_Info

    public class icDataMatrix_Decoder_Pro
    {
      #region Private_Fields
      private enum DM_AdvansedOptions
      {
         DM_PQ_APERTURE_PERCENTS = 1001
      }

      [DllImport("DM_EP_64.dll", EntryPoint = "Connect_DM_Decoder", CallingConvention = CallingConvention.StdCall)]
      internal static extern IntPtr Connect_DM_Decoder ([In] Int32 MaxRowCount, [In] Int32 MaxColCount);

      [DllImport( "DM_EP_64.dll", EntryPoint = "Disconnect_DM_Decoder", CallingConvention = CallingConvention.StdCall )]
        internal static extern void Disonnect_DM_Decoder ([In] IntPtr Decoder);

        [DllImport("DM_EP_64.dll", EntryPoint = "Create_DM_OptionsP", CallingConvention = CallingConvention.StdCall)]
        internal static extern IntPtr Create_DM_OptionsP(
              [In] IntPtr Decoder
            , [In] Int32 maxDMCount   // from 1 to 400
            , [In] Int32 cellColor    // CL_BLACKONWHITE, CL_WHITEONBLACK, CL_ANY
            , [In] Int32 mirrorMode   // MM_NORMAL, MM_MIRROR, MM_ANY
            , [In] Int32 speedMode    // DMSP_ULTIMATE,... SP_FAST
            , [In] Int32 qualityMask  // DM_QM_NO, DM_QM_ALL
            , [In] Int32 labelMode    // LM_STANDARD, LM_DOTPEEN, ...
            , [In] Int32 timeOut
            , [In] Int32 filterMode
            , [In] Int32 qzMode
            );

        [DllImport( "DM_EP_64.dll", EntryPoint = "Delete_DM_Options", CallingConvention = CallingConvention.StdCall )]
        internal static extern void Delete_DM_Options ([In] IntPtr Options);

        [DllImport( "DM_EP_64.dll", EntryPoint = "GetDM_ImageInfo", CallingConvention = CallingConvention.StdCall )]
        internal static extern IntPtr GetDM_ImageInfo ([In] IntPtr DecoderO);

        [DllImport( "DM_EP_64.dll", EntryPoint = "GetDM_Info", CallingConvention = CallingConvention.StdCall )]
        internal static extern IntPtr GetDM_Info ([In] IntPtr DecoderO, [In] Int32 SymbolNumber);

        [DllImport( "DM_EP_64.dll", EntryPoint = "DecodeBitsF", CallingConvention = CallingConvention.StdCall )]
        internal static extern int DecodeBitsF ([In] IntPtr DecoderO, [In] Int32 RowCount, [In] Int32 ColCount, [In] IntPtr[ ] PPBits);

        [DllImport("DM_EP_64.dll", EntryPoint = "SetDM_DecoderOption", CallingConvention = CallingConvention.StdCall)]
        internal static extern int SetDM_DecoderOption([In] IntPtr DecoderO, Int32 option, Int32 value);

        private IntPtr FDecoderW = IntPtr.Zero;
        private IntPtr DecoderO = IntPtr.Zero;
        private bool FOptionChanged = true;
        private Int32 FDMCount = 0;        // number of well decoded symbols within image
        private DM_Rejection_Reasons FRejectionReason = DM_Rejection_Reasons.DM_RR_DISCONNECTED;// if no one matrix has been decoded
        private int FBreakReason = 0;

      public icDataMatrix_Decoder_Pro(int MaxImageHeight, int MaxImageWidth)
      {
         FDecoderW = Connect_DM_Decoder(MaxImageHeight, MaxImageWidth);
         FOptMode.maxDMCount = 100;
         FOptMode.mirrorMode = (int)DM_MIRROR_MODE.MM_ANY;
         FOptMode.speedMode = (int)DM_SPEED.DMSP_ULTIMATEPLUS; //.DMSP_REGULAR; 
         FOptMode.qualityMask = (int)DM_QUALITY_MASK.DM_QM_ALL;
         FOptMode.labelMode = (int)DM_LABEL_MODE.LM_ST_DOT;
         FOptMode.cellColor = (int)DM_CELL_COLOR.CL_ANY;
         FOptMode.filterMode = (int)DM_FILTER_MODE.FM_AUTO;
         FOptMode.qzMode = (int)DMQZ_MODE.DMQZ_NORMAL;
         FOptMode.timeOut = 0;
      }
        public void Disconnect ( )
        {
            unsafe
            {
                fixed ( void* PD = &FDecoderW)
                {
                    IntPtr P = new IntPtr( PD );
                    Disonnect_DM_Decoder( P );
                }
            }
        }

        private unsafe struct T_US_DM_OptMode
        {
            public Int32 maxDMCount;   // from 1 to 100
            public Int32 cellColor;    // CL_BLACKONWHITE, CL_WHITEONBLACK2, CL_ANY
            public Int32 mirrorMode;   // MM_NORMAL, MM_MIRROR, MM_ANY
            public Int32 speedMode;    // SP_ROBUST, SP_FAST
            public Int32 qualityMask;  // DM_QM_NO, DM_QM_AXNU, DM_QM_PRGR, DM_QM_SYMCTR, DM_QM_ALL
            public Int32 labelMode;    // LM_NORMAL, LM_DOTPEEN, LM_FAX
            public Int32 timeOut;
            public Int32 filterMode;
            public Int32 qzMode;
        }

        private unsafe struct T_US_DM_ImageInfo
        { // averall image properties
            public Int32 DMCount;        // number of well decoded symbols within image
            public Int32 RejectionReason;// if no one matrix has been decoded
            public Int32 BreaknReason;// if no one matrix has been decoded
        }

        private T_US_DM_OptMode FOptMode;

         public Boolean SetApertureSizeInPercents(int aperturePercents)
         {
            return SetDM_DecoderOption(FDecoderW, (int)DM_AdvansedOptions.DM_PQ_APERTURE_PERCENTS, aperturePercents) == 0;
         }

        private int DecodeBitsO (Int32 RowCount, Int32 ColCount, IntPtr[ ] PPBits)
        {
            int Result = 0;
            if ( ( DecoderO == IntPtr.Zero ) || ( FOptionChanged ) )
            {
                if ( DecoderO != IntPtr.Zero )
                {
                    unsafe
                    {
                        fixed ( void* PO = &DecoderO )
                        {
                            IntPtr P = new IntPtr( PO );
                            Delete_DM_Options( P );
                            DecoderO = IntPtr.Zero;
                        }
                    }
                }

                unsafe
                {
                    IntPtr PD = FDecoderW;

                     DecoderO = Create_DM_OptionsP(PD
                               , FOptMode.maxDMCount
                               , FOptMode.cellColor
                               , FOptMode.mirrorMode
                               , FOptMode.speedMode
                               , FOptMode.qualityMask
                               , FOptMode.labelMode
                               , FOptMode.timeOut
                               , FOptMode.filterMode
                               , FOptMode.qzMode
                               );
                    FOptionChanged = false;
                }
            }
            Result = DecodeBitsF( DecoderO, RowCount, ColCount, PPBits );
            return Result;
        }

        unsafe private int Get_maxDMCount ( )
        {
            return FOptMode.maxDMCount;
        }
        unsafe private void Set_maxDMCount (int Value)
        {
            if ( FOptMode.maxDMCount == Value ) return;
            unsafe { FOptMode.maxDMCount = Value; }
            FOptionChanged = true;
        }
        unsafe private int Get_CellColor ( )
        {
            return FOptMode.cellColor;
        }
        unsafe private void Set_CellColor (int Value)
        {
            if ( FOptMode.cellColor == Value ) return;
            unsafe { FOptMode.cellColor = Value; }
            FOptionChanged = true;
        }
        unsafe private int Get_QualityMask ( )
        {
            return FOptMode.qualityMask;
        }
        unsafe private void Set_QualityMask (int Value)
        {
            if ( FOptMode.qualityMask == Value ) return;
            unsafe { FOptMode.qualityMask = Value; }
            FOptionChanged = true;
        }
        unsafe private int Get_MirrorMode ( )
        {
            return FOptMode.mirrorMode;
        }
        unsafe private void Set_MirrorMode (int Value)
        {
            if ( FOptMode.mirrorMode == Value ) return;
            unsafe { FOptMode.mirrorMode = Value; }
            FOptionChanged = true;
        }

        unsafe private int Get_SpeedMode ( )
        {
            return FOptMode.speedMode;
        }
        unsafe private void Set_SpeedMode (int Value)
        {
            if ( FOptMode.speedMode == Value ) return;
            unsafe { FOptMode.speedMode = Value; }
            FOptionChanged = true;
        }

        unsafe private int Get_LabelMode ( )
        {
            return FOptMode.labelMode;
        }
        unsafe private void Set_LabelMode (int Value)
        {
            if ( FOptMode.labelMode == Value ) return;
            unsafe { FOptMode.labelMode = Value; }
            FOptionChanged = true;
        }

        unsafe private int Get_FilterMode ( )
        {
            return FOptMode.filterMode;
        }
        unsafe private void Set_FilterMode (int Value)
        {
            if ( FOptMode.filterMode == Value ) return;
            unsafe { FOptMode.filterMode = Value; }
            FOptionChanged = true;
        }


        unsafe private int Get_QzMode()
        {
            return FOptMode.qzMode;
        }
        unsafe private void Set_QzMode(int Value)
        {
            if (FOptMode.qzMode == Value) return;
            unsafe { FOptMode.qzMode = Value; }
            FOptionChanged = true;
        }

        unsafe private int Get_TimeOut()
        {
            return FOptMode.timeOut;
        }
        unsafe private void Set_TimeOut (int Value)
        {
            if ( Value > 100000 ) Value = 100000;
            if ( FOptMode.timeOut == Value ) return;
            unsafe { FOptMode.timeOut = Value; }
            FOptionChanged = true;
        }

        #endregion

        public int MaxDMCount
        {
            get { return Get_maxDMCount( ); }
            set { Set_maxDMCount( value ); }
        }
        public DM_CELL_COLOR CellColor
        {
            get { return (DM_CELL_COLOR)Get_CellColor( ); }
            set { Set_CellColor( (int)( value ) ); }
        }
        public DM_QUALITY_MASK QualityMask
        {
            get { return (DM_QUALITY_MASK)Get_QualityMask( ); }
            set { Set_QualityMask( (int)value ); }
        }
        public DM_MIRROR_MODE MirrorMode
        {
            get { return (DM_MIRROR_MODE)Get_MirrorMode( ); }
            set { Set_MirrorMode( (int)value ); }
        }
        public DM_LABEL_MODE LabelMode
        {
            get { return (DM_LABEL_MODE)Get_LabelMode( ); }
            set { Set_LabelMode( (int)value ); }
        }
//        public DM_DECODER_SPEED SpeedMode
//        {
//            get { return (DM_DECODER_SPEED)Get_SpeedMode(); }
//            set { Set_SpeedMode((int)value); }
//        }
        public int SpeedMode
        {
            get { return (int)Get_SpeedMode(); }
            set { Set_SpeedMode((int)value); }
        }

        public DM_FILTER_MODE FilterMode
        {
            get { return (DM_FILTER_MODE)Get_FilterMode(); }
            set { Set_FilterMode( (int)value ); }
        }

        public int QzMode
        {
            get { return Get_QzMode(); }
            set { Set_QzMode(value); }
        }

        public int Timeout
        {
            get { return Get_TimeOut( ); }
            set { Set_TimeOut( value ); }
        }

        public int DecodeImage (Bitmap BMP)
        {
            int Result;
            GrayScale G = new GrayScale( BMP );
            G.OnDecBits += this.DecodeBitsO;

            Result = G.DecodeBits( );

            IntPtr II = GetDM_ImageInfo( DecoderO );
            unsafe
            {
                T_US_DM_ImageInfo* UII = (T_US_DM_ImageInfo*)II.ToPointer( );
                {
                    FRejectionReason = (DM_Rejection_Reasons)UII->RejectionReason;
                    FDMCount = UII->DMCount;
                    FBreakReason = UII->BreaknReason;
                }
            }
            return Result;
        }

        public DM_Rejection_Reasons Rejection_Reasons
        {
            get { return FRejectionReason; }
        }
        public int DMCount
        {
            get { return FDMCount; }
        }

        public TDM_Info Get_DM_Info (int Index)
        {
            TDM_Info Result = null;
            IntPtr II = IntPtr.Zero;
            if ( Index < 0 ) return null;
            if ( Index >= FDMCount ) return null;

            II = GetDM_Info( DecoderO, Index );
            Result = new TDM_Info( II );

            return Result;
        }
    }
    //DM DEcoder
}
