//using Keyence.AutoID.SDK;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Media;
using System.Text.RegularExpressions;
using ScaBox30.Model;
using ScaBox30.Controller;
using System.Text.Json;
using System.Runtime.InteropServices;
using System.Reflection;

namespace ScaBox30
{
    struct barcode_pos
    {
        public string code;
        public int position;
        public int xpos;
        public int ypos;
    }

    struct operation_mode
    {
        public const string manual = "Manual";
        public const string automatico = "Scanbox";
    }



    public partial class Principal : Form
    {
        private TcpReaderAccessor m_reader = new TcpReaderAccessor();
        private TcpReaderSearcher m_searcher = new TcpReaderSearcher();
        //private ReaderAccessor m_reader = new ReaderAccessor();
        //private ReaderSearcher m_searcher = new ReaderSearcher();
        List<NicSearchResult> m_nicList = new List<NicSearchResult>();

        private NicSearchResult host_ip_nic = null;
        private List<string> sensor_ip_list = new List<string>();
        private bool reader_found = false;
        private bool reader_connected = false;

        private int numberOfBoxesSelected = 100;
        private string received_data_package = "";
        List<Label> labels_ok2 = new List<Label>();  //Used a list to dinamicaly allocate the labels
        //private string[] individual_receivedData = new string[100];  // change from 31 to 100
        List<string> individual_receivedData = new List<string>();
        List<string> masterCodes = new List<string>();
        List<string> foundCodes = new List<string>();
        private string[] triad_receivedData = new string[3];
        private string[] string_position = new string[2];
        private string imageAreaStr = "";
        private float xScale = 1;
        private float yScale = 1;
        private float img_binning = 2; // 1/4 binning means dividing by 2 each dimension
        private float globalScale = 1;
        private int xLeftCornner = 0;
        private int yLeftCornner = 0;
        private int xOffset = 0;
        private int yOffset = 0;
        private int imgWidth = 0;
        private int imgHeight = 0;
        Point labelpoint = new Point(0, 0);


        //private string[] ordered_receivedData = new string[31];
        private barcode_pos[] ordered_receivedData = new barcode_pos[100]; // change from 31 to 100

        //results counters
        private int ok_count = 0;
        private int bad_count = 0;
        private int utr_count = 0;

        private string operatorTag = null;
        private string[] supervisor_passwords = new string[3];

        //Database support variables
        private string csvFileName = null;
        //private string csvConfigFileName = "master_codigos.csv";
        private string scanBoxPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "BoxScaner");
        private bool save_data = false;

        private bool scanbox = true;
        private bool exploring_box_type = true;

        //Key press supporting items
        DateTime _lastKeystroke = new DateTime(0);
        List<char> _barcode = new List<char>(10);


        // Zebra printer variables
        ZebraController ZT231 = new ZebraController("Etiqueta.prn");

        // Screenshot capture variables
        CapturaImagen capturaImagen = new CapturaImagen();



        ErrorCode code_actual = ErrorCode.None;
        ErrorCode code_anterior = ErrorCode.None;


        Timer printimer;
        DateTime lastDate;
        TimeSpan time;



        #region CONSTRUCTOR
        public Principal()
        {
            InitializeComponent();
            InitializeItems();
            SearchReader();
        }
        #endregion


        private void SearchReader()
        {
            bool host_found = false;

            m_nicList = m_searcher.ListUpNic();
            //Keyboard catcher subscription
            //this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Form1_KeyPress);


            // VERIFICA SI LA IP DE LA COMPUTADORA ES LA MISMA DEL ARCHIVO APP.CONFIG
            if (m_nicList != null)
            {
                for (int i = 0; i < m_nicList.Count; i++)
                {
                    if (Globals.cfg.iphost == m_nicList[i].NicIpAddr)
                    {
                        host_ip_nic = m_nicList[i];
                        //NICcomboBox.Items.Add(m_nicList[i].NicIpAddr);
                        host_found = true;
                        host_ip_lbl.Text = m_nicList[i].NicIpAddr;
                        host_ip_lbl.ForeColor = Color.Green;
                        break;
                    }
                }
            }


            if (!host_found)
            {
                host_ip_lbl.Text = "Error";
                host_ip_lbl.ForeColor = System.Drawing.Color.Red;
            }

            // SI NO SE ESTAN BUSCANDO SCANERS CONECTADOS SE PROCEDE CON LA BUSQUEDA
            if (!m_searcher.IsSearching)
            {
                m_searcher.SelectedNicSearchResult = host_ip_nic;

                m_searcher.Start((res) =>
                {
                    //Defines searched actions here.Defined actions work asynchronously.
                    //"SearchListUp" works when a reader was searched.
                    BeginInvoke(new delegateUserControl(SearchListUp), res.IpAddress);
                });

            }

            for (int i = 0; i < sensor_ip_list.Count; i++)
            {
                if (Globals.cfg.ipscanner == sensor_ip_list[i])
                {
                    sr2000w_ip_lbl.Text = sensor_ip_list[i];
                    reader_found = true;
                    break;
                }
            }
            if (!reader_found)
            {
                sr2000w_ip_lbl.Text = "0.0.0.0";
            }
        }


        #region FORM LOAD
        private void Form1_Load(object sender, EventArgs e)
        {

            // CARGA DE CONFIGURACIONES
            LoadConfigJson(Globals.config_json);

            // Initialize daily consecutive date to today to avoid unexpected reset on first print
            Globals.consecDate = DateTime.Today;

            if (Globals.cfg.printerenabled)
            {
                btnPrinter.ForeColor = Color.White;
                btnPrinter.Text = "Impresora Activada";
            }
            else
            {
                btnPrinter.ForeColor = Color.DimGray;
                btnPrinter.Text = "Impresora Desactivada";
            }


            printimer = new System.Windows.Forms.Timer();
            printimer.Interval = 100;
            printimer.Tick += new EventHandler(Tick);



            // CARGA LA BASE DE DATOS
            LoadJson(Application.StartupPath + "\\data.json");

            this.Text = "ScanBox (Modo " + (scanbox ? operation_mode.automatico : operation_mode.manual) + ")";
            labelCaption.Text = this.Text;


            for (int i = 0; i <= numberOfBoxesSelected; i++)
            {
                Label label = new Label();
                this.Controls.Add(label);
                label.AutoSize = true;
                label.BackColor = System.Drawing.Color.Transparent;
                label.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                label.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0)))));
                label.Location = new System.Drawing.Point(20, 20);
                label.Name = "label_ok_" + i;
                label.TextAlign = ContentAlignment.MiddleCenter;
                label.Size = new System.Drawing.Size(33, 20);
                label.Text = "New";
                label.Visible = true;
                label.BringToFront();

                //this.labels_ok2[i] = label;
                this.labels_ok2.Add(label);
            }

            //Read programs configuration from App.config
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program1_OPCcodes").ToString(), Program = "1" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program2_OPCcodes").ToString(), Program = "2" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program3_OPCcodes").ToString(), Program = "3" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program4_OPCcodes").ToString(), Program = "4" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program5_OPCcodes").ToString(), Program = "5" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program6_OPCcodes").ToString(), Program = "6" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program7_OPCcodes").ToString(), Program = "7" });
            Globals.scaner_config.Add(new Programas() { Box_Capacity = ConfigurationManager.AppSettings.Get("program8_OPCcodes").ToString(), Program = "8" });



            //OCULTA LABELS CON RESULTADOS
            for (int i = 0; i <= numberOfBoxesSelected; i++)
            {
                labels_ok2[i].Visible = false;
            }


            //ESTABLECE EL DELAY PARA LA LIMPIEZA DE LA ULTIMA LECTURA
            try
            {
                timer1.Interval = Globals.cfg.cleardelay * 1000; // Comvert time delay from seconds to mili seconds
            }
            catch (FormatException)
            {
                timer1.Interval = 3000;  //3 seconds delay by default when incorrect data in App.comfig
            }
            catch (OverflowException)
            {
                timer1.Interval = 3000;  //3 seconds delay by default when incorrect data in App.comfig
            }



            var d = OpenOperatorDialog();
            op_ID_lbl.Text = d.OPIDgave;



            //Disable the 1D barcode scanner as input method
            this.KeyPreview = false;

            //Create ScanerBox Directory inside my documents
            if (!Directory.Exists(scanBoxPath))
            {
                try
                {
                    DirectoryInfo di = Directory.CreateDirectory(scanBoxPath);
                }
                catch (UnauthorizedAccessException)
                {
                    RJMessageBox.Show("El directorio no se pudo crear\nRevise que el usuario tenga los permisos adecuados", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }

        }
        #endregion

        #region SCANNER 1D
        private void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            //Key_Preview must be set to True on the form EWQ
            // check timing (keystrokes within 100 ms)
            TimeSpan elapsed = (DateTime.Now - _lastKeystroke);
            if (elapsed.TotalMilliseconds > 300)
            {
                _barcode.Clear();
            }


            // record keystroke & timestamp
            _barcode.Add(e.KeyChar);
            _lastKeystroke = DateTime.Now;

            // process barcode
            if (e.KeyChar == 13 && _barcode.Count > 0)
            {
                string msg = new String(_barcode.ToArray());
                msg = msg.Replace("\r", "");
                if (msg != "")
                {
                    barcode_lbl.Text = msg;

                    Globals.found_element = Globals.actual_data.Find(x => x.CCE.Equals(msg));
                    Print("Completa");
                    timer1.Enabled = true;
                }
                _barcode.Clear();
            }
        }
        #endregion

        #region KEYENCE DATA PROCESSING
        private void ReceivedDataWrite(string receivedData)
        {
            if (receivedData.Contains("BLOAD") || receivedData.Contains("RP"))
            {
                return;
            }

            received_data_package = (receivedData);
            received_data_package = received_data_package.Replace("\r", "");  //removes \r caracter from the package since it creates errors during parsing
            DataText.Text = received_data_package;
            individual_receivedData = DataText.Text.Split(',').ToList();

            if (exploring_box_type == true)
            {
                if (received_data_package != "")
                {

                    // En la exploración obtenemos los códigos recibidos y encontramos el que se repita más veces
                    string[] tempTriad = new string[3];
                    string boxType = "";
                    string command = "BLOAD,";
                    string response = "";
                    Data element = null;

                    // Limpieza de códigos coincidentes cada lectura
                    foundCodes.Clear();
                    for (int i = 0; i < individual_receivedData.Count(); i++)
                    {
                        tempTriad = individual_receivedData[i].Split(':');
                        if (tempTriad[0] != "ERROR")
                        {
                            foundCodes.Add(tempTriad[0]);
                        }
                    }
                    if (foundCodes.Count > 0)
                    {
                        var codesDictionary = foundCodes.GroupBy(x => x).Where(g => g.Count() > 0).ToDictionary(x => x.Key, y => y.Count()); //Creates a dictionary with all the codes and their number of ocurrences
                        var maxCount = codesDictionary.Values.Max();   //Finds the bigest number in the repetitions fiels, not used for now
                        var keyOfMaxCount = codesDictionary.Aggregate((x, y) => x.Value > y.Value ? x : y).Key; // Finds the code that repeats the most, 
                                                                                                                //in case twoor more codes get the same number of repetitions
                                                                                                                //and this number is the largest keyOfMaxCount can get any of those codes 

                        string found_code = keyOfMaxCount;
                        element = Globals.actual_data.Find(x => x.CCE.Equals(found_code)); //Busca el elemento que coincide con el codigo -> obtiene fila con la informacion de la etiqueta
                        Globals.found_element = element;
                    }




                    if (element == null)
                    {
                        RJMessageBox.Show("El opc no se encuentra en la base de datos", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }
                    else
                    {

                        boxType = (from item in Globals.scaner_config where item.Box_Capacity == element.Box_Capacity select item.Program).FirstOrDefault();
                        command = command + boxType;
                        string temp = (from item in Globals.scaner_config where item.Program == boxType select item.Box_Capacity).FirstOrDefault();
                        temp = temp != null ? temp : "--";
                        labelCapacity.Text = temp.Length > 2 ? temp.Substring(0, 2) : temp;


                        // El layout no está programado en el scanner
                        if (boxType == null)
                        {
                            ClearScreen();
                            RJMessageBox.Show("Este layout no puede ser procesado por el scanner!", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }

                        while (response.Equals(string.Empty))
                        {
                            int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
                            Console.WriteLine("Ejecutando: {0} en la linea {1}", command, lineNumber + 2);
                            // En modo debug, no enviar comando al sensor
                            if (!Globals.debug)
                            {
                                response = m_reader.ExecCommand(command);
                            }
                            else
                            {
                                response = "OK"; // Simular respuesta
                            }
                            System.Threading.Thread.Sleep(Globals.cfg.interval);

                        }

                        response = "";

                        scale_function();   //Get scaling correct for next box type
                        exploring_box_type = false;

                        // SIMULACION DE RESPUESTA DEL ESCANER (PRUEBAS SIN ESCANNER)
                        if (Globals.debug)
                        {
                            ReceivedDataWrite(Globals.testdata);
                        }
                        else
                        {
                            int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
                            Console.WriteLine("Ejecutando: {0} en la linea {1}", "LON", lineNumber + 2);
                            response = m_reader.ExecCommand("LON");
                            System.Threading.Thread.Sleep(Globals.cfg.interval);

                            ReceivedDataWrite(response);
                            response = "";
                        }
                    }
                    Console.WriteLine(command);
                }




            }
            else
            {


                //barcode_lbl.Text = "----------";

                if (received_data_package != "")                                              //To prevent errors from empty packages
                {
                    string[] tempTriad = new string[3];

                    for (int i = 0; i < individual_receivedData.Count(); i++)
                    {
                        tempTriad = individual_receivedData[i].Split(':');   //If "Error" is present tempTriad will have "Error" in position 0

                        if (tempTriad[0] != "ERROR")
                        {
                            masterCodes.Add(tempTriad[0]);
                        }
                    }
                    if (masterCodes.Count > 0)
                    {
                        var codesDictionary = masterCodes.GroupBy(x => x).Where(g => g.Count() > 0).ToDictionary(x => x.Key, y => y.Count()); //Creates a dictionary with all the codes and their number of ocurrences
                        var maxCount = codesDictionary.Values.Max();   //Finds the bigest number in the repetitions fiels, not used for now
                        var keyOfMaxCount = codesDictionary.Aggregate((x, y) => x.Value > y.Value ? x : y).Key; // Finds the code that repeats the most, 
                                                                                                                //in case twoor more codes get the same number of repetitions
                                                                                                                //and this number is the largest keyOfMaxCount can get any of those codes 
                        barcode_lbl.Text = keyOfMaxCount; // Master code selected from the code that gets more repetitions
                    }
                    else
                    {
                        barcode_lbl.Text = "Caja vacia";    //If is empty all lectures where errors and no dictionary can be created hence no master label 

                    }

                    masterCodes.Clear(); //clear master codes list


                }

                //Compare each code with the master code
                if (received_data_package != "")
                {
                    int data_received_count = individual_receivedData.Count();
                    if (numberOfBoxesSelected < data_received_count)
                    {
                        data_received_count = numberOfBoxesSelected;      //In case a wrong sensor configuration is used and more
                    }                                                     //codes than expected are received, program only parses up to numberOfBoxesSelected
                    for (int i = 0; i < data_received_count; i++)
                    {
                        if (individual_receivedData[i] != "ERROR")
                        {
                            //pair_receivedData = individual_receivedData[i].Split(':');
                            //ordered_receivedData[i + 1].code = pair_receivedData[0];
                            //ordered_receivedData[i + 1].position = Int32.Parse(pair_receivedData[1]);
                            triad_receivedData = individual_receivedData[i].Split(':');
                            ordered_receivedData[i + 1].code = triad_receivedData[0];
                            ordered_receivedData[i + 1].position = Int32.Parse(triad_receivedData[1]);
                            //Ivan
                            string_position = triad_receivedData[2].Split('/');
                            ordered_receivedData[i + 1].xpos = Int32.Parse(string_position[0]);
                            ordered_receivedData[i + 1].ypos = Int32.Parse(string_position[1]);
                            //Ivan
                            if (ordered_receivedData[i + 1].code == barcode_lbl.Text)
                            {
                                ok_count++;
                                labels_ok2[ordered_receivedData[i + 1].position].Visible = true;
                                switch (Globals.cfg.resultado)
                                {
                                    case "number":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = ordered_receivedData[i + 1].position.ToString();
                                        break;
                                    case "label":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = "OK";
                                        break;
                                    case "numberlabel":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = ordered_receivedData[i + 1].position.ToString() + "\rOK";
                                        break;
                                }
                                labelpoint.X = xOffset + (int)((float)(ordered_receivedData[i + 1].xpos - xLeftCornner) / (img_binning * globalScale)) - (labels_ok2[ordered_receivedData[i + 1].position].Size.Width / 2);
                                labelpoint.Y = yOffset + panelTitleBar.Height + (int)((float)(ordered_receivedData[i + 1].ypos - yLeftCornner) / (img_binning * globalScale)) - (labels_ok2[ordered_receivedData[i + 1].position].Size.Height / 2);
                                labels_ok2[ordered_receivedData[i + 1].position].Location = labelpoint;
                                labels_ok2[ordered_receivedData[i + 1].position].BackColor = Color.White;
                                labels_ok2[ordered_receivedData[i + 1].position].ForeColor = Color.ForestGreen;

                            }
                            else
                            {
                                bad_count++;
                                labels_ok2[ordered_receivedData[i + 1].position].Visible = true;
                                switch (Globals.cfg.resultado)
                                {
                                    case "number":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = ordered_receivedData[i + 1].position.ToString();
                                        break;
                                    case "label":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = "X";
                                        break;
                                    case "numberlabel":
                                        labels_ok2[ordered_receivedData[i + 1].position].Text = ordered_receivedData[i + 1].position.ToString() + "\rX";
                                        break;
                                }
                                labelpoint.X = xOffset + (int)((float)(ordered_receivedData[i + 1].xpos - xLeftCornner) / (img_binning * globalScale)) - (labels_ok2[ordered_receivedData[i + 1].position].Size.Width / 2);
                                labelpoint.Y = yOffset + panelTitleBar.Height + (int)((float)(ordered_receivedData[i + 1].ypos - yLeftCornner) / (img_binning * globalScale)) - (labels_ok2[ordered_receivedData[i + 1].position].Size.Height / 2);
                                labels_ok2[ordered_receivedData[i + 1].position].Location = labelpoint;
                                labels_ok2[ordered_receivedData[i + 1].position].BackColor = Color.White;
                                labels_ok2[ordered_receivedData[i + 1].position].ForeColor = Color.Red;
                            }

                        }

                        if (individual_receivedData[i] == "ERROR")
                        {
                            utr_count++;
                        }

                        if (i == data_received_count - 1)
                        {
                            utr_count_lbl.Text = utr_count.ToString();
                            ok_count_lbl.Text = ok_count.ToString();
                            bad_count_lbl.Text = bad_count.ToString();
                        }
                    }

                    if (bad_count > 0)
                    {
                        time = DateTime.Now - lastDate;
                        labelRate.Text = (time.TotalMilliseconds / 1000).ToString().Substring(0, 5) + " Seg.";
                        TgrBtn.Enabled = true;
                        //Change background color to red
                        this.BackColor = Color.Red;
                        DialogResult dialogResult = RJMessageBox.Show("Uno o más codigos son incorrectos!", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        this.BackColor = SystemColors.Control;
                        
                        // Capturar zona del PictureBox cuando hay errores para auditoría
                        capturaImagen.CapturarImg(blackimg_picbx);
                        capturaImagen.GuardarImg(barcode_lbl.Text + "_ERROR", operatorTag);
                    }

                    //If 1 or more codes are questionable (utr_count>0) the user has to decide if the results are added to the csv file
                    if ((utr_count > 0) && (bad_count == 0))
                    {
                        time = DateTime.Now - lastDate;
                        labelRate.Text = (time.TotalMilliseconds / 1000).ToString().Substring(0, 5) + " Seg.";
                        TgrBtn.Enabled = true;
                        //Change background color to red
                        this.BackColor = Color.Red;
                        DialogResult dialogResult = RJMessageBox.Show("¿Desea realizar la prueba de nuevo?", "ScanBox", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
                        this.BackColor = SystemColors.Control;

                        if (dialogResult == DialogResult.Yes)
                        {
                            save_data = false;
                        }
                        else if (dialogResult == DialogResult.No)
                        {
                            save_data = true;
                        }
                    }


                    if ((utr_count == 0) && (bad_count == 0))
                    {
                        time = DateTime.Now - lastDate;
                        labelRate.Text = (time.TotalMilliseconds / 1000).ToString().Substring(0, 5) + " Seg.";
                        TgrBtn.Enabled = true;
                        save_data = true;
                        
                        // Capturar zona del PictureBox después del segundo escaneo exitoso
                        capturaImagen.CapturarImg(blackimg_picbx);
                        capturaImagen.GuardarImg(barcode_lbl.Text, operatorTag);
                    }



                    //If save_data is true data must be saved
                    if (save_data == true)
                    {
                        //Write Results in to a CSV file
                        csvFileName = scanBoxPath + "\\" + "Codigos Escaneados " + DateTime.Now.ToString("yyyy-MM-dd") + ".csv";  //Nombre del archivo incluye el path, y el dia
                        if (File.Exists(csvFileName))
                        {
                            try
                            {
                                using (TextWriter sw = new StreamWriter(csvFileName, append: true))
                                    sw.WriteLine("{0},{1},{2},{3},{4},{5},{6}", operatorTag, barcode_lbl.Text, DateTime.Now.ToString("dd/MM/yyyy"), DateTime.Now.ToString("h:mm:ss tt"),
                                    ok_count.ToString(), bad_count.ToString(), utr_count.ToString());
                            }
                            catch (IOException)
                            {
                                RJMessageBox.Show("¡El archivo se encuentra abierto!\nCierre el archivo y repita la lectura.", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            }
                        }
                        else
                        {
                            using (TextWriter sw = new StreamWriter(csvFileName, append: true))
                            {
                                sw.WriteLine("Operator tag,OPC,Date,Time,OK,NOK,Questionable");
                                sw.WriteLine("{0},{1},{2},{3},{4},{5},{6}", operatorTag, barcode_lbl.Text, DateTime.Now.ToString("dd/MM/yyyy"), DateTime.Now.ToString("h:mm:ss tt"), ok_count.ToString(), bad_count.ToString(), utr_count.ToString());
                            }
                        }
                    }


                    printimer.Start();


                    UpdateTgrBtn();
                    this.KeyPreview = true;
                    //Starts timer to clear image
                    timer1.Enabled = true;
                    //TestPrint("Completa");
                }
            }

        }
        #endregion

        private void Tick(object sender, EventArgs e)
        {
            printimer.Stop();

            var element = Globals.found_element;

            if (element != null)
            {
                if (element.Box_Capacity != null)
                {
                    if (bad_count == 0)
                    {
                        // LA PRUEBA NO SE VA A REPETIR
                        if (save_data)
                        {
                            if (ok_count >= int.Parse(element.Box_Capacity.Substring(0, 2)))
                            {
                                Print("Completa");
                            }
                            else
                            {
                                Print("Parcial");
                            }
                        }
                    }

                }
                else
                {
                    RJMessageBox.Show("El opc no tiene un boxcapacity correcto. Por favor configúrelo en la base de datos primero y después repita la prueba.", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }

                save_data = false;
            }
        }

        #region UI CONTROLS

        //Change operation mode 
        private void btnToogleMode_Click(object sender, EventArgs e)
        {

            DialogResult result = RJMessageBox.Show("Desea cambiar el modo de operación a " + (scanbox ? operation_mode.manual : operation_mode.automatico) + "?", "ScanBox", MessageBoxButtons.OKCancel);

            if (result == DialogResult.OK)
            {
                if (btnToogleMode.Text == operation_mode.manual)
                {
                    //ESTABLECE EL MODO DE OPERACION A SCANBOX
                    scanbox = true;
                    exploring_box_type = true;
                    TgrBtn.Enabled = reader_connected ? true : false;
                    UpdateTgrBtn();
                    KeyPreview = false; //Deshabilita el scanner 1d
                }
                else
                {
                    //ESTABLECE EL MODO DE OPERACION A MANUAL (ESCANER 1D)
                    scanbox = false;
                    // En modo manual, habilitar el botón si debug está activado o hay sensor
                    TgrBtn.Enabled = (Globals.debug || reader_connected) ? true : false;
                    UpdateTgrBtn();
                    KeyPreview = true; //Habilita el scanner 1d
                    ActiveControl = barcode_lbl;
                }

                btnToogleMode.Text = scanbox ? operation_mode.automatico : operation_mode.manual;
                this.Text = "ScanBox (Modo " + (scanbox ? operation_mode.automatico : operation_mode.manual) + ")";
                labelCaption.Text = this.Text;
            }
        }

        //Ejecuted when [Ejecutar Lectura] is pressed
        private void TgrBtn_Click(object sender, EventArgs e)
        {
            labelRate.Text = "0 Seg.";
            lastDate = DateTime.Now;

            //Clear Counters
            ok_count = 0;
            bad_count = 0;
            utr_count = 0;
            ok_count_lbl.Text = "--";
            bad_count_lbl.Text = "--";
            utr_count_lbl.Text = "--";
            //Make black image invisible to allow live view
            blackimg_picbx.Visible = false;
            //disable image clear timer
            timer1.Enabled = false;

            //Disable button for re-clic and master barcode from being scanned
            TgrBtn.Enabled = false;
            UpdateTgrBtn();

            this.KeyPreview = false;

            //Cleaar Labels
            for (int i = 1; i <= numberOfBoxesSelected; i++)
            {
                //labels_ok[i].Visible = false;
                labels_ok2[i].Visible = false;
            }

            // REFERENCIA DE COMANDOS UTILIZADOS OBTENIDOS DEL MANUAL
            // LON -> Inicia la lectura
            // BLOAD -> Carga archivos de copia de seguridad
            // RP -> Comando de configuración de operación
            // RP 215 -> Especificación de rango de captura de imagen

            if (reader_found)
            {
                string program = "1";
                string command = "BLOAD," + program;
                string response = "";

                //Envía el comando hasta obtener una respuest
                while (response.Equals(string.Empty))
                {
                    int lineNumber = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
                    Console.WriteLine("Ejecutando: {0} en la linea {1}", command, lineNumber + 2);
                    response = m_reader.ExecCommand(command);
                    System.Threading.Thread.Sleep(Globals.cfg.interval);
                }
                response = "";

                exploring_box_type = true;

                int lineNumber2 = (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber();
                Console.WriteLine("Ejecutando: {0} en la linea {1}", "LON", lineNumber2 + 1);
                response = m_reader.ExecCommand("LON");
                System.Threading.Thread.Sleep(Globals.cfg.interval);

                ReceivedDataWrite(response);
                response = "";

            }
            else
            {
                // SIMULACION DE RESPUESTA DEL SCANER
                if (Globals.debug)
                {
                    exploring_box_type = true;
                    ReceivedDataWrite(Globals.testdata);
                }

            }

        }

        //Dispose before closing Form for avoiding error.
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            m_reader?.Dispose();
            m_searcher?.Dispose();
            liveviewForm1?.Dispose();
            capturaImagen?.Dispose();
        }


        private Form CreateDataUIInstance()
        {
            // Use reflection to instantiate the Form type named "DataUI" (avoids compiler ambiguity if a generated resource class shares the name)
            var asm = typeof(Principal).Assembly;
            var dataUiType = asm.GetTypes().FirstOrDefault(t => t.Name == "DataUI" && typeof(Form).IsAssignableFrom(t));
            if (dataUiType != null)
            {
                return Activator.CreateInstance(dataUiType) as Form;
            }
            return null;
        }

        private void btnBD_MouseClick(object sender, MouseEventArgs e)
        {
            var dataForm = CreateDataUIInstance();
            if (dataForm != null)
            {
                dataForm.ShowDialog();
            }
        }


        private void timer1_Tick(object sender, EventArgs e)
        {
            ClearScreen();
        }

        private void ClearScreen()
        {
            //Disable clear image timr
            timer1.Enabled = false;
            //Impose black image over liveview
            blackimg_picbx.Visible = true;
            //Clear count labels
            ok_count_lbl.Text = "--";
            bad_count_lbl.Text = "--";
            utr_count_lbl.Text = "--";
            //Clear Master Code
            barcode_lbl.Text = "----------";

            DataText.Text = "";

            labelCapacity.Text = "--";
            //Make ok lables invisible
            for (int i = 0; i <= numberOfBoxesSelected; i++)
            {
                labels_ok2[i].Visible = false;
            }
        }


        private void reader_connector_Tick(object sender, EventArgs e)
        {
            if (!reader_connected)
            {
                SearchReader();
                ConnectReader();
            }



            ErrorCode code = m_reader.LastErrorInfo;

            code_actual = code_actual != code && code_anterior != code ? code : ErrorCode.None;
            code_anterior = code;


            if (code_actual != ErrorCode.None)
            {
                switch (code_actual)
                {
                    case ErrorCode.AlreadyOpen:
                        break;
                    case ErrorCode.Closed:
                        reader_connected = false;
                        break;
                    case ErrorCode.OpenFailed:
                        reader_connected = false;
                        break;
                    case ErrorCode.Timeout:
                        break;
                    case ErrorCode.SendFailed:
                        break;
                    case ErrorCode.BeginReceiveFailed:
                        break;
                }
            }


            if (reader_connected && scanbox)
            {
                sr2000w_ip_lbl.ForeColor = Color.ForestGreen;
                TgrBtn.Enabled = true;
                UpdateTgrBtn();
            }
            else
            {

                if (!reader_connected)
                {
                    sr2000w_ip_lbl.Text = "0.0.0.0";
                    sr2000w_ip_lbl.ForeColor = SystemColors.ControlText;

                    TgrBtn.Enabled = false;
                    UpdateTgrBtn();
                }

            }

            //Console.WriteLine("Reader: {0}, Error: {1}", m_reader.LastErrorInfo, code_actual);

        }


        // CONNECT TO FOUND READER
        private void ConnectReader()
        {
            string sr2000w_ip;
            sr2000w_ip = Globals.cfg.ipscanner;

            if (reader_found)
            {
                //Stop liveview.
                liveviewForm1.EndReceive();
                //Set ip address of liveview.
                liveviewForm1.IpAddress = sr2000w_ip;
                //Start liveview.
                liveviewForm1.BeginReceive();
                //Set ip address of ReaderAccessor.
                m_reader.IpAddress = sr2000w_ip;
                //Connect TCP/IP.
                reader_connected = m_reader.Connect((data) =>
                {
                    //Define received data actions here.Defined actions work asynchronously.
                    //"ReceivedDataWrite" works when reading data was received.
                    BeginInvoke(new delegateUserControl(ReceivedDataWrite), Encoding.ASCII.GetString(data));
                });

            }
        }



        // WINDOW CLOSE BUTTON
        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        // WINDOW MAXIMIZE BUTTON
        private void btnMax_Click(object sender, EventArgs e)
        {

            if (WindowState == FormWindowState.Maximized)
            {
                this.WindowState = FormWindowState.Normal;
            }
            else
            {
                WindowState = FormWindowState.Maximized;
            }
        }
        // WINDOW MINIMIZE BUTTON
        private void btnMin_Click(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Normal || WindowState == FormWindowState.Maximized)
            {
                this.WindowState = FormWindowState.Minimized;
            }
            else
            {
                WindowState = FormWindowState.Normal;
            }
        }


        // DRAG FORM
        [DllImport("user32.DLL", EntryPoint = "SendMessage")]
        private extern static void SendMessage(System.IntPtr hWnd, int wMsg, int wParam, int lParam);
        [DllImport("user32.DLL", EntryPoint = "ReleaseCapture")]
        private extern static void ReleaseCapture();
        private void panelTitleBar_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Clicks == 2)
            {
                WindowState = WindowState == FormWindowState.Maximized ? FormWindowState.Normal : FormWindowState.Maximized;
            }
            else
            {
                ReleaseCapture();
                SendMessage(this.Handle, 0x112, 0xf012, 0);
            }
        }


        // RESIZE FORM
        private const int cGrip = 16;
        private const int cCaption = 32;
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x84)
            {
                Point pos = new Point(m.LParam.ToInt32());
                pos = this.PointToClient(pos);
                if (pos.Y < cCaption)
                {
                    m.Result = (IntPtr)2;
                    return;
                }

                if (pos.X >= this.ClientSize.Width - cGrip && pos.Y >= this.ClientSize.Height - cGrip)
                {
                    m.Result = (IntPtr)17;
                    return;
                }
            }
            base.WndProc(ref m);
        }


        #endregion

        #region METHODS

        //delegateUserControl is for controlling UserControl from other threads.
        private delegate void delegateUserControl(string str);

        private void SearchListUp(string ip)
        {
            if (ip != "")
            {
                sensor_ip_list.Add(ip);
                //if (ConfigurationManager.AppSettings.Get("sr2000w_ip") == ip)
                if ( Globals.cfg.ipscanner == ip)
                {
                    sr2000w_ip_lbl.Text = ip;
                    reader_found = true;
                }
                //connect_btn.Enabled = true;
                return;
            }
        }

        // CARGA EL ARCHIVO DATA.JSON EXISTENTE
        private void LoadJson(String jsonfile)
        {
            if (File.Exists(jsonfile))
            {
                Globals.actual_data.Clear();
                this.Cursor = Cursors.WaitCursor;
                string jscontent = File.ReadAllText(jsonfile);
                Globals.actual_data = JsonSerializer.Deserialize<List<Data>>(jscontent);
                jscontent = "";
                this.Cursor = Cursors.Default;
            }
        }

        private void LoadConfigJson(string jsonfile)
        {
            if (File.Exists(jsonfile))
            {
                string jsoncontent = File.ReadAllText(jsonfile);
                Globals.config = JsonSerializer.Deserialize<List<Config>>(jsoncontent);

                Globals.cfg.iphost = Globals.config[0].iphost;
                Globals.cfg.cleardelay = Globals.config[0].cleardelay;
                Globals.cfg.interval = Globals.config[0].interval;
                Globals.cfg.ipscanner = Globals.config[0].ipscanner;
                Globals.cfg.usbprinter = Globals.config[0].usbprinter;
                Globals.cfg.ipprinter = Globals.config[0].ipprinter;
                Globals.cfg.printertype = Globals.config[0].printertype;
                Globals.cfg.resultado = Globals.config[0].resultado;
                Globals.cfg.printerenabled = Globals.config[0].printerenabled;
            }
            else
            {
                //GRABAR CONFIGURACIONES DEFAULT
                Globals.cfg.iphost = "192.168.100.99";
                Globals.cfg.cleardelay = 5;
                Globals.cfg.interval = 200;
                Globals.cfg.ipscanner = "192.168.100.100";
                Globals.cfg.usbprinter = "none";
                Globals.cfg.ipprinter = "192.168.100.101";
                Globals.cfg.printertype = "eth";  // Puede ser eth o usb
                Globals.cfg.resultado = "label";  // Etiquetas mostradas en el layout (OK,X), Región ó (OK,X) + Región
                Globals.cfg.printerenabled = true;

                Globals.config.Clear();
                Globals.config.Add(Globals.cfg);
                var json = JsonSerializer.Serialize<List<Config>>(Globals.config);
                File.WriteAllText(Globals.config_json, json);
            }


        }

        private operatorDialog OpenOperatorDialog()
        {
            operatorDialog d = new operatorDialog(false);
            d.ShowDialog();
            operatorTag = d.OPIDgave;
            return d;
        }

        private void scale_function()
        {
            //  RETORNA LOS PUNTOS (a,b) y (c,d) QUE SE TRADUCEN EN (x,y) PARA CADA PUNTO
            //  (a,b)
            //      *-----------------
            //      |                |
            //      |                |
            //      -----------------*

            //                      (c,d)

            // En modo debug, no intentar leer del sensor
            if (Globals.debug)
            {
                // Usar valores por defecto para debug
                xLeftCornner = 0;
                yLeftCornner = 0;
                xScale = 1;
                yScale = 1;
                globalScale = 1;
                imgWidth = liveviewForm1?.Bounds.Width ?? 800;
                imgHeight = liveviewForm1?.Bounds.Height ?? 600;
                xOffset = 0;
                yOffset = 0;
                return;
            }

            //Get LiveView Size for scale and scale from sensor using command RP,215
            imageAreaStr = m_reader.ExecCommand("RP,215");  //Read captured image area
                                                            //List<string> coordinates = (from Match m in Regex.Matches(imageAreaStr, @"\d{4}") select m.Value).Where(p => !string.IsNullOrEmpty(p)).ToList();
            List<string> coordinates_str = (from Match m in Regex.Matches(imageAreaStr, @"\d{4}") select m.Value).ToList();
            var coordinates_int = new[] { 0, 0, 0, 0 };
            if (coordinates_str.Count == 4)
            {
                //coordinates_int = coordinates_str.ForEach(Int32.Parse(coordinates_str));
                coordinates_int[0] = Int32.Parse(coordinates_str[0]); //x top left corner
                coordinates_int[1] = Int32.Parse(coordinates_str[1]); //y top left corner
                coordinates_int[2] = Int32.Parse(coordinates_str[2]); //x bottom right corner
                coordinates_int[3] = Int32.Parse(coordinates_str[3]); //y bottom right corner
            }
            xLeftCornner = coordinates_int[0];
            yLeftCornner = coordinates_int[1];
            //Assuming a quarter Image received meaning 0.5xWidth x 0.5xHeight
            //The following code is to get the location of the pixel zero on the trimmed image in the screen
            xScale = ((float)coordinates_int[2] - (float)coordinates_int[0]) / ((float)liveviewForm1.Bounds.Width);
            xScale = xScale / img_binning;
            yScale = ((float)coordinates_int[3] - (float)coordinates_int[1]) / ((float)liveviewForm1.Bounds.Height);
            yScale = yScale / img_binning;
            if (yScale > xScale)  // use the biggest number since image mantains aspect ratio
                globalScale = yScale;
            else
                globalScale = xScale;

            imgWidth = coordinates_int[2] - coordinates_int[0];
            imgWidth = (int)((float)imgWidth / (img_binning * globalScale));
            imgHeight = coordinates_int[3] - coordinates_int[1];
            imgHeight = (int)((float)imgHeight / (img_binning * globalScale));
            xOffset = liveviewForm1.Bounds.X + (liveviewForm1.Bounds.Width - imgWidth) / 2;
            yOffset = liveviewForm1.Bounds.Y + (liveviewForm1.Bounds.Height - imgHeight) / 2;
        }

        private void InitializeItems()
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.Padding = new Padding(Globals.borderSize);//Set border size
            this.labelCaption.Text = this.Text;
            this.panelTitleBar.BackColor = Globals.primaryColor;
            this.panelTitleBar.Padding = new Padding(0, 0, 0, Globals.borderSize);
            this.BackColor = Globals.primaryColor;
            this.DataText.BackColor = Globals.primaryColor;
        }

        // PRINT ZEBRA PRINTER
        private void Print(String completa_parcial)
        {

            if (Globals.cfg.printerenabled)
            {
                // Reset daily consecutive counter if day changed
                if (Globals.consecDate.Date != DateTime.Today)
                {
                    Globals.consec = 1;
                    Globals.consecDate = DateTime.Today;
                }

                string etiqueta = barcode_lbl.Text;

                if (Globals.found_element.CCE == etiqueta)
                {
                    String v1 = Globals.found_element.nom_1;
                    String v2 = Globals.found_element.@base + " / " + Globals.found_element.addition + " " + Globals.found_element.oeil;
                    // Mostrar información en todos los modos (manual y scanbox)
                    String v3 = completa_parcial + ": " + ok_count.ToString() + " de " + Globals.found_element.Box_Capacity.Substring(0, 2);
                    String v4 = Globals.consec.ToString("D5"); //Numero consecutivo
                    DateTime v5 = DateTime.Now; //Fecha y hora de impresión 
                    String barcode = Globals.found_element.CCE;

                    ZT231.buildPrn(v1, v2, v3, v4, v5, barcode);

                    switch (Globals.cfg.printertype)
                    {
                        case "eth":
                            ZT231.SendZplOverTcp(Globals.cfg.ipprinter);
                            break;
                        case "usb":
                            ZT231.SendZplOverUsb(Globals.cfg.usbprinter);
                            break;
                    }
                }
                Globals.consec++;
            }

        }

        private void UpdateTgrBtn()
        {
            if (TgrBtn.Enabled)
            {
                TgrBtn.BackColor = Color.SeaGreen;
                TgrBtn.ForeColor = Color.White;
            }
            else
            {
                TgrBtn.BackColor = SystemColors.ButtonFace;
            }
        }

        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            if (Globals.cfg.printerenabled)
            {
                //btnPrinter.BackColor = Color.DarkRed;
                btnPrinter.Text = "Impresora Desactivada";
                //btnPrinter.BackColor = Color.DimGray;
                btnPrinter.ForeColor = Color.DimGray;
                Globals.cfg.printerenabled = false;
                Globals.config[0].printerenabled = false;
            }
            else
            {
                //btnPrinter.BackColor = Color.LawnGreen;
                btnPrinter.Text = "Impresora Activada";
                //btnPrinter.BackColor = Color.FromArgb(64, 69, 74);
                btnPrinter.ForeColor = Color.White;
                Globals.cfg.printerenabled = true;
                Globals.config[0].printerenabled = true;
            }

            var json = JsonSerializer.Serialize<List<Config>>(Globals.config);
            File.WriteAllText(Globals.config_json, json);

        }

        private void btnConfiguracion_Click(object sender, EventArgs e)
        {

            if (!Globals.valid_user)
            {
                Usuario usuario = new Usuario();
                DialogResult result = usuario.ShowDialog();

                if (Globals.valid_user)
                {
                    Configuracion config = new Configuracion();
                    config.ShowDialog();
                    timer1.Interval = Globals.cfg.cleardelay * 1000;
                }
                else
                {
                    if (result != DialogResult.Cancel)
                    {
                        RJMessageBox.Show("No tiene permiso para realizar esta acción", "ScanBox", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }

                }
            }
        }

        private void bttnQA_Click_1(object sender, EventArgs e)
        {

        }


    }


}
