using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace DMatrixEncode
{
   // coding schemes 
   public enum T_SCHEME { schAUTO = 0, schASC, schC40, sch256, schEDF, schTXT, schX12 };

   // character dimensions 
   public enum T_DIMENSION
   {
      dim_auto = -1,                                 // auto
      dim_10_10 = 0, dim_12_12, dim_14_14, dim_16_16,// square         // 0- 3
      dim_18_18, dim_20_20, dim_22_22, dim_24_24,    //                // 4- 7
      dim_26_26, dim_32_32, dim_36_36, dim_40_40,    //                // 8-11
      dim_44_44, dim_48_48, dim_52_52, dim_64_64,    //                //12-15
      dim_72_72, dim_80_80, dim_88_88, dim_96_96,    //                //16-19
      dim_104_104, dim_120_120, dim_132_132, dim_144_144,   //         square //20-23
      dim_18_8, dim_32_8, dim_26_12,                 // rectangle      //24-26
      dim_36_12, dim_36_16, dim_48_16               //      rectangle //27-29
   };

   public enum T_RESULT
   {
      DME_RES_OK = 0,    // Ok
      DME_RES_LONG1,     // Too long text for this matrix size
      DME_RES_LONG2,     // Too long text for all sizes
      DME_RES_ANSIX12,   // Unresolved character in ANSIX12 encodation
      DME_RES_EDIFACT,   // Unresolved character  for Edifact encodation
      DME_RES_AU_FAIL,   // Authentication failure
      DME_RES_TYPE,      // Type error
      DME_RES_DIMENSION, // Dimension error
      DME_RES_PARAMS,    // Invalid parameter
      DME_RES_UNREGISTR, // Unregistered version SDK
      DME_RES_UserID,    // User ID not found or error
      DME_RES_STRUCTURE, // structure error
      DME_RES_CRYPTO,    // data damaged (crypto error)
      DME_RES_MATRIX,    // incorrect MATRIX
      DME_RES_EMPTY,     // MATRIX is EMPTY (нет кодированных данных)
      DME_RES_ProdID,    // Product ID error
      DME_RES_UNREALIZED,// Unrealized
      DME_RES_ALGORITHM, // 
      DME_RES_UNKNOWN    // UnKnown error
   };

   public struct DMatrixSize
   {
      public int width;
      public int height;
   }

   public class DMatrixEncoder
   {
      private static readonly int[] dimentions_ess200 =
      {
         10,10,   12,12,   14,14,   16,16,   18,18,   20,20,   22,22,   24,24,
         26,26,   32,32,   36,36,   40,40,   44,44,   48,48,   52,52,   64,64,
         72,72,   80,80,   88,88,   96,96,   104,104, 120,120, 132,132, 144,144,
         8,18,    8,32,    12,26,   12,36,   16,36,   16,48
      };

      private const string DLL_NAME = "ECC200.dll";
      [DllImport(DLL_NAME, EntryPoint = "CreateSymbol_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern IntPtr CreateSymbol_ECC200();

      [DllImport(DLL_NAME, EntryPoint = "DeleteSymbol_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern void DeleteSymbol_ECC200(IntPtr encoder);

      [DllImport(DLL_NAME, EntryPoint = "Clear_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern void Clear_ECC200(IntPtr encoder);

      [DllImport(DLL_NAME, EntryPoint = "SetScheme_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern Int32 SetScheme_ECC200(IntPtr encoder, T_SCHEME schema);

      [DllImport(DLL_NAME, EntryPoint = "GetScheme_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern T_SCHEME GetScheme_ECC200(IntPtr encoder);

      [DllImport(DLL_NAME, EntryPoint = "SetDimension_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern Int32 SetDimension_ECC200(IntPtr encoder, T_DIMENSION dim);

      [DllImport(DLL_NAME, EntryPoint = "GetDimension_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern T_DIMENSION GetDimension_ECC200(IntPtr encoder);

      [DllImport(DLL_NAME, EntryPoint = "SetSignUser_ECC200", CallingConvention = CallingConvention.StdCall)]
      internal static extern Int32 SetSignUser_ECC200(IntPtr encoder, bool sign);

      [DllImport(DLL_NAME, EntryPoint = "SetSignProduct_ECC200", CallingConvention = CallingConvention.StdCall)]
      unsafe internal static extern Int32 SetSignProduct_ECC200(IntPtr encoder, bool sign, byte* ProductID);

      [DllImport(DLL_NAME, EntryPoint = "SetDataToEncoding_ECC200", CallingConvention = CallingConvention.StdCall)]
      unsafe internal static extern Int32 SetDataToEncoding_ECC200(IntPtr encoder, byte* data, Int32 dataSize);

      [DllImport(DLL_NAME, EntryPoint = "WriteEncodedSymbolToBMP_ECC200", CallingConvention = CallingConvention.StdCall)]
      unsafe internal static extern Int32 WriteEncodedSymbolToBMP_ECC200(IntPtr encoder, Int32 modul, Int32 QZ, byte* filePath);

      [DllImport(DLL_NAME, EntryPoint = "WriteEncodedSymbolToBuf_ECC200", CallingConvention = CallingConvention.StdCall)]
      unsafe internal static extern Int32 WriteEncodedSymbolToBuf_ECC200(IntPtr encoder, byte* dest, Int32 maxDestSize);

      private IntPtr encoder = IntPtr.Zero;
      public int lastError = 0;

      public DMatrixEncoder()
      {
         encoder = CreateSymbol_ECC200();
         if(encoder == null)
            throw new Exception("Failed CreateSymbol_ECC200!");
      }

      ~DMatrixEncoder()
      {
         if (encoder != IntPtr.Zero)
            DeleteSymbol_ECC200(encoder);
         encoder = IntPtr.Zero;
      }

      public bool SetScheme(T_SCHEME scheme)
      {
         lastError = SetScheme_ECC200(encoder, scheme);
         return lastError == 0;
      }

      public bool SetDimensions(T_DIMENSION dim)
      {
         lastError = SetDimension_ECC200(encoder, dim);
         return lastError == 0;
      }

      public DMatrixSize GetMatrixSize()
      {
         DMatrixSize res;
         T_DIMENSION dim = GetDimension_ECC200(encoder);
         res.height = dimentions_ess200[(int)dim * 2];
         res.width = dimentions_ess200[(int)dim * 2 + 1];
         return res;
      }

      public bool SetSignUser(bool sign)
      {
         lastError = SetSignUser_ECC200(encoder, sign);
         return lastError == 0;
      }

      public bool SetSignProduct(string productID)
      {
         unsafe
         {
            if (productID == null)
            {
               lastError = SetSignProduct_ECC200(encoder, false, null);
               return lastError == 0;
            }
            else
            {
               byte[] id = Encoding.UTF8.GetBytes(productID);
               fixed (byte* ptr = id)
               {
                  lastError = SetSignProduct_ECC200(encoder, true, ptr);
               }
               return lastError == 0;
            }
         }
      }

      public void Crear()
      {
         Clear_ECC200(encoder);
      }

      public bool EncodeToBuf(string data, byte[] dest)
      {
         byte[] str = Encoding.UTF8.GetBytes(data);
         unsafe
         {
            fixed (byte* ptr = str)
            {
               lastError = SetDataToEncoding_ECC200(encoder, ptr, data.Length);
               if (lastError != 0)
                  return false;
            }
            fixed(byte* ptr = dest)
            {
               lastError = WriteEncodedSymbolToBuf_ECC200(encoder, ptr, dest.Length);
               return lastError == 0;
            }
         }
      }
      
      public bool EncodeToBMP(string data, string fileName, int module, int QZ)
      {
         byte[] str = Encoding.UTF8.GetBytes(data);
         byte[] file = Encoding.UTF8.GetBytes(fileName);
         unsafe
         {
            fixed (byte* ptr = str)
            {
               lastError = SetDataToEncoding_ECC200(encoder, ptr, data.Length);
               if (lastError != 0)
                  return false;
            }
            fixed (byte* ptr= file)
            {
               lastError = WriteEncodedSymbolToBMP_ECC200(encoder, module, QZ, ptr);
               return lastError == 0;
            }
         }
      }
   }
}
