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

namespace icQRDecNetPro
{
    public enum QR_Rejection_Reasons
    {
        QR_SUCCESSFUL         = 0,
        QR_NO_IMAGE_FOUND     = 2,
        QR_POOR_IMAGE_QUALITY = 3,
        QR_ERROR_OF_RS_CODE   = 4,
        QR_ERR_DECODE         = 5

       ,QR_RR_DISCONNECTED = -1
    }

    public enum QR_Break_Reason
    {
        ALL_INSPECTED = 0,
        TIMEOUT = 1,
        TERMINATED = 2
    }

    public enum QRCELL_COLOR
    {
        QRCL_BLACKONWHITE = 1
       ,QRCL_WHITEONBLACK = 2
       ,QRCL_ANY = 3
    }

    public enum QR_MIRROR_MODE
    {
        MM_NORMAL = 1,
        MM_MIRROR = 2,
        MM_ANY = 3
    }

    public enum QR_DECODER_SPEED
    {
        SP_ROBUST = 0,
    }

    public enum QR_LABEL_MODE
    {
        LM_STANDARD  = 0, // ISO 18004
    }
    
    public enum QR_QUALITY_MASK
    {
        QR_QM_NO = 0x0000,
        QR_QM_ALL = 0x7FFF
    }

    public enum QR_FILTER_MODE
    {
        FM_NON        = 0, //!< no filter
        FM_AUTO       = 1,  //!< Auto selection of sharpening parameters
        FM_DOT_PEEN_TO_NORMAL = 2
   }

    public enum QRQZ_MODE{
        QRQZ_STANDARD = 0 //!< allows QZ>= 4 moduls
       ,QRQZ_SMALL    = 1 //!< allows QZ>= 1 module
    }

    public enum QRSYMBOL_MODE
    {
        QRC_NORMAL = 0     //!<- Ordinary QR Code (with 3 finder patterns)
       ,QRC_MICRO = 1     //!<- Micro QR Code (1 finder pattern)
       ,QRC_ANY = 2     //!<- Symbols of both type will be decoded simultaneously 
    };

    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 TQR_Quality
    {
        public Single symbol_contrast;
        public Single axial_nonuniformity;
        public Single grid_nonuniformity;
//        public Single fixed_pattern_damage;   // average
//        public Single QZ_damage;
        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 QZ_damage_grade;
        public Single unused_error_correction_grade;
        public Single modulation_grade;
        public Single format_grade;
        public Single version_grade;
        public Single decode_grade; // (min of grades)
        public Single overall_grade; // N/A because we don't support several scans
    }

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

//        #region PrivateFields
        unsafe private T_US_QR_Info* FInfo;
        private unsafe struct T_US_QR_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 Int16* pch;		   // pointer to that array
            public Int32 RSErr;	       //!< number of Reed Solomon errors
            public Int32 Dim;	       //!< dimension of QR Code
            public Int32 Version;      // 1 -...
            public Int32 level;        // L, M, H, Q
            public Int32 Mask;  
            public Int32 Micro_QRC;    // = true if micro
            public Int32 color;        // detected color of symbol
            public Int32 mirrored;     // = true if mirrored
            public Single symbol_contrast;
            public Single axial_nonuniformity;
            public Single grid_nonuniformity;
            public Single unused_error_correction;

            public Single symbol_contrast_grade;
            public Single axial_nonuniformity_grade;
            public Single grid_nonuniformity_grade;
            public Single unused_error_correction_grade;
            public Single modulation_grade;
            public Single fixed_pattern_damage_grade;
            public Single format_grade; 
            public Single version_grade; 
            public Single decode_grade;
            public Single overall_grade; // (min of grades)
        }

        class T_US_RowCols : TRowCols
        {
            unsafe public void SetInfo (T_US_QR_Info* FInfo)
            {
                FValid = false;
                if ( FInfo == (T_US_QR_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 : TQR_Quality
        {
            unsafe public void SetInfo (T_US_QR_Info* FInfo)
            {
                if ( FInfo == (T_US_QR_Info*)null )
                {
                    symbol_contrast = -1;
                    axial_nonuniformity = -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;
                    format_grade = -1;
                    version_grade = -1;
                    decode_grade = -1;
                    overall_grade = -1;
                }
                else
                {
                    symbol_contrast         = FInfo->symbol_contrast;
                    axial_nonuniformity     = FInfo->axial_nonuniformity;
                    grid_nonuniformity      = FInfo->grid_nonuniformity;
                    unused_error_correction = FInfo->unused_error_correction;

                    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;
                    format_grade               = FInfo->format_grade;
                    version_grade              = FInfo->version_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 Dim
        {
            get
            {
                if (!IsInfo())
                    return -1;
                unsafe { return FInfo->Dim; };
            }
        }

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

        public unsafe String level
        {
            get
            {
                char[] lev = new char[2];
                string levres;
                int val = -1;

                if (!IsInfo())
                    return val.ToString();
                lev[0] = (char)(FInfo->level);
                lev[1] = (char)0;
                levres = new string(lev);
                unsafe { return levres; };
            }
        }

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

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

        private byte[ ] GetDecodedBytes (StringConvertor SC)
        {
            byte[ ] Result;
            unsafe
            {
                if ( IsInfo( ) )
                {
                    Result = SC.PWCharToArray((ushort*)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 );
            if ((SC.CodePage != 1200) && (SC.CodePage != 1201))// UTF-32 BigEndian UTF-32 LittleEndian
            {
                byte[] A = GetDecodedBytes(SC);
                string res;
                try
                {
                    res = SC.ByteArrayToString(A);
                    return res;
                }
                catch (DecoderFallbackException)
                {
                }

            }

            unsafe
            {
                char* C;
                short* W = FInfo->pch;
                C = (char*)W;

                string res = new String(C, 0, FInfo->pchlen);

                return res;

            }

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

        unsafe public String GetSymbologyID()
        {
            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;
        }

        public TRowCols RowCols
        {
            get
            {
                return FRowCols;
            }
        }

        public TQR_Quality Quality
        {
            get { return FQuality; }
        }

    }
    // TQR_Info

    public class ic_Decoder_Pro
    {
//        #region Private_Fields

//        IntPtr ptrdec is form paraneter 

        [DllImport("QRC_PRO.dll", EntryPoint = "Disconnect_QR_Decoder", CallingConvention = CallingConvention.StdCall)]
        internal static extern void Disonnect_QR_Decoder ([In] IntPtr Decoder);

        [DllImport("QRC_PRO.dll", EntryPoint = "Create_QR_OptionsP", CallingConvention = CallingConvention.StdCall)]
        internal static extern IntPtr Create_QR_OptionsP(
              [In] IntPtr Decoder
            , [In] Int32 maxQRCount   // 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    // express
            , [In] Int32 qualityMask  // QR_QM_NO, QR_QM_ALL
            , [In] Int32 labelMode    // LM_STANDARD, LM_DOTPEEN, ...
            , [In] Int32 timeOut
            , [In] Int32 filterMode
            , [In] Int32 Symbiligy
            , [In] Int32 qzMode
            );

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

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

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

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

        private IntPtr FDecoderW = IntPtr.Zero;
        private IntPtr DecoderO = IntPtr.Zero;
        private bool FOptionChanged = true;
        private Int32 FQRCount = 0;        // number of well decoded symbols within image
        private QR_Rejection_Reasons FRejectionReason = QR_Rejection_Reasons.QR_RR_DISCONNECTED;// if no one matrix has been decoded
        private int FcornQ = 0;
/*
        public void Disconnect ( )
        {
            unsafe
            {
                fixed ( void* PD = _decoderPtr )
                {
                    IntPtr P = new IntPtr( PD );
                    Disonnect_QR_Decoder( P );
                }
            }
        }
*/
        private unsafe struct T_US_QR_OptMode
        {
            public Int32 maxQRCount;   // from 1 to 10
            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;  // QR_QM_NO, QR_QM_AXNU, QR_QM_PRGR, QR_QM_SYMCTR, QR_QM_ALL
            public Int32 labelMode;    // LM_NORMAL, LM_DOTPEEN, LM_FAX
            public Int32 timeOut;
            public Int32 filterMode;
            public Int32 symbology;    //!< QRC_NORMAL | QRC_MICRO | QRC_ANY
            public Int32 qzMode;
        }

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

        private T_US_QR_OptMode FOptMode;

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

                unsafe
                {
                   IntPtr PD = FDecoderW;

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

        unsafe private int Get_maxQRCount ( )
        {
            return FOptMode.maxQRCount;
        }
        unsafe private void Set_maxQRCount (int Value)
        {
            if ( FOptMode.maxQRCount == Value ) return;
            unsafe { FOptMode.maxQRCount = 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_symbology()
        {
            return FOptMode.symbology;
        }
        unsafe private void Set_symbology(int Value)
        {
            if (FOptMode.symbology == Value) return;
            unsafe { FOptMode.symbology = 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 MaxQRCount
        {
            get { return Get_maxQRCount( ); }
            set { Set_maxQRCount( value ); }
        }
        public QRCELL_COLOR CellColor
        {
            get { return (QRCELL_COLOR)Get_CellColor(); }
            set { Set_CellColor( (int)( value ) ); }
        }

        public QR_QUALITY_MASK QualityMask
        {
            get { return (QR_QUALITY_MASK)Get_QualityMask( ); }
            set { Set_QualityMask( (int)value ); }
        }
        public QR_MIRROR_MODE MirrorMode
        {
            get { return (QR_MIRROR_MODE)Get_MirrorMode( ); }
            set { Set_MirrorMode( (int)value ); }
        }
        public QR_LABEL_MODE LabelMode
        {
            get { return (QR_LABEL_MODE)Get_LabelMode( ); }
            set { Set_LabelMode( (int)value ); }
        }
//        public QR_DECODER_SPEED SpeedMode
//        {
//            get { return (QR_DECODER_SPEED)Get_SpeedMode(); }
//            set { Set_SpeedMode((int)value); }
//        }
        public int SpeedMode
        {
            get { return (int)Get_SpeedMode(); }
            set { Set_SpeedMode((int)value); }
        }

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

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

        public int Symbology
        {
            get { return Get_symbology(); }
            set { Set_symbology(value); }
        }

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

        public ic_Decoder_Pro (IntPtr decptr)// int MaxRows, int MaxCols)
        {
            FDecoderW = decptr;//Connect_QR_Decoder( MaxRows, MaxCols );

            FOptMode.maxQRCount = 10;
            FOptMode.mirrorMode = (int)QR_MIRROR_MODE.MM_ANY;
            FOptMode.speedMode =  (int)QR_DECODER_SPEED.SP_ROBUST;
            FOptMode.qualityMask = (int)QR_QUALITY_MASK.QR_QM_ALL;
            FOptMode.labelMode =  (int)QR_LABEL_MODE.LM_STANDARD;
            FOptMode.cellColor = (int)QRCELL_COLOR.QRCL_ANY;
            FOptMode.filterMode = (int)QR_FILTER_MODE.FM_NON;
            FOptMode.symbology = (int)QRSYMBOL_MODE.QRC_ANY;
            FOptMode.qzMode = (int)QRQZ_MODE.QRQZ_SMALL;
            FOptMode.timeOut = 0;
        }


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

            IntPtr II = GetQR_ImageInfo( DecoderO );
            unsafe
            {
                T_US_QR_ImageInfo* UII = (T_US_QR_ImageInfo*)II.ToPointer( );
                {
                    FRejectionReason = (QR_Rejection_Reasons)UII->RejectionReason;
                    FQRCount = UII->QRCount;
                }
            }
            return Result;
        }

        public QR_Rejection_Reasons Rejection_Reasons
        {
            get { return FRejectionReason; }
        }
        public int QRCount
        {
            get { return FQRCount; }
        }

        public TQR_Info Get_QR_Info (int Index)
        {
            TQR_Info Result = null;
            IntPtr II = IntPtr.Zero;
            if ( Index < 0 ) return null;
            if ( Index >= FQRCount ) return null;

            II = GetQR_Info( DecoderO, Index );
            Result = new TQR_Info( II );

            return Result;
        }
    }
    //DM DEcoder
}
