using System;
using System.Drawing;
using System.IO;

// this is the actual extended CyberSc DLL with image pre-processing facilities
using CyberEx = CyberScExtend.CyberSc;

// short aliases for CyberSc Structures / Enums (see API docs for details) used in the CyberScExtend namespace
using CyberConfigs = CyberScExtend.ConfigSettings;
using CyberResolutions = CyberScExtend.Resolutions;
using CyberDeviceCode = CyberScExtend.DeviceCode;
using CyberCvErrors = CyberScExtend.cvErrors;


namespace CyberScExtendDemo
{
    internal static class Program
    {
        // basic required callbacks
        private static CyberEx.ScanDataCallback scanCb = ScanCallback;
        private static CyberEx.ErrorReportCallback errCb = ErrorCallback;
        private static CyberEx.ProcessedScanCallback ProScanCb = ProcessecdScanCallback ;

        [STAThread]
        private static void Main(string[] args)
        {
            // Init wrapper
            // int ver = CyberEx.SInit(scanCb, errCb);
            int ver = CyberEx.SInitWithPreProcessing(ProScanCb, errCb);
            if (ver == 0)
            {
                Console.WriteLine("Init failed.");
                return;
            }
            CyberEx.SPOff(); // Ensure Scanner is ON (and printer is off) before config

            // Enable pre-processing (deskew, autocrop, barcode reading etc) - optional
            bool removeWhiteSpcs = false;
            bool autoFillEBlackEdges = true;
            bool autoReadBarcodes = true;
            bool autoDeskew = true;
            bool autoCrop = true;
            CyberEx.SetImageProcessingSettings(removeWhiteSpcs, autoFillEBlackEdges, autoReadBarcodes, autoDeskew, autoCrop);


            // write out returned version info based on (LSB to MSB):
            // - LSB: Hardware version
            // - Byte 2: Device driver version
            // - Byte 3: CyberSc Internal DLL version -  - different from X.Y.Z format file version 
            // - MSB: Not used Reserved for application version
            byte hardwareVersion = (byte)(ver & 0xFF);
            byte driverVersion = (byte)((ver >> 8) & 0xFF);
            byte dllVersion = (byte)((ver >> 16) & 0xFF);
            Console.WriteLine($" - Init ==>  hardware ver {hardwareVersion}");
            Console.WriteLine($" - Init ==>  driver ver {driverVersion}");
            Console.WriteLine($" - Init ==>  Internal Dll ver {dllVersion}");

            // Stop scanner (ensure interface free for printing)
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STOPSCAN, UIntPtr.Zero);

            // Enter print mode (native sequence: ScPOn/ScPOff exposed by original DLL only).
            // NOTE: Wrapper currently does not expose print functions; if you need printing,
            // keep original native P/Invoke for print-only calls or extend CyberScExtend with them.

            // Example: If CyberSc wrapper is extended later to add ScPOn/ScPString etc, we can them here.
            // For now this demonstrates scanner setup only.

            // Configure device & resolution then restart scanner polling:
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_DEV, (UIntPtr)(int)CyberDeviceCode.EDEV200A4_1728);    // 200 DPI

            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)(int)CyberResolutions.EACRmFULL256);    // 24 bit color
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)0x60);

            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STARTSCAN, UIntPtr.Zero);

            Console.WriteLine(" - Ready. Insert paper into scanner OR Press any key to exit.");
            Console.ReadKey();
        }

        // ----------------------------------------------------------------------------------------
        // Callback implementations
        // ----------------------------------------------------------------------------------------
        private static void ScanCallback(ref CyberScExtend.CyberImg img)
        {
            Console.WriteLine($" - Scan callback ==>  w={img.wWid}, h={img.wHei},  col mode={img.wNoClr}, Bit planes={img.bPlanes}");
        }

        private static void ProcessecdScanCallback(Bitmap bitmap, ref CyberScExtend.CyberImg img, string barcode)
        {
            Console.WriteLine($" - Processed Scan callback ==>  w={img.wWid}, h={img.wHei},  col mode={img.wNoClr}, Bit planes={img.bPlanes}");
            if (!string.IsNullOrEmpty(barcode))
            {
                Console.WriteLine($" - Processed Scan callback ==>  Barcode: {barcode}");
            }

            // write out image to file for demo purposes in png format in scans folder
            string folderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scans");
            Directory.CreateDirectory(folderPath);
            string filename = Path.Combine(folderPath, $"scan_{DateTime.Now.Ticks}.png");
            bitmap.Save(filename, System.Drawing.Imaging.ImageFormat.Png);  

            Console.WriteLine($" - Saved scan to: {filename}");
        }

        private static void ErrorCallback(uint dwError, UIntPtr dwType)
        {
            switch (dwError)
            {
                case (uint)CyberCvErrors.CV_BARCODE:        // Deprecated - use the processedScanCallback instead
                case (uint)CyberCvErrors.CV_DEBUG:
                    // IntPtr strPtr = (IntPtr)dwType.ToUInt64();
                    // string barcodeStr = Marshal.PtrToStringAnsi(strPtr);
                    // we could do the above but the wrapper provides a helper function with error checking
                    string message = CyberEx.GetCallbackString(dwType);
                    Console.WriteLine($"Message: {message}");
                    break;

                case (uint)CyberCvErrors.CV_SCANNER_NOT_CONNECTED:
                    Console.WriteLine("Scanner not connected");
                    break;

                default:
                    Console.WriteLine($"Error: {dwError}");
                    break;
            }
        }
    }
}