using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Runtime.InteropServices;

// 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 ScanExtendDemo
{
    public partial class MainWindow : Form
    {
        private CyberEx.ScanDataCallback m_fullRawImageCallback;         // switched to wrapper delegate
        private CyberEx.ScanPartDataCallback m_partRawImageCallback;     // switched to wrapper delegate
        private CyberEx.ErrorReportCallback m_errorReportCallback;       // switched to wrapper delegate
        private CyberEx.ProcessedScanCallback m_processedImageCallback;

        private const int MAXIMAGES = 20;
        private List<Bitmap> m_images = new List<Bitmap>(MAXIMAGES);
        private int m_currentImageIndex = -1;


        private CpuEater m_cputEater = new CpuEater();

        private bool m_Scanner_Needs_Reset = true;
        private string m_errorMsg = "";
        private string m_statusMsg = "  ==>  Scanner not Configured";

        private PixelFormat m_pixelFormat = PixelFormat.Format24bppRgb;
        private ColorPalette m_greyPalette;
        private int m_gammaValue;
        private int m_lightValue;
        private int m_bwThresholdValue;
        private int m_HorizontalBorder = -3;
        private int m_VerticalBorder = -3;
        private bool m_fillEdges = false;

        public MainWindow()
        {
            InitializeComponent();
            m_fullRawImageCallback = ScanDataHandler;
            m_partRawImageCallback = ScanPartDataHandler;
            m_errorReportCallback = ErrorMessageHandler;
            m_processedImageCallback = ProcessedScanHandler;
        }

        // On window load initialise the scanner
        private void MainWindow_Load(object sender, EventArgs e)
        {
            // We need to create 8 bit grayscale palette for 8 bit indexed scans
            m_greyPalette = new Bitmap(1, 1, PixelFormat.Format8bppIndexed).Palette;
            for (int i = 0; i < 256; i++)
            {
                m_greyPalette.Entries[i] = Color.FromArgb(i, i, i);
            }

            // Default settings
            partScanBtn.Enabled = false;
            radioButtonFullColour.Checked = true;
            radioButton200DPI.Checked = true;
            m_gammaValue = 8;
            numericUpDownGamma.Value = m_gammaValue;
            m_lightValue = 67;
            numericUpDownLight.Value = m_lightValue;    
            m_bwThresholdValue = 173;
            numericUpDownBWThreshold.Value = m_bwThresholdValue;

            // Image processing defaults
            ProcessImagecheckBox1.Checked = true;
            ProcessDefaultsCheckBox.Checked = true;
            ProcessDefaultsCheckBox.Enabled = true;
            numericUpDownHBorder.Enabled = false;
            numericUpDownHBorder.Value = -3;
            numericUpDownVBorder.Enabled = false;
            numericUpDownVBorder.Value = -3;
            FillEdgesCheckBox.Checked = m_fillEdges;
            FillEdgesCheckBox.Enabled = false;

            // InitScanner();   // calling here sometimes causes issues even though scanner is present
            // status timer 
            paperStatusTm.Enabled = true;

        }

        // on unload dispose of bitmaps 
        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (var img in m_images)
            {
                img.Dispose();
            }
            m_images.Clear();
            m_images = null;
        }

        private bool InitScanner()
        {
            Console.WriteLine("==> [Scanner Demo]: Initialising Scanner");

            // Initialise the scanner
            int ret = 0;
            if ( ProcessImagecheckBox1.Checked )
            {
                CyberEx.SResetCallbacks();

                bool removeWhiteSpecks = false;     // default (false)
                bool barcodeSearch = true;          // default (false)  **
                bool deSkew = true;                 // default (true)
                bool autoCrop = true;               // default (true)
                bool preserve1bppFormat = false;    // default (false) 

                if (ProcessDefaultsCheckBox.Checked == true)
                {
                    // Note - Defaults are automatically used if no params are passed
                    // m_fillEdges = FillEdgesCheckBox.Checked;
                    CyberEx.SetImageProcessingSettings(removeWhiteSpecks, m_fillEdges, barcodeSearch);//, 20, 3, 1.0f, 0, 0);      
                }
                else
                {
                    CyberEx.SetImageProcessingSettings(removeWhiteSpecks, m_fillEdges, barcodeSearch, deSkew, autoCrop, preserve1bppFormat,
                                                        20, 3, 1.0f, m_HorizontalBorder, m_VerticalBorder);  
                }
                ret = CyberEx.SInitWithPreProcessing(m_processedImageCallback, m_errorReportCallback);
            }
            else
            {
                CyberEx.SResetCallbacks();
                ret = CyberEx.SInit(m_fullRawImageCallback, m_errorReportCallback);
            }

            // make sure part scan is disabled initially
            int[] steps = { };
            int numSteps = steps.Length;
            CyberEx.SSetupPartScan(steps, numSteps, null);

            // - LSB: Hardware version
            // - Byte 2: Device driver version
            // - Byte 3: CyberScEx internal DLL version - different from X.Y.Z format file version 
            // - MSB: Not used Reserved for application version
            byte hardwareVersion = (byte)(ret & 0xFF);
            byte driverVersion = (byte)((ret >> 8) & 0xFF);
            byte dllVersion = (byte)((ret >> 16) & 0xFF);

            if (ret == 0)
            {
                m_errorMsg = "  ==>  Unable to init scanner";
                MessageBox.Show(m_errorMsg);
                CyberEx.SResetCallbacks();
                m_Scanner_Needs_Reset = true;
            }
            else
            {
                m_Scanner_Needs_Reset = false;
                m_statusMsg = "  ==>  Scanner Initialized:  DLL Internal version: " + dllVersion;
            }

            Console.WriteLine($"==> [Scanner Demo]: Init returned: hardware ver {hardwareVersion}");
            Console.WriteLine($"==> [Scanner Demo]: Init returned: driver ver {driverVersion}");
            Console.WriteLine($"==> [Scanner Demo]: Init returned: Dll Internal ver {dllVersion}");

            return !m_Scanner_Needs_Reset;
        }


        private void StartScnBtn_Click(object sender, EventArgs e)
        {
            if ( m_Scanner_Needs_Reset )
            {
                if (! InitScanner() ) return;
            }
            
            // Basically configure the scanner once the, "ready for scans" button is pressed
            // this is a one time configuration unless the user wants to change settings or there is a major error 
            try
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STOPSCAN, UIntPtr.Zero);

                SetColorDepth();

                SetDPI();

                SetBWThreshold();
                SetLight();       // not sure this does anything !
                SetGamma();

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

                // start scan as soon as any paper is detected rather than waiting for the full paper edge to be detected
                // Note : this needs to be set before each scan and seems to work 
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_FORCESCAN, UIntPtr.Zero);
                
                // This only needs to be set once but is a bit less flexible than CV_CAL_FORCESCAN ??
                //CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_FORCESCAN_STAY, 1);

                m_statusMsg = "  ==>  Scanner Ready";
                partScanBtn.Enabled = true;
            }
            catch (Exception ex)
            {
                m_statusMsg = "  ==>  Config Problem ";
                m_errorMsg = "  ==>  " + ex.Message.ToString();
                m_Scanner_Needs_Reset = true;
            }
        }

        // Gray Scale and Colour modes use gamma setting
        private void SetGamma()
        {
            
            m_gammaValue = (int)numericUpDownGamma.Value;
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_GAMMA, (UIntPtr)m_gammaValue);
            m_errorMsg = "";
        }

        private void SetLight()
        {
            // Light setting does not appear to have much effect ? 
            m_lightValue = (int)numericUpDownLight.Value;
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_LIGHT, (UIntPtr)m_lightValue);
            m_errorMsg = "";
        }

        // in B/W mode this provides more precise control than gamma
        // but it does not apply to grayscale / full colour modes 
        private void SetBWThreshold()
        {
            if (m_pixelFormat.Equals(PixelFormat.Format1bppIndexed))
            {
                m_bwThresholdValue = (int)numericUpDownBWThreshold.Value;
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_BW, (UIntPtr)m_bwThresholdValue);
                m_errorMsg = "";
            }
            else
            {
                m_errorMsg  = "  ==>  B/W Threshold only applies in B/W mode";
            }
        }

        private void SetDPI()
        {
            if (radioButton200DPI.Checked)
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_DEV, (UIntPtr)(int)CyberDeviceCode.EDEV200A4_1728);  // 200 DPI
            }
            else if (radioButton300DPI.Checked)
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_DEV, (UIntPtr)(int)CyberDeviceCode.EDEV300A4_2592);  // 300 DPI
            }
        }

        private void SetColorDepth()
        {
            if (radioButtonBW.Checked)
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)(int)CyberResolutions.EACRmFULL2);  // 1 bit B/W
                m_pixelFormat = PixelFormat.Format1bppIndexed;
            }
            else if (radioButtonGrayScale.Checked)
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)(int)CyberResolutions.EACRmFULL256);  // 8 bits per channel
                // mode set for 8 bit grayscale
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)0x80);
                m_pixelFormat = PixelFormat.Format8bppIndexed;
            }
            else if (radioButtonFullColour.Checked)
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)(int)CyberResolutions.EACRmFULL256);  // 8 bit per channel 
                // mode set for 24 bit colour
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_SET_RESO, (UIntPtr)0x60);
                m_pixelFormat = PixelFormat.Format24bppRgb;
            }
        }


        private void ReceiveImage(Bitmap image)
        {
            Console.WriteLine($"==> [Scanner Demo]: Received Image of size {image.Width}x{image.Height}");
            if (ProcessImagecheckBox1.Checked)
            {
                if (ProcessDefaultsCheckBox.Checked == false)
                {
                    Console.WriteLine($"==> [Scanner Demo]: Crop H border = {m_HorizontalBorder}, Crop V border = {m_VerticalBorder}");
                }
                else
                {
                    Console.WriteLine("==> [Scanner Demo]: Using default image processing settings");
                }
            }
            else Console.WriteLine("==> [Scanner Demo]: Raw image from scanner");

            // keep image list size trimmed to the defined max number 
            if (m_images.Count < MAXIMAGES)
            {
                m_images.Add(image);
                m_currentImageIndex = m_images.Count - 1;
            }
            else
            {
                m_images.RemoveAt(0);
            }

            // dispose of previous image if any
            if (scannedImageBox.Image != null)
            {
                scannedImageBox.Image.Dispose();
            }

            // resize image to fit the pictureBox but keep aspect ratio
            float aspectRatio = (float)image.Width / (float)image.Height;
            var scaledImage = new Bitmap(image, (int)((float)scannedImageBox.Size.Height * aspectRatio), scannedImageBox.Size.Height);
            scannedImageBox.Image = scaledImage;

            nextImageBtn.Enabled = (m_currentImageIndex < m_images.Count - 1);
            PreviousImageBtn.Enabled = (m_currentImageIndex > 0);
        }

        private void nextImageBtn_Click(object sender, EventArgs e)
        {
            if (m_currentImageIndex >= m_images.Count - 1) return;

            m_currentImageIndex++;
            var nextImage = m_images[m_currentImageIndex];

            // dispose of previous image if any
            if (scannedImageBox.Image != null)
            {
                scannedImageBox.Image.Dispose();
            }

            // resize image to fit the pictureBox but keep aspect ratio
            float aspectRatio = (float)nextImage.Width / (float)nextImage.Height;
            var scaledImage = new Bitmap(nextImage, (int)(float)(scannedImageBox.Size.Height * aspectRatio), scannedImageBox.Size.Height);
            scannedImageBox.Image = scaledImage;

            nextImageBtn.Enabled = (m_currentImageIndex < m_images.Count - 1);
            PreviousImageBtn.Enabled = (m_currentImageIndex > 0);
        }

        private void PreviousImageBtn_Click(object sender, EventArgs e)
        {
            if (m_currentImageIndex < 0) return;

            m_currentImageIndex--;
            var previousImage = m_images[m_currentImageIndex];

            // dispose of previous image if any
            if (scannedImageBox.Image != null)
            {
                scannedImageBox.Image.Dispose();
            }

            // resize image to fit the pictureBox but keep aspect ratio
            float aspectRatio = (float)previousImage.Width / (float)previousImage.Height;
            var scaledImage = new Bitmap(previousImage, (int)(float)(scannedImageBox.Size.Height * aspectRatio), scannedImageBox.Size.Height);
            scannedImageBox.Image = scaledImage;

            nextImageBtn.Enabled = (m_currentImageIndex < m_images.Count - 1);
            PreviousImageBtn.Enabled = (m_currentImageIndex > 0);
        }


        private void partScanBtn_Click(object sender, EventArgs e)
        {
            int[] steps = { 100, 100, 100 };
            int numSteps = steps.Length;

            // This sets up part scanning but note that at the end of the part scans the 
            // full page scan callback set by SInit or SInitWithPreProcessing is called again.
            CyberEx.SSetupPartScan(steps, numSteps, m_partRawImageCallback);

            m_statusMsg = " ==> Part scans enabled ==> Disable via the 'Ready For Scan' Button ";

            // This allows part scans to be turned off by clicking on Start Scan button again
            m_Scanner_Needs_Reset = true;
        }

        private void rejectBtn_Click(object sender, EventArgs e)
        {
            CyberEx.SRejectPaper();
        }

        private void loadBtn_Click(object sender, EventArgs e)
        {
            if (m_cputEater.IsRunning)
            {
                m_cputEater.Stop();
            }
            else
            {
                m_cputEater.Run();
            }
        }


        private void paperStatusTm_Tick(object sender, EventArgs e)
        {
            var paper = CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_PAPER, UIntPtr.Zero);
            if (paper != 0)
            {
                paperLbl.Text = "Paper Detected " + m_statusMsg + m_errorMsg;
            }
            else
            {
                paperLbl.Text = "No Paper " + m_statusMsg + m_errorMsg;
                // this option needs to be set before each scan 
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_FORCESCAN, UIntPtr.Zero);
            }
        }

        private void radioButtonGrayScale_CheckedChanged(object sender, EventArgs e)
        {
            SetColorDepth();
        }

        private void radioButtonBW_CheckedChanged(object sender, EventArgs e)
        {
            SetColorDepth();
        }

        private void radioButtonFullColour_CheckedChanged(object sender, EventArgs e)
        {
            SetColorDepth();
        }

        private void radioButton200DPI_CheckedChanged(object sender, EventArgs e)
        {
            SetDPI();
        }

        private void radioButton300DPI_CheckedChanged(object sender, EventArgs e)
        {
            SetDPI();
        }

        private void numericUpDownGamma_ValueChanged(object sender, EventArgs e)
        {
            SetGamma();
        }


        private void numericUpDownLight_ValueChanged(object sender, EventArgs e)
        {
            SetLight();
        }

        private void numericUpDownBWThreshold_ValueChanged(object sender, EventArgs e)
        {
            SetBWThreshold();
        }

        private void ProcessImagecheckBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (ProcessImagecheckBox1.Checked)
            {
                ProcessDefaultsCheckBox.Enabled = true;
                numericUpDownHBorder.Enabled = true;
                numericUpDownVBorder.Enabled = true;
                FillEdgesCheckBox.Enabled = true;
            }
            else
            {
                ProcessDefaultsCheckBox.Enabled = false;
                numericUpDownHBorder.Enabled = false;
                numericUpDownVBorder.Enabled = false;
                FillEdgesCheckBox.Enabled = false;
            }

            m_Scanner_Needs_Reset = true;
            //m_statusMsg = " ==> Click the 'Ready Scan' Button ";
            //m_errorMsg = " ==> Scanner needs reinitialising";
            CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STOPSCAN, UIntPtr.Zero);

            // call 'Ready For Scan' button so re-init for correct proessing done striaght away 
            StartScnBtn_Click(sender, e);
        }

        private void ProcessDefaultsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (ProcessDefaultsCheckBox.Checked)
            {
                numericUpDownHBorder.Enabled = false;
                numericUpDownVBorder.Enabled = false;
                FillEdgesCheckBox.Enabled = false;
            }
            else
            {
                numericUpDownHBorder.Enabled = true;
                numericUpDownVBorder.Enabled = true;
                FillEdgesCheckBox.Enabled = true;
            }

            m_Scanner_Needs_Reset = true;
            m_statusMsg = " ==> Click the 'Ready Scan' Button ";
            m_errorMsg = " ==> Scanner needs reinitialising";

        }

        private void FillEdgesCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            m_fillEdges = FillEdgesCheckBox.Checked;
            m_Scanner_Needs_Reset = true;
            m_statusMsg = " ==> Click the 'Ready Scan' Button ";
            m_errorMsg = " ==> Scanner needs reinitialising";
        }

        private void numericUpDownHBorder_ValueChanged(object sender, EventArgs e)
        {
            m_HorizontalBorder = (int)numericUpDownHBorder.Value;
            m_Scanner_Needs_Reset = true;
            m_statusMsg = " ==> Click the 'Ready Scan' Button ";
            m_errorMsg = " ==> Scanner needs reinitialising";


        }

        private void numericUpDownVBorder_ValueChanged(object sender, EventArgs e)
        {
            m_VerticalBorder = (int)numericUpDownVBorder.Value;
            m_Scanner_Needs_Reset = true;
            m_statusMsg = " ==> Click the 'Ready Scan' Button ";
            m_errorMsg = " ==> Scanner needs reinitialising";
        }

        private void CalibrateBtn_Click(object sender, EventArgs e)
        {
            string StatusText = "";
            try
            {
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STOPSCAN, UIntPtr.Zero);
                if (MessageBox.Show("Insert Blank A4 sheet of paper and then click OK", "ReCalibration",
                                     MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
                {
                    if (CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_RECALIBRATE, UIntPtr.Zero) > 0)
                    {
                        m_statusMsg = " ==> Scanner Calibrated OK";
                        m_errorMsg = "";
                    }
                    else
                    {
                        m_statusMsg = " ==> Problem ReCalibrating scanner";
                        m_errorMsg = " ==> Please try again";
                    }
                }
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STARTSCAN, UIntPtr.Zero);
            }
            catch (Exception ex)
            {
                m_statusMsg = " ==> Error Calibrating scanner";
                m_errorMsg = " ==> " + ex.Message;
                StatusText = m_statusMsg + " " + m_errorMsg;
                MessageBox.Show(StatusText, "ReCalibration Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                CyberEx.SCalibrate((int)CyberConfigs.CV_CAL_STARTSCAN, UIntPtr.Zero);
            }

        }

        // ----------------------------------------------------------------------------
        // From here down these are callback functions. These are not in the UI thread.
        // ---------------------------------------------------------------------------- 
        private void ScanDataHandler(ref CyberScExtend.CyberImg img)
        {
            var bitmap = new Bitmap(img.wWid, img.wHei, img.wWidBy, m_pixelFormat, img.pIm);

            // Grayscale images need a defined palette assigned
            if (m_pixelFormat == PixelFormat.Format8bppIndexed)
            {
                bitmap.Palette = m_greyPalette;
            }

            // raw images come upside down from the scanner so we need to flip them
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
            Invoke((Action)delegate () { ReceiveImage(new Bitmap(bitmap)); });
        }

        private void ProcessedScanHandler(Bitmap processedBitmap, ref CyberScExtend.CyberImg img, string barcode)
        {
            m_statusMsg = $" ==>  Barcode Read: {barcode}";

            // No additional processing needed here as the image is already processed
            Invoke((Action)delegate () { ReceiveImage(new Bitmap(processedBitmap)); });
        }

        private bool ScanPartDataHandler(ref CyberScExtend.CyberImg img)
        {
            var bitmap = new Bitmap(img.wWid, img.wHei, img.wWidBy, m_pixelFormat, img.pIm);

            // Grayscale images need a defined palette assigned
            if (m_pixelFormat == PixelFormat.Format8bppIndexed)
            {
                bitmap.Palette = m_greyPalette;
            }

            // images come upside down from the scanner so we need to flip them
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
            Invoke((Action)delegate () { ReceiveImage(new Bitmap(bitmap)); });
            return true;
            //return false;
        }

        // The second (dwType) parameter must be interpreted on the basis of the first parameter (dwError)
        // because for CV_BARCODE, CV_DEBUG it is actually a pointer to a string. All pointers are to memory
        // within the DLL and should not be modified.
        private void ErrorMessageHandler(uint dwError, UIntPtr dwType)
        {
            switch (dwError)
            {
                case (uint)CyberCvErrors.CV_BARCODE:
                {
                    // Barcode read - in this case dwType is a pointer to a string
                    // 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 barcodeStr = CyberEx.GetCallbackString(dwType);
                    m_statusMsg = " ";
                    m_errorMsg = "  ==>  Barcode Read: " + barcodeStr;
                    break;
                }
                case (uint)CyberCvErrors.CV_DEBUG:
                {
                    // Debug message - in this case dwType is a pointer to a string
                    string debugStr = CyberEx.GetCallbackString(dwType);
                    m_statusMsg = " ==> Error ";
                    m_errorMsg = "  ==>  Debug: " + debugStr;
                    m_Scanner_Needs_Reset = true;
                    break;
                }
                case (uint)CyberCvErrors.CV_SCANNER_NOT_CONNECTED:
                {
                    m_statusMsg = " ==> Error ";
                    m_errorMsg = "  ==>  Scanner Not Connected";
                    m_Scanner_Needs_Reset = true;
                    break;
                }
                case (uint)CyberCvErrors.CV_SCANNER_ERROR_CALIB:
                {
                    m_statusMsg = " ==> Error ";
                    m_errorMsg = "  ==>  Scanner Calibration Error";
                    m_Scanner_Needs_Reset = true;
                    break;
                }
                case (uint)CyberCvErrors.CV_SCAN_ABORTED:
                {
                    m_statusMsg = " ==> Scan Aborted ";
                    m_errorMsg = "  ==>  User Abort";
                    break;
                }
                case (uint)CyberCvErrors.CV_BARCODE_CHECKSUM_BAD:
                {
                    m_statusMsg = " ==> Error ";
                    m_errorMsg = "  ==> Barcode Checksum Error";
                    break;
                }
                default:
                {
                    m_statusMsg = " ==> Error ";
                    m_errorMsg = "  ==>  Scanner Unknown Error";
                    m_Scanner_Needs_Reset = true;
                    break;
                }
            }
        }


    }
}
