using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using PC_Gui;
using System.IO;
using EmbeddedDesktop;
using System.Security.Cryptography.X509Certificates;
using Ini;


namespace PCAgent
{
    public partial class Form1 : Form
    {

        private string INI_FILE_NAME = "\\ERD.ini";                 //Nazev INI souboru

        /// <summary>
        /// Konstruktor formulare & inicializace delegatu
        /// </summary>
        public Form1()
        {
            InitializeComponent();
            comboBox2.SelectedIndex = 4;
            RC.InternalEventMessage += new RemoteControlEvent(NewEventReceived);
            RC.InternalStateChange += new RemoteControlStatusEvent(StateChangeReceived);
            RC.Display.OnOutputBufferChange += new BufferStateChangeDelegate(updateDisplayBufferState);
            RC.Display.OnSendBegin += new SendActionDelegate(DataSendBegin);
            RC.Display.OnSendFinish += new SendActionDelegate(DataSendFinish);

            LoadIniSettings(Application.StartupPath + INI_FILE_NAME);   
        }


        /// <summary>
        /// Metoda callbacku udalosti Zacatek Odesilani
        /// </summary>
        void DataSendBegin()
        {
            try
            {
                if (this.InvokeRequired)
                {

                    this.Invoke((MethodInvoker)delegate { DataSendBegin() ; });
                    return;
                }
                label13.Visible = true;

            }
            catch (Exception)
            {

            }
        }

        /// <summary>
        /// Metoda callbacku prijeti udalosti Ukonceni Odesilani
        /// </summary>
        void DataSendFinish()
        {
            try
            {
                if (this.InvokeRequired)
                {

                    this.Invoke((MethodInvoker)delegate { DataSendFinish(); });
                    return;
                }
                label13.Visible = false;

            }
            catch (Exception)
            {

            }
        }

        /// <summary>
        /// Metoda aktualizuje StatusBar podle stavu zasobniku DisplayReaderu
        /// </summary>
        /// <param name="max"></param>
        /// <param name="current"></param>
        void updateDisplayBufferState(int max, int current)
        {
            try
            {
                if (this.InvokeRequired)
                {

                    this.Invoke((MethodInvoker)delegate { updateDisplayBufferState(max,current); });
                    return;
                }
                
                progressBar2.Maximum = max+1;
                progressBar2.Value = current;
                float div = current/((float)max+1);


            }
            catch (Exception)
            {

                progressBar2.Value = progressBar2.Maximum;
            }
            
        }

        /// <summary>
        /// Metoda pro nacteni jednotlivych nastaveni z INI souboru
        /// </summary>
        /// <param name="iniName">adresa souboru</param>
        private void LoadIniSettings(string iniName)
        {

            setupFile = new IniFile(iniName);
            string type = setupFile.IniReadValue("Connection", "Type");

            if (type == ClientConnection.Serial.ToString())
                radioButton2.Checked = true;

            string tcpPort = setupFile.IniReadValue("Connection", "TcpPort");
            try
            {
                numericUpDown1.Value = System.Convert.ToDecimal(tcpPort);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse TcpPort parameter.");
            }

            string tcpTimeout = setupFile.IniReadValue("Connection", "TcpTimeOut");
            try
            {
                numericUpDown2.Value = System.Convert.ToDecimal(tcpTimeout);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse TcpTimeout parameter.");
            }

            string cipher = setupFile.IniReadValue("Connection", "TcpCipher");

            try
            {
                checkBox1.Checked = System.Convert.ToBoolean(cipher);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse TcpCipher parameter.");
            }

            string certificate = setupFile.IniReadValue("Connection", "Certificate");

            try
            {
                textBox1.Text = certificate;
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse Certificate parameter.");
            }

            string password = setupFile.IniReadValue("Connection", "Password");

            try
            {
                maskedTextBox1.Text = password;
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse TcpCipher parameter.");
            }

            string com = setupFile.IniReadValue("Connection", "ComPort");

            DetectPorts();

            try
            {
                int index = comboBox1.FindString(com);
                if (index >= 0)
                    comboBox1.SelectedIndex = index;
            }
            catch (Exception)
            {
                Console.WriteLine("Requested COM port was not detected.");
            }

            string baud = setupFile.IniReadValue("Connection", "ComSpeed");

            try
            {
                int index = comboBox2.FindString(baud);
                if (index >= 0)
                    comboBox2.SelectedIndex = index;
            }
            catch (Exception)
            {
                Console.WriteLine("Requested COM speed not set.");
            }

            string screenWidth = setupFile.IniReadValue("Screen", "Width");
            try
            {
                numericUpDown6.Value = System.Convert.ToDecimal(screenWidth);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse Width parameter.");
            }

            string screenHeight = setupFile.IniReadValue("Screen", "Height");
            try
            {
                numericUpDown7.Value = System.Convert.ToDecimal(screenHeight);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse Height parameter.");
            }

            string samplingMax = setupFile.IniReadValue("Screen", "SamplingMax");
            try
            {
                numericUpDown3.Value = System.Convert.ToDecimal(samplingMax);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse SamplingMax parameter.");
            }

            string samplingMin = setupFile.IniReadValue("Screen", "SamplingMin");
            try
            {
                numericUpDown4.Value = System.Convert.ToDecimal(samplingMin);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse SamplingMin parameter.");
            }

            string cleanup = setupFile.IniReadValue("Screen", "CleanUp");
            try
            {
                numericUpDown5.Value = System.Convert.ToDecimal(cleanup);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse CleanUp parameter.");
            }

            string time = setupFile.IniReadValue("Log", "Time");

            try
            {
                checkBox2.Checked = System.Convert.ToBoolean(time);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not parse time parameter.");
            }
        }

        /// <summary>
        /// Ulozeni nastaveni do INI souboru
        /// </summary>
        /// <param name="iniName">cesta k souboru</param>
        private void SaveIniSettings(string iniName)
        {
            setupFile = new IniFile(iniName);
            setupFile.IniWriteValue("Connection","Type",(radioButton1.Checked?ClientConnection.Wifi.ToString():ClientConnection.Serial.ToString()));
            setupFile.IniWriteValue("Connection","TcpPort",numericUpDown1.Value.ToString());
            setupFile.IniWriteValue("Connection","TcpCipher",checkBox1.Checked.ToString());
            setupFile.IniWriteValue("Connection", "Certificate", textBox1.Text.ToString());
            setupFile.IniWriteValue("Connection", "Password", maskedTextBox1.Text.ToString());
            setupFile.IniWriteValue("Connection", "TcpTimeOut", numericUpDown2.Value.ToString());
            setupFile.IniWriteValue("Connection","ComPort",comboBox1.SelectedItem.ToString());
            setupFile.IniWriteValue("Connection","ComSpeed",comboBox2.SelectedItem.ToString());

            setupFile.IniWriteValue("Screen", "Width", numericUpDown6.Value.ToString());
            setupFile.IniWriteValue("Screen", "Height", numericUpDown7.Value.ToString());

            setupFile.IniWriteValue("Screen", "SamplingMax", numericUpDown3.Value.ToString());
            setupFile.IniWriteValue("Screen", "SamplingMin", numericUpDown4.Value.ToString());
            setupFile.IniWriteValue("Screen", "CleanUp", numericUpDown5.Value.ToString());

            setupFile.IniWriteValue("Log", "Time", checkBox2.Checked.ToString());

        }


        private IniFile setupFile;                                  //instance reprezentujici ini soubor

        public RemoteControl RC = new RemoteControl();              //instance dalkoveho ovladani

        DisplayReaderParams DRParams = new DisplayReaderParams();   //Parametry Display Readeru

        bool testStarted = false;

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            //Povoleni resp zakazani prislusnych objektu podle volby rozhrani
            if (this.radioButton2.Checked)
            {
                groupBox3.Enabled = true;
                groupBox2.Enabled = false;
                
            }
            else
            {
                groupBox3.Enabled = false;
                groupBox2.Enabled = true;
            }
            
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            DetectPorts();                                  //Detekce pouzitelnych portu
        }

        /// <summary>
        /// Detekce dostupnych portu
        /// </summary>
        private void DetectPorts()
        {
            string[] names = SerialPort.GetPortNames();     //Ziskani jmen dostupnych portu
            comboBox1.Items.Clear();                        //Vymazani akt. seznamu
            if (names.Length == 0)
            {
                comboBox1.Enabled = false;
                comboBox1.Text = "Žádný port";
            }
            else
            {
                comboBox1.Enabled = true;
                comboBox1.Items.AddRange(names);
                comboBox1.SelectedIndex = 0;
            }
        }

        /// <summary>
        /// Callback povoleni sifrovani
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            groupBox5.Enabled = checkBox1.Checked;
        }


        /// <summary>
        /// Callback pro vyvolani dialogu ziskani cesty k certifikatu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Soubor certifikátu (*.pfx)|*.pfx|Všechny soubory (*.*)|*.*";
            openFileDialog1.ShowDialog();
        }

        /// <summary>
        /// Callback udalosti ukonceni formjulare OK
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
        {
            textBox1.Text = openFileDialog1.FileName;
        }

        /// <summary>
        /// Aktualizace infoboxu
        /// </summary>
        private void updateStatusInfo()
        {
            richTextBox1.Text = "";
            richTextBox1.Text += "Komunikační rozhraní: " + (radioButton1.Checked ? "TCP/IP" : "COM port") + "\n";
            richTextBox1.Text += "Port: " + (radioButton1.Checked ? numericUpDown1.Value.ToString() : comboBox1.SelectedItem) + "\n";
            richTextBox1.Text += "Šifrování: " + ((radioButton1.Checked && checkBox1.Checked) ? "SSL verze 3" : "Není") + "\n";
            richTextBox1.Text += radioButton1.Checked ? ("Timeout spojení: " +  (numericUpDown2.Value.ToString() + " ms\n") ) : "Rychlost: " + comboBox2.SelectedItem + " kbps\n";
            richTextBox1.Text += "Verze aplikace: " + Application.ProductVersion.ToString();
        }

        /// <summary>
        /// Metioda callbacku volaneho v pripade, ze formular prejde do popredi
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Shown(object sender, EventArgs e)
        {
            updateStatusInfo();
        }

        /// <summary>
        /// Metoda callbacku zmeny vybraneho tabu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabControl1_Selected(object sender, TabControlEventArgs e)
        {
            updateStatusInfo();
        }

        private int counter = 1000000;


        private DisplayReader testReader;               //Instance tridy cteni displeje pro testovaci ucely

        private FileStream f;                           //Stream, kam se maji zapisovat docasna data

        /// <summary>
        /// Zpracovani informace o zmene interniho stavu
        /// </summary>
        /// <param name="s">stav</param>
        private void StateChangeReceived(RemoteControlState s)
        {
            try
            {
                if (this.InvokeRequired)                                                //Pokud je nutne synchronizovat vlakna
                {

                    this.Invoke((MethodInvoker)delegate { StateChangeReceived(s); });   //volej tuto metodu znovu snchronne
                    return;
                }
                switch (s)                                                              //Jinak proved prislusne akce
                {
                    case RemoteControlState.Stopped:
                        label12.ForeColor = Color.Black;
                        label12.Text = "Zastaven";
                        notifyIcon1.BalloonTipText = "ERD: Zastaven";
                        notifyIcon1.ShowBalloonTip(2000);
                        break;
                    case RemoteControlState.Listenning:
                        label12.ForeColor = Color.Orange;
                        label12.Text = "Naslouchám";
                        notifyIcon1.BalloonTipText = "ERD: Naslouchám";
                        notifyIcon1.ShowBalloonTip(2000);
                        break;
                    case RemoteControlState.Configuring:
                        label12.ForeColor = Color.Green;
                        label12.Text = "Nastavuji";
                        notifyIcon1.BalloonTipText = "ERD: Nastavuji";
                        notifyIcon1.ShowBalloonTip(2000);
                        break;
                    case RemoteControlState.Active:
                        label12.ForeColor = Color.Green;
                        label12.Text = "Aktivní";
                        notifyIcon1.BalloonTipText = "ERD: Aktivní";
                        notifyIcon1.ShowBalloonTip(2000);
                        break;
                    case RemoteControlState.Error:
                        label12.ForeColor = Color.Red;
                        label12.Text = "Chyba";
                        notifyIcon1.BalloonTipText = "ERD: Chyba!";
                        notifyIcon1.ShowBalloonTip(5000);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception)
            {

                Console.WriteLine("Could not proceed state change.");
            }
            
            

        }

        /// <summary>
        /// Callback tlacitka pro spusteni mericiho testu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            if (!testStarted)
            {
                try
                {
                    StartTest();
                }
                catch (Exception)
                {
                    Console.WriteLine("Test could not be started.");
                }
                if (testReader.IsStarted)
                {
                    button4.Text = "Stop";
                    testStarted = true;
                }
            }
            else
            {
                ClosePerformanceTest();
                
                label2.Text = "Test byl přerušen.";
            }
            
        }

        /// <summary>
        /// Spusteni testovaciho cteni
        /// </summary>
        private void StartTest()
        {
            timer1.Interval = 500;
            f = new FileStream("tstStream.tmp", FileMode.Create, FileAccess.ReadWrite);     //Otevreni vystupniho 
            counter = 0;
            testReader = new DisplayReader(f, DRParams);    //Inicializace
            testReader.Start();
            timer1.Start();                                 //Spusteni casovace pro odmereni dfelky testu
            label2.Text = "Test výkonu probíhá...";
            progressBar1.Visible = true;
            progressBar1.Maximum = 20;
            progressBar1.Value = 0;
        }

        /// <summary>
        /// Metoda callbacku casovace pro mereni casu testu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            counter++;
            if (counter >= 20)
            {
                
                label2.Text = "Maximální možný výkon: " + testReader.GetFPS().ToString() + " FPS.";
                ClosePerformanceTest();
                return;
            }
            progressBar1.Value = counter;
        }
        
        /// <summary>
        /// Metoda zajisti korektni ukonceni vykonnostniho testu
        /// </summary>
        private void ClosePerformanceTest()
        {
            testReader.Stop();
            testReader = null;
            timer1.Stop();
            f.Close();
            progressBar1.Visible = false;
            button4.Text = "Start";
            testStarted = false;
        }

        /// <summary>
        /// Nacteni parametru ze tridy DisplayReader do GUI
        /// </summary>
        private void ReloadParams()
        {
            numericUpDown3.Value = RC.Display.DisplayParams.MaxSampling;
            numericUpDown4.Value = RC.Display.DisplayParams.MinSampling;
            numericUpDown5.Value = RC.Display.DisplayParams.CollectorTimeOut/1000;
            numericUpDown6.Value = RC.Display.DisplayParams.DeviceScreen.Width;
            numericUpDown7.Value = RC.Display.DisplayParams.DeviceScreen.Height;

        }

        /// <summary>
        /// Resetuje parametry DisplayReaderu do Defaultu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button8_Click(object sender, EventArgs e)
        {
            RC.Display.DisplayParams = new DisplayReaderParams();
            ReloadParams();
            
        }

        /// <summary>
        /// Vrati zmeny ve formulari
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button7_Click(object sender, EventArgs e)
        {
            ReloadParams();
        }


        /// <summary>
        /// Ulozi zmeny do tridy DisplayReader
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            CaptureParams();
            RC.Display.DisplayParams = DRParams;
        }

        /// <summary>
        /// Ziska parametry z formulare
        /// </summary>
        private void CaptureParams()
        {
            DRParams = new DisplayReaderParams();
            DRParams.MaxSampling = System.Convert.ToInt16(numericUpDown3.Value);
            DRParams.MinSampling = System.Convert.ToInt16(numericUpDown4.Value);
            DRParams.CollectorTimeOut = System.Convert.ToInt16(numericUpDown5.Value) * 1000;
            DRParams.DeviceScreen = new Size(System.Convert.ToInt16(numericUpDown6.Value), System.Convert.ToInt16(numericUpDown7.Value));
        }

        /// <summary>
        /// Reakce formulare na zmenu velikosti
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Resize(object sender, EventArgs e)
        {
            if (FormWindowState.Minimized == this.WindowState)
            {
                this.Hide();
                notifyIcon1.Visible = true;
                notifyIcon1.ShowBalloonTip(500);
                
                
                this.ShowInTaskbar = true;
            }
            else if (FormWindowState.Normal == this.WindowState)
            {
                notifyIcon1.Visible = false;
            }
        }

        /// <summary>
        /// Reakce stavove ikony na dvojklik
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Show();
            this.WindowState = FormWindowState.Normal;
        }

        /// <summary>
        /// Callback tlacitka Start
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            Start();

        }

        /// <summary>
        /// Funkce pro odstartovani cekani na klienta
        /// </summary>
        public void Start()
        {
            bool result = false;
            if (radioButton2.Checked)
            {
                result = RC.Start(comboBox1.SelectedItem.ToString(), System.Convert.ToInt32(comboBox2.SelectedItem.ToString()));
            }
            else
            {
                if (checkBox1.Checked)
                {
                    if (testPassword())
                    {
                        result = RC.Start(System.Convert.ToInt32(numericUpDown1.Value), new X509Certificate2(textBox1.Text, maskedTextBox1.Text));
                    }
                    else
                        MessageBox.Show("Neplatný certifikát nebo heslo.");
                }
                else
                {
                    result = RC.Start(System.Convert.ToInt32(numericUpDown1.Value));
                }
            }

            LockControls(result);
        }

        /// <summary>
        /// Funkce pro uzamknuti ovladacich prvku
        /// </summary>
        /// <param name="par">true pokud maji byt zamknuty</param>
        private void LockControls(bool par)
        {
            btnStart.Enabled = !par;
            button3.Enabled = par;
            groupBox1.Enabled = !par;
            groupBox2.Enabled = !par;
            groupBox3.Enabled = !par;
            groupBox6.Enabled = !par;
            groupBox7.Enabled = !par;
            groupBox8.Enabled = !par;

            if (!par)
            {
                radioButton1_CheckedChanged(this, null);
            }


        }

        /// <summary>
        /// Metoda pro zpracovani prijate udalosti
        /// </summary>
        /// <param name="e">text udalosti</param>
        public void NewEventReceived(string e)
        {
            try
            {
                if (this.InvokeRequired)                                            //Pokud je potreba synchronizace
                {
                
                    this.Invoke((MethodInvoker)delegate { NewEventReceived(e); });  //Volej metodu synchronne
                return;
                }
                if (checkBox2.Checked)                                              //Nasleduji akce metody
                {
                    listBox1.Items.Add(DateTime.Now.ToLongTimeString() + ": " + e);
                }
                else
                    listBox1.Items.Add(e);
                
            }
            catch (Exception)
            {
                Console.WriteLine("Could not send event...");
            }


        }

        /// <summary>
        /// Udalost osetrujici ukonceni programu
        /// Ma dojit k ulozeni nastaveni a zastavit dalkove ovladani.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            SaveIniSettings(Application.StartupPath + INI_FILE_NAME);       //Ulozeni
            RC.Stop();                                                      //Zastaveni vsech sluzeb
        }

        /// <summary>
        /// Callback tlacitka Stop
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            RC.Stop();
            LockControls(false);
        }


        /// <summary>
        /// Funkce pro zpracovan parameru z prikazove radky
        /// </summary>
        /// <param name="args"></param>
        internal void SetArgs(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i] == "-m")
                {

                    WindowState = FormWindowState.Minimized;
                    notifyIcon1.Visible = true;
                    notifyIcon1.ShowBalloonTip(500);
                    ShowInTaskbar = true;
                    Hide();
                }
                if (args[i] == "-s")
                    Start();
            }
        }

        /// <summary>
        /// Callback tlacitka Test
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click_1(object sender, EventArgs e)
        {
            if (testPassword())
                MessageBox.Show("Certifikát a heslo jsou platné.");
            else
                MessageBox.Show("Neplatný certifikát nebo heslo.");
        }

        /// <summary>
        /// Metoda provede overeni dvojice Certifikat-heslo
        /// </summary>
        /// <returns>uspesnost</returns>
        private bool testPassword()
        {
            try
            {
                X509Certificate2 cert = new X509Certificate2(textBox1.Text, maskedTextBox1.Text);
            }
            catch (Exception)
            {

                return false;
            }
            
            return true;
        }
    }
}
