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;
using ChamChat.Models;

namespace ChamChat
{
    public partial class ChamChat : Form
    {
        Boolean DEBUGGING = true;

        public ChamChat()
        {
            InitializeComponent();
        }

        String _ProfileFileName;
        List<Interface> _Interfaces = new List<Interface>();
        List<Client> _Clients = new List<Client>();
        List<Task> _Tasks = new List<Task>();
        List<ProfileOption> _selectedOptions = new List<ProfileOption>();
        Client _storedClient;
        Graph _Graph;
        Color _StepSelectionColor = Color.LightYellow;
        Color _StepNotSelectedColor = SystemColors.Window; // Reset  in ChamChat_Load() to always match design

        private const string FormHeaderText = "ChamChat";


        protected override bool ProcessDialogKey(Keys keyData)
        {
            //if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
            //{
            //    if (tabControl.SelectedTab == tabGraph)
            //    {
            //        tabControl.SelectedTab = tabProfile;
            //        return true;
            //    }
            //}
            return base.ProcessDialogKey(keyData);
        }



        private void ChamChat_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Beta version!", "ChamChat", MessageBoxButtons.OK, MessageBoxIcon.Information);


            Layouts.Buttons(this);
            Layouts.DataGridViewNotSortable(dgvTasks);
            Layouts.DataGridViewNotSortable(dgvLoops);
            dgvTasks.DefaultCellStyle.SelectionBackColor = Color.LightGray;

            //tabProfile.BackColor = Color.LightCyan;

            _StepNotSelectedColor = dgvSteps.DefaultCellStyle.BackColor;

            tabControl.Appearance = TabAppearance.FlatButtons;
            tabControl.ItemSize = new Size(0, 1);
            tabControl.SizeMode = TabSizeMode.Fixed;
            tabControl.SelectedTab = tabProfile;
            //tabControl.Dock = DockStyle.Fill;
            tabControl.Location = new Point(0, 20);
            tabControl_SelectedIndexChanged(null, null); // Set menu options


            _Interfaces = Interfaces.GetAll();
            InitInterfaceListBox();


            _Clients = Clients.GetAll();


            // Link client to interface if type matches or else remove
            for (int i = _Clients.Count - 1; i >= 0; i--)
            {
                bool linked =false;
                Client c = _Clients[i];
                foreach (Interface iF in _Interfaces)
                {
                    if (c.InterfaceType == iF.Type)
                    {
                        c.Interface = iF;
                        linked = true;
                        break; // break from foreach (Interface iF in _Interfaces) 
                    }
                }
                if (!linked)
                        _Clients.RemoveAt(i);
            }

            if (_Clients.Count == 0 && !DEBUGGING)
            {
                MessageBox.Show("No clients can be linked to the connected interface(s)!","ChamChat", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Application.Exit();
            }


            if (_Clients.Count == 0 && DEBUGGING)
            {
                tabControl.SelectedTab = tabInterfaceComm;
                cboxCmd.Text = "? <SP> A2LO <CR>";
                cbCR.Checked = false;
                cbLF.Checked = false; 
                cbANSIX328.Checked = true;
                return;
            }





            InitClientListBox();
            

            NewProfile(8);



            // Load settings from previous run of program
            try
            {
                cboxClient.SelectedIndex = Properties.Settings.Default.Client;
            }
            catch
            {
                cboxClient.SelectedIndex = 0;
            }


            
            // Open profile if exists
            try
            {
                if (File.Exists(Properties.Settings.Default.ProfileFilename))
                {
                    OpenFile(Properties.Settings.Default.ProfileFilename);
                }
            }
            catch { }


            // Set executable path in tasks
            Task.SetLogPath(Path.GetDirectoryName(Application.ExecutablePath));
        }


        private void InitClientListBox()
        {
            _storedClient = _Clients[0];
            cboxClient.Items.Clear();
            foreach (Client c in _Clients)
                cboxClient.Items.Add(string.Format("{0:00000}", c.MIDS));
            cboxClient.SelectedIndex = 0;
        }


        private void InitInterfaceListBox()
        {
            cboxInterfaces.Items.Clear();
            foreach (Interface c in _Interfaces)
                cboxInterfaces.Items.Add(c.ToString());

            if (_Interfaces.Count == 0)
                menuInterface.Enabled = false;
            else
            cboxInterfaces.SelectedIndex = 0;
        }

        
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                Interface iface = _Interfaces[cboxInterfaces.SelectedIndex];
                
                string cmd = (string)cboxCmd.Text.Trim();

                if (cbANSIX328.Checked)
                {
                    cmd = ANSI_X328.ToASCII(cmd);
                }
                else
                {
                    if (cbCR.Checked)
                        cmd += (char)13;

                    if (cbLF.Checked)
                        cmd += (char)10;
                }

                txtbCommandSent.Text = ANSI_X328.ToANSI(cmd) + " (" + ANSI_X328.ToStringOfHexPerChar(cmd, true) + ")";


                string returnData = null;
                try
                {

                    Int32 returnDataWaitms = 500;
                    if(!Int32.TryParse(txtbReturnDataWaitms.Text, out returnDataWaitms))
                        returnDataWaitms = 1000;

                    DataTransferLog log = iface.DataTransfer(cmd, out returnData, returnDataWaitms);

                    

                    if (cbANSIX328.Checked)
                    {
                        log.Command = ANSI_X328.ToANSI(cmd);
                        log.ReturnData = ANSI_X328.ToANSI(returnData);
                    }
                    lboxIF.Items.Add(log.ToString());

                    if (returnData != null)
                        txtbResponse.Text = log.ReturnData;
                    else
                        txtbResponse.Text = "InterfaceStatus: " + iface.InterfaceStatus.Message;

                }
                catch(Exception ex)
                {
                    lboxIF.Items.Add("Exception: " + ex.Message);
                    txtbResponse.Text = "N/A";
                }
                cboxCmd.Focus();
                cboxCmd.SelectAll();
            }
            catch (Exception ex)
            {
                lboxIF.Items.Add("Exception: " + ex.Message);
            }
        }



        private void btnClearMess_Click(object sender, EventArgs e)
        {
            lboxIF.Items.Clear();
        }

        private void cboxCmd_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                btnSend_Click(null, null);
            }
        }

        private void cboxClient_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Prevent overwriting by accident
            _ProfileFileName = null;
            this.Text = FormHeaderText;

            Client client = _Clients[cboxClient.SelectedIndex];


            dgvSteps_SetColumnVisibility(client);

            // List available options for this client
            while (dgvOptions.Rows.Count > 0)
                dgvOptions.Rows.RemoveAt(0);
            for (int i = 0; i < client.Options.Count; i++)
            {
                ProfileOption option = client.Options[i];
                dgvOptions.Rows.Add();
                dgvOptions[0, i].Value = option.ToStr();
            }

            if (client.Options.Count == 0)
                dgvOptions.Visible = false;
            else
                dgvOptions.Visible = true;


            // Set default types if a different client inherritance is chosen
            if (!client.GetType().Equals(_storedClient.GetType()))
                SetDefaultOptions(client);

            // Set size
            dgvOptions.Columns[0].Width = dgvOptions.Width - 3;
            if (dgvOptions.Rows.Count < 10)
            {
                dgvOptions.ScrollBars = ScrollBars.None;
                dgvOptions.Height = 22 * dgvOptions.Rows.Count + 2;
            }
            else
            {
                dgvOptions.ScrollBars = ScrollBars.Vertical;
                dgvOptions.Height = 200;
            }

            dgvOptions_UpdateSelection();  
           
            btnWriteProfile.Text = String.Format("Start {0:00000}", client.MIDS);
            lblClientType.Text = client.TypeDescription;


            // Save current for next selection of new client
            _storedClient = client;

            UpdateGUI();


        }

        private void btnAddStep_Click(object sender, EventArgs e)
        {
            InsertProfileStep();
        }

        private void dgvProfile_KeyPress(object sender, KeyPressEventArgs e)
        {
            // Only numbers allowed
            if (e.KeyChar < (char)Keys.D0 || e.KeyChar > (char)Keys.D9)
                e.Handled = true;
        }


        private void InsertProfileStep()
        {
            if (dgvSteps.CurrentCell == null)
                return;
            
            int i = dgvSteps.CurrentCell.RowIndex;
            if (i < 0)
                return;

            dgvSteps.Rows.Insert(i+1, 1);

            for (int r = 0; r < dgvSteps.Rows.Count; r++)
                dgvSteps[0, r].Value = string.Format("{0:00}", r+1);

            dgvSteps.CurrentCell = dgvSteps.Rows[i+1].Cells[1];
            UpdateGUI();
        }

        private void dgvSteps_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex < 0 || e.RowIndex < 0)
                return;

            if (dgvSteps.Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn)
                return;

            

            // Indicate if step is correct by coloring stepnumber
            Step step;
            for (int r = 0; r < dgvSteps.Rows.Count; r++)
                if (GetStep(r, out step))
                {
                    dgvSteps[0, r].Style.BackColor = dgvSteps[1, r].Style.BackColor;
                    dgvSteps[0, r].Style.SelectionBackColor = dgvSteps[1, r].Style.SelectionBackColor;
                }
                else
                {
                    dgvSteps[0, r].Style.BackColor = Color.OrangeRed;
                    dgvSteps[0, r].Style.SelectionBackColor = Color.OrangeRed;
                    
                }

            if(dgvSteps[e.ColumnIndex, e.RowIndex].Value == null)
            {
                UpdateGUI();
                return;
            }

            string v = dgvSteps[e.ColumnIndex, e.RowIndex].Value.ToString();
            if (v.Length == 0)
            {
                UpdateGUI();
                return;
            }

            Double i = 0;
            if (!Double.TryParse(v, out i))
                dgvSteps[e.ColumnIndex, e.RowIndex].Value = "";

            dgvSteps.CurrentCell = dgvSteps[e.ColumnIndex, e.RowIndex];
            
            UpdateGUI();
        }


        private bool GetStep(int row, out Step Step)
        {
            Client client = _Clients[cboxClient.SelectedIndex];
            Step = new Step(0);


            #region Return true and out null if row is empty
            bool contentFound = false;
            for (int i = 1;i< dgvSteps.ColumnCount; i++)
            {
                if (dgvSteps[i, row].Value != null)
                {
                    contentFound = true;
                    i = dgvSteps.ColumnCount;
                }
            }
            if (!contentFound)
            {
                Step = null;
                return true;
            }
            #endregion Return true and out null if row is empty


            // Check value for current step
            bool stepOK = true;

            // Duration
            double hrs = 0;
            if (!double.TryParse((string)dgvSteps[7, row].Value, out hrs))
                stepOK = false;
            double min = 0;
            if (!double.TryParse((string)dgvSteps[8, row].Value, out min))
                stepOK = false;
            Double duration = 60 * hrs + min;


            // Temperature
            Double temperature = 0;
            if (client.Parameters.Contains(Parameter.T))
                if (!Double.TryParse((string)dgvSteps[dgvProfileColumnT.Index, row].Value, out temperature))
                    stepOK = false;


            // Relative humidity
            Double rh = 0;
            if (client.Parameters.Contains(Parameter.RH))
                if (!Double.TryParse((string)dgvSteps[dgvProfileColumnRH.Index, row].Value, out rh))
                    stepOK = false;


            // Temperature Ramp ctrl
            Boolean rampCtrlT = false;
            if (client.Parameters.Contains(Parameter.RampT))
            {
                if (Convert.ToBoolean(dgvSteps.Rows[row].Cells[dgvProfileColumnRampCtrlT.Index].Value) == true)
                    rampCtrlT = true;
            }


            // RH Ramp ctrl
            Boolean rampCtrlRH = false;
            if (client.Parameters.Contains(Parameter.RampRH))
            {
                if (Convert.ToBoolean(dgvSteps.Rows[row].Cells[dgvProfileColumnRampCtrlRH.Index].Value) == true)
                    rampCtrlRH = true;
            }



            // Pre temperature 
            Double PreT = 0;
            if (client.Parameters.Contains(Parameter.PreTemp))
                if (!Double.TryParse((string)dgvSteps[5, row].Value, out PreT))
                    stepOK = false;


            // Pre temperature 
            Double limitT = 0;
            if (client.Parameters.Contains(Parameter.Tlimit))
                if (!Double.TryParse((string)dgvSteps[6, row].Value, out limitT))
                    stepOK = false;


            // Add if step is ok
            if (stepOK && duration > 0)
            {

                Step = new Step(duration);
                Step.T = temperature;
                Step.RH = rh;
                Step.RampCtrlT = rampCtrlT;
                Step.RampCtrlRH = rampCtrlRH;
                Step.PreTemp = PreT;
                Step.LimitT = limitT;
                return true;
            }
            return false;
        }


        private Loop GetLoop(int row)
        {
            int first = Int32.Parse(dgvLoops[0, row].Value.ToString());
            int last = Int32.Parse(dgvLoops[1, row].Value.ToString());
            int n = Int32.Parse(dgvLoops[2, row].Value.ToString());
            return new Loop(n, first - 1, last - 1);
        }



        private Boolean GetProfile(out Profile profile)
        {
            profile = new Profile();
            Client client = _Clients[cboxClient.SelectedIndex];
            List<Step> steps = new List<Step>();
            List<ProfileOption> options = new List<ProfileOption>();
            List<Loop> loops = new List<Loop>();

            for (int row = 0; row < dgvSteps.Rows.Count; row++)
            {
                Step step;
                if (GetStep(row, out step))
                {
                    if(step!=null)
                    steps.Add(step);
                }
                else
                {
                    return false;
                }
            }
            if (steps.Count == 0)
                return false;

            // ProfileOptions
            options = _selectedOptions;


            // Loops
            for(int i = 0;i<dgvLoops.Rows.Count;i++)
                loops.Add( GetLoop(i));

            // Title
            string title = txtbTitle.Text;
            if (title.Length < 2)
                return false;

            profile = new Profile(title, steps, loops, options);

            return true;
        }



        private void btnAddLoop_Click(object sender, EventArgs e)
        {
            int first_PK = (int)nudLoopFirst.Value-1;
            int last_PK = (int)nudLoopLast.Value-1;
            int n = (int)nudCount.Value;

            if (first_PK >= last_PK)
                return;

            if (n < 1)
                return;

            Profile p;
            if (!GetProfile(out p))
                return;

            List<Step> steps  = p.Steps;

           

            if (first_PK < 0 || last_PK > steps.Count)
            {
                MessageBox.Show("Loop values do not comply with steps!", "Add loop", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }


            // Loop can only be properly nested, they cannot overlap
            List<Loop> loops = new List<Loop>();
            for (int i = 0; i < dgvLoops.Rows.Count; i++)
                loops.Add(GetLoop(i));

            foreach (Loop l in loops)
            {
                if (l.First == first_PK && l.Last == last_PK)
                {
                    MessageBox.Show("Loop already exists!", "Add loop", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                // If loop starts within existing loop and ends outside, it is incorrect
                if (first_PK > l.First && first_PK < l.Last && last_PK > l.Last)
                {
                    MessageBox.Show("Loop starts within other loop, but ends outside. Loops can only be nested!", "Add loop", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                // If loop ends within existing loop and starts outside, it is incorrect
                if (last_PK > l.First && last_PK < l.Last && first_PK < l.First)
                {
                    MessageBox.Show("Loop ends within other loop, but starts outside. Loops can only be nested!", "Add loop", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            // Add loop if correct
            dgvLoops.Rows.Add();
            dgvLoops[0, dgvLoops.Rows.Count - 1].Value = (first_PK+1).ToString();
            dgvLoops[1, dgvLoops.Rows.Count - 1].Value = (last_PK+1).ToString();
            dgvLoops[2, dgvLoops.Rows.Count - 1].Value = n.ToString();
            UpdateGUI();
        }

        private void dgvLoops_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (dgvLoops.Rows.Count == 0)
                    return;

                int index = dgvLoops.CurrentCell.RowIndex;

                // Check selection
                if (index < 0)
                    return;

                dgvLoops.Rows.RemoveAt(index);
                UpdateGUI();
            }
        }

        private void btnWriteProfile_Click(object sender, EventArgs e)
        {
            Profile profile;
            if (!GetProfile(out profile))
            {
                MessageBox.Show("Could not get profile from data!", "Profile", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // Check absolute maximum temperature
            if (profile.MaximumTemperature > 210)
            {
                DialogResult dr = MessageBox.Show("Profile reaches temperature above 210 °C.\n\nAre you sure you want to continue?", "High temperatures!", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                if (dr != DialogResult.Yes)
                    return;
            }


            Client client = _Clients[cboxClient.SelectedIndex];
            Boolean IsStandAlone = true;


            // Create task
            Task task;
            if (client.GetType() == typeof(TSx_Client))
                task = new TSx_Task(client, profile);
            else if (client.GetType() == typeof(LHL113_Client))
                task = new LHL113_Task(client, profile);
            else if (client.GetType() == typeof(HTS7057_Client))
                task = new HTS7057_Task(client, profile);
            else if (client.GetType() == typeof(PLxKPH_Client))
                task = new PLxKPH_Task(client, profile);
            else
            {
                MessageBox.Show("Tasks are not implemented for this client!", "Start task", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }



            // If client is already in another task, prohibit adding task.
            if (_Tasks.FindIndex(delegate(Task t) { return (t.Client == task.Client); }) >= 0)
            {
                MessageBox.Show("The client is already used in another task. Complete the running task first!", "Start task", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }


            if (!task.IsStandAloneProgrammable(profile))
            {
                IsStandAlone = false;

                if (task.IsControlledProgrammable(profile))
                {
                    DialogResult dr = MessageBox.Show("Profile is not standalone programmable in client.\n\nDo you want to create a task?", "Profile to client", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                    if (dr != DialogResult.Yes)
                        return;
                }
                else
                {
                    string text = "";
                    foreach (ClientMessage clientMsg in task.Client.ClientMessages)
                        text += clientMsg.Message + "\n";

                    MessageBox.Show(text, "Profile to client", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    //MessageBox.Show("Profile cannot be performed!", "Profile to client", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }


            // Check if client is online
            try
            {
                if (!client.IsOnline())
                {
                    MessageBox.Show("Could not communicate with client!", "Start task", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
            catch (Exception ex)
            {
                String msg = String.Format("Could not verify communication with client! Connection lost?\n\n[Error: {0}]", ex.Message);
                MessageBox.Show(msg, "Start task", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }



            // Check if client is idle
            try
            {
                if (!client.IsIdle())
                {
                    MessageBox.Show("Client is not idle!", "Start task", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
            catch (Exception ex)
            {
                String msg = String.Format("Could not verify that client is idle! Connection lost?\n\n[Error: {0}].", ex.Message);
                MessageBox.Show(msg, "Start task", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            this.Cursor = Cursors.WaitCursor;

            // Start task
            task.Start(IsStandAlone); // Exceptions handled in task

            _Tasks.Add(task);
            UpdateTasksListing();
            dgvTasks.Rows[_Tasks.Count - 1].Selected = true;

            dgvTasks.CurrentCell = dgvTasks[0, _Tasks.Count - 1];
            dgvTasks_CellClick(null, null);

            tabControl.SelectedTab = tabTasks;

            this.Cursor = Cursors.Default;
        }



        private void btnTaskNextStep_Click(object sender, EventArgs e)
        {
            try
            {
                Task task = SelectedTask;

                if (task == null)
                    return;

                if (!task.Client.Parameters.Contains(Parameter.ProceedWithNextStep))
                {
                    DialogResult dr = MessageBox.Show("The 'proceed to next step feature' is not implemented in this client.", "Next step", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }


                if (task.Status == TaskStatus.IsRunning)
                {
                    DialogResult dr = MessageBox.Show("Are you sure you want to proceed to the next profile step?", "Next step", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                    if (dr != DialogResult.Yes)
                        return;
                }

                this.Cursor = Cursors.WaitCursor;
                

                if (!task.NextStep())
                    MessageBox.Show("Could not proceed with next step!", "Next step", MessageBoxButtons.OK, MessageBoxIcon.Warning);


                // Update GUI
                timerUpdateTask_Tick(null, null);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Could not proceed with next step!\n\n" + ex.Message, "Next step", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }



        private void btnTaskStop_Click(object sender, EventArgs e)
        {
            try
            {
                Task task = SelectedTask;

                if (task == null)
                    return;

                if (task.Status == TaskStatus.IsRunning)
                {
                    DialogResult dr = MessageBox.Show("Are you sure you want to end the test?", "Stop", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                    if (dr != DialogResult.Yes)
                        return;
                }

                this.Cursor = Cursors.WaitCursor;

                if (!task.Stop())
                    MessageBox.Show("Task thread was not properly ended!", "Stop", MessageBoxButtons.OK, MessageBoxIcon.Warning);

                System.Threading.Thread.Sleep(5000); // Wait for processes to finish


                // Update GUI
                timerUpdateTask_Tick(null, null);
            }
            catch(Exception ex) 
            {
                MessageBox.Show("Task thread was not properly ended!\n\n"+ ex.Message, "Stop", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }


        public Task SelectedTask
        {
            get
            {
                if (dgvTasks.CurrentCell == null)
                    return null;

                int i = dgvTasks.CurrentCell.RowIndex;

                if (i < 0)
                    return null;

                return _Tasks[i];
            }
        }

        private void lbTasks_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateTask(SelectedTask);
        }



        private void UpdateTasksListing()
        {
            if (_Tasks.Count != dgvTasks.Rows.Count)
            {
                // Task added or removed
                while (dgvTasks.Rows.Count > 0)
                    dgvTasks.Rows.RemoveAt(0);
                for (int i = 0; i < _Tasks.Count; i++)
                {
                    Task t = _Tasks[i];
                    dgvTasks.Rows.Add();
                    dgvTasks[0, i].Value = t.Description;
                    dgvTasks[0, i].Style.ForeColor = t.StatusColor;
                    dgvTasks[0, i].Style.SelectionForeColor = t.StatusColor;
                }
                dgvTasks.ClearSelection();
            }
            else
            {
                // Only update colors
                for (int i = 0; i < _Tasks.Count; i++)
                {
                    Task t = _Tasks[i];
                    dgvTasks[0, i].Style.ForeColor = t.StatusColor;
                    dgvTasks[0, i].Style.SelectionForeColor = t.StatusColor;
                }
            }
        }

        private void UpdateTask(Task task)
        {
            if (task.Status == TaskStatus.Stopped || task.Status == TaskStatus.Finished)
                btnTaskStop.Enabled = false;


            lblTaskStatus.ForeColor = task.StatusColor;
            lblTaskStatus.Text = SelectedTask.Status.ToText();
            if (task.Status == TaskStatus.IsRunning)
            {
                double totalMin = (DateTime.Now - task.StartOfTask).TotalMinutes;
                double hrs = Math.Floor(totalMin / 60);
                double min = totalMin - hrs * 60;
                lblTaskStatus.Text += String.Format(" ({0:00}h{1:00})", hrs, min);
            }
            lblTaskProgress.Text = task.TaskProgress;

            if (task.IsRunningStandAlone)
                lblTaskIsStandalone.Text = "Stand alone task";
            else
                lblTaskIsStandalone.Text = "Interface controlled task";


            // Interface description
            lblTaskInterface.Text = task.Client.Interface.ToString();


            // lboxInterfaceLogs (cleared when index changes in task selection)
            List<DataTransferLog> dataTransferLogs = new List<DataTransferLog>(task.Client.DataTransferLogs);
            foreach (DataTransferLog il in dataTransferLogs)
                if (!lboxDataTransferLogs.Items.Contains(il.ToString()))
                    lboxDataTransferLogs.Items.Insert(0, il.ToString());


            // Task event logs (cleared when index changes in task selection)
            foreach (String s in task.EventLogs)
                if (!lboxEventLogs.Items.Contains(s))
                    lboxEventLogs.Items.Insert(0, s);
        }
        
        private void timerUpdateTask_Tick(object sender, EventArgs e)
        {
            // Process may not cause an unhandled exception!
            if (SelectedTask != null)
            {
                DateTime dt = DateTime.Now;
                try
                {
                    lblLastTaskUpdate.Text = string.Format("Last GUI refresh: {0:00}:{1:00}:{2:00}", dt.Hour, dt.Minute, dt.Second);

                    UpdateTask(SelectedTask);
                    UpdateTasksListing();
                }
                catch (Exception ex)
                {
                    lblLastTaskUpdate.Text = "Error: timerUpdateTask_Tick(), " + ex.Message;
                }
            }
        }




        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Activate update timer
            if (tabControl.SelectedTab == tabTasks && _Tasks.Count > 0)
            {
                timerUpdateTask.Start();
                timerUpdateTask_Tick(null, null);
            }
            else
                timerUpdateTask.Stop();


            // Set available menu options
            if (tabControl.SelectedTab != tabProfile)
            {
                menuProfileSave.Enabled = false;
                menuProfileSaveAs.Enabled = false;
                MenuProfileImport.Enabled = false;
                MenuProfileStore.Enabled = false;
            }
            else
            {
                menuProfileSave.Enabled = true;
                menuProfileSaveAs.Enabled = true;
                MenuProfileImport.Enabled = true;
                MenuProfileStore.Enabled = true;
            }

        }

        private void dgvTasks_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            Task task = SelectedTask;

            if (task == null)
            {
                gbTask.Visible = false;
                return;
            }

            lboxDataTransferLogs.Items.Clear();
            lboxEventLogs.Items.Clear();
            gbTask.Visible = true;
            gbTask.Text = task.Description;
            lblProfileTitle.Text = SelectedTask.Profile.Title;

            if (task.Status == TaskStatus.Stopped || task.Status == TaskStatus.Finished)
                btnTaskStop.Enabled = false;
            else
                btnTaskStop.Enabled = true;

            
            if (task.Client.Parameters.Contains(Parameter.ProceedWithNextStep))
                btnTaskNextStep.Enabled = true;
            else
                btnTaskNextStep.Enabled = false;


            UpdateTask(task);
        }







        private void menuTasks_Click(object sender, EventArgs e)
        {
            tabControl.SelectedTab = tabTasks;
        }



        private void SetDefaultOptions(Client client)
        {
            _selectedOptions = new List<ProfileOption>();

            // Preselect options with lowest XX00-XX99 value
            foreach (ProfileOption c_opt in client.Options)
            {
                List<ProfileOption> list = _selectedOptions.FindAll(delegate(ProfileOption po) { return (((int)po).ToString().Substring(0, 2) == ((int)c_opt).ToString().Substring(0, 2)); });
                if (list.Count > 0)
                {
                    // Group already in selected options. Replace if lower.
                    foreach (ProfileOption opt in list)
                    {
                        if ((int)c_opt < (int)opt)
                        {
                            _selectedOptions.Remove(opt);
                            _selectedOptions.Add(c_opt);
                        }
                    }
                }
                else
                    _selectedOptions.Add(c_opt);
            }
        }


        private void NewProfile(int rows)
        {
            Client client = _Clients[cboxClient.SelectedIndex];

            List<ProfileOption> selectedOptions = new List<ProfileOption>();
            while (dgvOptions.Rows.Count > 0)
                dgvOptions.Rows.RemoveAt(0);
            for (int i = 0; i < client.Options.Count; i++)
            {
                ProfileOption option = client.Options[i];
                dgvOptions.Rows.Add();
                dgvOptions[0, i].Value = option.ToStr();
            }

            SetDefaultOptions(client);
            dgvOptions.Columns[0].Width = dgvOptions.Width - 3;
            if (dgvOptions.Rows.Count < 10)
                dgvOptions.ScrollBars = ScrollBars.None;
            else
                dgvOptions.ScrollBars = ScrollBars.Vertical;

            dgvOptions_UpdateSelection();  

            DateTime d = DateTime.Now;
            string title =  string.Format("{0:0000}-{1:00}-{2:00} {3:00}h{4:00}", d.Year, d.Month, d.Day, d.Hour, d.Minute);

            
            lblSummary.Text = "Duration (hh:mm): -";
            btnWriteProfile.Enabled = false;
            pnlGraphControls.Visible = false;


            Profile p = new Profile(title, new List<Step>(), new List<Loop>(), _selectedOptions);

            ProfileToGUI(p);

            _ProfileFileName = null;
            this.Text = FormHeaderText;

            for (int i = 0; i < rows; i++)
            {
                dgvSteps.Rows.Add();
                dgvSteps[0,i].Value = string.Format("{0:00}", i+1);
            }
            dgvSteps.CurrentCell = dgvSteps[1, 0];
            UpdateGUI();
        }



        private void dgvSteps_SetAllColumnsVisible()
        {
            dgvSteps.SuspendLayout();
            foreach (DataGridViewColumn column in dgvSteps.Columns)
                column.Visible = true;

            dgvSteps.ResumeLayout();
            dgvSteps.Refresh();
        }
        private void dgvSteps_SetColumnVisibility(Client client)
        {
            dgvSteps_SetAllColumnsVisible();
            
            List<Parameter> parameters = client.Parameters;

            if (!parameters.Contains(Parameter.RampT))
                dgvProfileColumnRampCtrlT.Visible = false;

            if (!parameters.Contains(Parameter.PreTemp))
                dgvProfileColumnPreT.Visible = false;

            if (!parameters.Contains(Parameter.RH))
                dgvProfileColumnRH.Visible = false;

            if (!parameters.Contains(Parameter.RampRH))
                dgvProfileColumnRampCtrlRH.Visible = false;

            if (!parameters.Contains(Parameter.Tlimit))
                dgvProfileColumnLimit.Visible = false;

            // Distribute columns
            int numVisible = 0;
            foreach (DataGridViewColumn column in dgvSteps.Columns)
                if (column.Visible)
                {
                    numVisible++;
                    column.SortMode = DataGridViewColumnSortMode.NotSortable;
                }
            numVisible--; // Step number column

            dgvSteps.Columns[0].Width = 40;
            int w = dgvSteps.Width - dgvSteps.Columns[0].Width;
            int colWidth = (int)Math.Floor((double)w / (double)numVisible);
            for (int i = 1; i < dgvSteps.Columns.Count; i++)
                dgvSteps.Columns[i].Width = colWidth;

            dgvSteps.Refresh();
        }





        private void CleanUpTaskList()
        {
            if (_Tasks.RemoveAll(delegate(Task t) { return (t.Status != TaskStatus.IsRunning); }) == 0)
                return;
            UpdateTasksListing();
            dgvTasks.ClearSelection();
            gbTask.Visible = false;
        }

        private void btnCleanUpTaskList_Click(object sender, EventArgs e)
        {
            CleanUpTaskList();
        }

        private void dgvProfile_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (dgvSteps.CurrentCell != null)
                    if (dgvSteps.CurrentCell.ColumnIndex == 0)
                        RemoveStep(dgvSteps.CurrentCell.RowIndex);
            }
        }

        private void RemoveStep(int row)
        {
            List<int> toRemove = new List<int>();
            for(int i=0;i<dgvLoops.RowCount;i++)
            {
                Loop l = GetLoop(i);

                if (l.First == row || l.Last == row)
                    toRemove.Add(i);

                if (l.First > row)
                    dgvLoops[0, i].Value = l.First ;

                if (l.Last > row)
                    dgvLoops[1, i].Value = l.Last ;
            }   

            // Remove loops starting with hightest rown index
            for (int i = toRemove.Count-1; i >= 0;i--)
                dgvLoops.Rows.RemoveAt(toRemove[i]);
            
            // Remove row in steps and renumber
            dgvSteps.Rows.RemoveAt(row);
            for (int i = 0; i < dgvSteps.Rows.Count; i++)
                dgvSteps[0, i].Value = string.Format("{0:00}", i + 1);
        }




        private void dgvOptions_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            ProfileOption clicked = _Clients[cboxClient.SelectedIndex].Options[e.RowIndex];
         
            // Disable other options in group. Options start with 2 same digits.
            string d = ((int)clicked).ToString().Substring(0, 2);
            _selectedOptions.RemoveAll(delegate(ProfileOption opt) { return (((int)opt).ToString().Substring(0, 2) == d); });
            _selectedOptions.Add(clicked);

            dgvOptions_UpdateSelection();
        }

        private void dgvOptions_UpdateSelection()
        {
            for (int i = 0; i < dgvOptions.Rows.Count; i++)
            {
                dgvOptions.Rows[i].DefaultCellStyle.ForeColor = SystemColors.ControlDark;
                foreach (ProfileOption opt in _selectedOptions)
                    if (opt.ToStr() == (string)dgvOptions[0, i].Value)
                        dgvOptions.Rows[i].DefaultCellStyle.ForeColor = SystemColors.ControlText;
            }

            dgvOptions.ClearSelection();
        }






        private void btnCleanUpSteps_Click(object sender, EventArgs e)
        {
            List<int> emptySteps = new List<int>();
            for (int r = 0; r < dgvSteps.Rows.Count; r++)
            {
                Step s;
                if (GetStep(r, out s))
                {
                    if (s == null)
                        emptySteps.Add(r);
                }
            }
            
            // Remove rows, but start with highest row index
            while (emptySteps.Count > 0)
            {
                int max = -1;
                foreach (int step in emptySteps)
                    if (step > max)
                        max = step;
                RemoveStep(max);
                emptySteps.Remove(max);
            }
            UpdateGUI();



            return;

            // List<int> incompleteSteps = new List<int>();
            // List<int> emptySteps = new List<int>();
            // for (int r = 0; r < dgvSteps.Rows.Count; r++)
            // {
            //     Step s;
            //     if (GetStep(r, out s))
            //     {
            //         if (s == null)
            //             emptySteps.Add(r);
            //     }
            //     else
            //         incompleteSteps.Add(r);
            // }

            // if(incompleteSteps.Count>0)
            // {
            //        string msg = "You are about to remove "+incompleteSteps.Count.ToString()+" incomplete step(s)!\n\nAre you sure you want to continue?";
            //         DialogResult dr = MessageBox.Show(msg, "Clean up steps", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
            //         if (dr != DialogResult.Yes)
            //             incompleteSteps = new List<int>();
            //}


            //  haal alleen lege stappen weg


            // List<int> toRemove = new List<int>();
            // foreach (int s in incompleteSteps)
            //     toRemove.Add(s);
            // foreach (int s in emptySteps)
            //     toRemove.Add(s);


            //  Remove rows, but start with highest row index
            // while (toRemove.Count > 0)
            // {
            //     int max = -1;
            //     foreach (int step in toRemove)
            //         if (step > max)
            //             max = step;
            //     RemoveStep(max);
            //     toRemove.Remove(max);
            // }

            // UpdateGUI();
        }


        private void UpdateGUI()
        {
            // Loops
            if (dgvLoops.RowCount < 5)
            {
                dgvLoops.ScrollBars = ScrollBars.None;
                foreach (DataGridViewColumn c in dgvLoops.Columns)
                    c.Width = 45;
            }
            else
            {
                dgvLoops.ScrollBars = ScrollBars.Vertical;
                foreach (DataGridViewColumn c in dgvLoops.Columns)
                    c.Width = 39;
            }


            // Steps
            if (dgvSteps.RowCount < 9)
            {
                dgvSteps.ScrollBars = ScrollBars.None;
                dgvSteps.Height = (1+dgvSteps.RowCount) * 22 -1;

            }
            else
            {
                dgvSteps.ScrollBars = ScrollBars.Vertical;
                dgvSteps.Height = (1 +8) * 22 - 1;
            }




            Profile profile;

            if (cboxClient.SelectedIndex < 0 || !GetProfile(out profile))
            {
                lblSummary.Text = "Duration (hh:mm): -";
                btnWriteProfile.Enabled = false;
                return;
            }

            if (profile.Steps.Count == 0)
            {
                lblSummary.Text = "Duration (hh:mm): -";
                btnWriteProfile.Enabled = false;
                pnlGraphControls.Visible = false;
            }

            pnlGraphControls.Visible = true;
            btnWriteProfile.Enabled = true;


            double totalMin = profile.DurationMin;
            double hrs = Math.Floor(totalMin / 60);
            double min = totalMin - 60 * hrs;
            lblSummary.Text = String.Format("Duration (hh:mm): {0:0}:{1:00}", hrs, min);

            // Graph
            Client client = _Clients[cboxClient.SelectedIndex];
            pnlGraphControls.Visible = true;
            cboxGraphSerialized.Checked = false;
            if (profile.Loops.Count == 0)
                cboxGraphSerialized.Visible = false;
            else
                cboxGraphSerialized.Visible = true;
            InitGraph(profile, client);
        }

     

        private void menuTasksCleanUp_Click(object sender, EventArgs e)
        {
            CleanUpTaskList();
        }


        private Boolean SaveProfile(Profile Profile, String Filename)
        {
            try
            {
                Profile.ClientMIDS = _Clients[cboxClient.SelectedIndex].MIDS;
                using (System.IO.StreamWriter output = new System.IO.StreamWriter(Filename, false))
                {
                    output.WriteLine(Profile.XML);
                }
                this.Text = FormHeaderText + " [" + new FileInfo(Filename).Name + "]";
                return true;
            }

            catch(Exception ex)
            {
                MessageBox.Show("Failed to save profile!\n\n" + ex.Message, "Save profile", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }



        private void dgvSteps_Fill(List<Step> steps)
        {
            try
            {
                // Disable event triggering for dgv
                this.dgvSteps.CellValueChanged -= new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvSteps_CellValueChanged);

                dgvSteps_SetAllColumnsVisible(); // Set all rows visible to alter them

                while (dgvSteps.Rows.Count > 0)
                    dgvSteps.Rows.RemoveAt(0);

                if (steps.Count > 0)
                    dgvSteps.Rows.Add(steps.Count);

                for (int i = 0; i < steps.Count; i++)
                {
                    Step s = steps[i];

                    dgvSteps[dgvProfileColumnStep.Index, i].Value = string.Format("{0:00}", i + 1);
                    dgvSteps[dgvProfileColumnT.Index, i].Value = s.T.ToString();
                    dgvSteps[dgvProfileColumnT.Index, i].Value = s.T.ToString();
                    dgvSteps[dgvProfileColumnRH.Index, i].Value = s.RH.ToString();
                    dgvSteps[dgvProfileColumnPreT.Index, i].Value = s.PreTemp.ToString();
                    dgvSteps[dgvProfileColumnLimit.Index, i].Value = s.LimitT.ToString();

                    DataGridViewRow row = dgvSteps.Rows[i];
                    if (s.RampCtrlT)
                        row.Cells[dgvProfileColumnRampCtrlT.Index].Value = true;
                    else
                        row.Cells[dgvProfileColumnRampCtrlT.Index].Value = false;
                    if (s.RampCtrlRH)
                        row.Cells[dgvProfileColumnRampCtrlRH.Index].Value = true;
                    else
                        row.Cells[dgvProfileColumnRampCtrlRH.Index].Value = false;

                    int hrs = (int)Math.Floor(s.DurationMin / 60);
                    int min = (int)Math.Round(s.DurationMin - 60 * hrs);
                    dgvSteps[dgvProfileColumnDurationHr.Index, i].Value = hrs.ToString();
                    dgvSteps[dgvProfileColumnDurationMin.Index, i].Value = min.ToString();
                }
                dgvSteps_SetColumnVisibility(_Clients[cboxClient.SelectedIndex]);  // Set visibility of columns acc. client
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
            }
            finally
            {
                this.dgvSteps.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvSteps_CellValueChanged);
            }
        }


        private void ProfileToGUI(Profile p)
        {
            try
            {
                // Steps
                dgvSteps_Fill(p.Steps);

                // Loops
                while (dgvLoops.Rows.Count > 0)
                    dgvLoops.Rows.RemoveAt(0);
                if (p.Loops.Count > 0)
                    dgvLoops.Rows.Add(p.Loops.Count);
                for (int i = 0; i < p.Loops.Count; i++)
                {
                    Loop l = p.Loops[i];
                    dgvLoops[0, i].Value = string.Format("{0:0}",l.First + 1);
                    dgvLoops[1, i].Value = string.Format("{0:0}", l.Last + 1);
                    dgvLoops[2, i].Value = l.N.ToString();
                }

                // Options
                _selectedOptions = p.Options;
                dgvOptions_UpdateSelection();

                // Title
                txtbTitle.Text = p.Title;

                // Update duration
                UpdateGUI();
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
            }
        }


        private void MenuProfileOpen_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("Changes to the current profile will be lost!\n\nAre you sure you want to continue?", "Open profile", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
            if (dr != DialogResult.Yes)
                return;


            ofdOpen.FileName = "";
            ofdOpen.Filter = "CCP|*.ccp";
            ofdOpen.Title = "Open profile";
            dr = ofdOpen.ShowDialog();
            if (dr == DialogResult.Cancel)
                return;

            if (!File.Exists(ofdOpen.FileName))
                return;
            try
            {
                OpenFile(ofdOpen.FileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to open profile!\n\n" + ex.Message, "Open", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }


        }


        private void OpenFile(String file)
        {
            string xml = "";
            try
            {
                using (StreamReader reader = File.OpenText(file)) { xml = reader.ReadToEnd(); }
            }
            catch { throw new Exception(string.Format("Could not read file {0}", new FileInfo(file).Name)); }

            Profile p = new Profile(xml);
            
            
            if (p.ClientMIDS != 0)
            {
                int i = _Clients.FindIndex(delegate(Client c) { return (c.MIDS == p.ClientMIDS); });
                if (i > -1)
                    cboxClient.SelectedIndex = i;
            }


            ProfileToGUI(p);

            _ProfileFileName = file;
            this.Text = FormHeaderText + " [" + new FileInfo(file).Name + "]";
        }


        private void menuProfile_Click(object sender, EventArgs e)
        {
            tabControl.SelectedTab = tabProfile;
        }

        private void menuProfileNew_Click(object sender, EventArgs e)
        {
            // Check if dgvSteps is empty
            bool contentFound = false;
            for (int r = 0; r < dgvSteps.Rows.Count; r++)
            {
                for (int i = 1; i < dgvSteps.ColumnCount; i++)
                {
                    if (dgvSteps[i, r].Value != null)
                    {
                        contentFound = true;
                        i = dgvSteps.ColumnCount;
                    }
                }
            }
            if (contentFound)
            {
                DialogResult dr = MessageBox.Show("Changes to the current profile will be lost!\n\nAre you sure you want to continue?", "New profile", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
                if (dr != DialogResult.Yes)
                    return;
            }

            tabControl.SelectedTab = tabProfile;
            NewProfile(8);
        }

        private void menuProfileSave_Click(object sender, EventArgs e)
        {
            Profile p = new Profile();
            if (GetProfile(out p))
            {
                if (_ProfileFileName != null)
                {
                    if (File.Exists(_ProfileFileName))
                    {
                        SaveProfile(p, _ProfileFileName);
                        return;
                    }
                }
                menuProfileSaveAs_Click(sender, e); // Do a 'Save As'
            }
            else
                MessageBox.Show("Cannot save profile. Profile is incomplete!", "Save profile", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void menuProfileSaveAs_Click(object sender, EventArgs e)
        {
            Profile profile = new Profile();
            if (!GetProfile(out profile))
            {
                MessageBox.Show("Cannot save profile. Profile is incomplete!", "Save profile", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            sfdSave.Title = "Save profile";
            sfdSave.FileName = profile.Title + ".ccp";
            sfdSave.Filter = "CCP|*.ccp";
            DialogResult dr = sfdSave.ShowDialog();
            if (dr != DialogResult.OK)
                return;

            _ProfileFileName = sfdSave.FileName;
            SaveProfile(profile, _ProfileFileName);
        }

        private void menuInterface_Click(object sender, EventArgs e)
        {
            tabControl.SelectedTab = tabInterfaceComm;
        }



        private void ChamChat_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                Properties.Settings.Default.Client = cboxClient.SelectedIndex;
                Properties.Settings.Default.ProfileFilename = _ProfileFileName;
                Properties.Settings.Default.Save();
            }
            catch { }
        }

        private void btnShowProfileReturn_Click(object sender, EventArgs e)
        {
            tabControl.SelectedTab = tabProfile;
            menu.Enabled = true;
        }

        private void btnGraphNext_Click(object sender, EventArgs e)
        {
            int newX = pboxGraph.Location.X - (int)Math.Round((double)pnlGraph.Width / 2.0);
            if (-newX > pboxGraph.Width - pnlGraph.Width)
                newX = -(pboxGraph.Width - pnlGraph.Width);
            pboxGraph.Location = new Point(newX, 0);
        }


        private void btnGraphPrevious_Click(object sender, EventArgs e)
        {
            int x = pboxGraph.Location.X +(int)Math.Round((double)pnlGraph.Width / 2.0);

            if (x > 0)
                x = 0;
            
            pboxGraph.Location = new Point(x, 0);
        }

        private void btnZoomIn_Click(object sender, EventArgs e)
        {
            _Graph.HrsInView = Math.Max(1, _Graph.HrsInView - 1);
            ReDrawGraph();
            
            ScrBarGraph_Visibility();
        }

        private void btnZoomOut_Click(object sender, EventArgs e)
        {
            int w = _Graph.Width;
            _Graph.HrsInView += 1;
            
            ReDrawGraph();
            
            // Check if width has changed. If not, undo change, but do not redraw pbox.
            if(_Graph.Width == w)
                _Graph.HrsInView -= 1;

            ScrBarGraph_Visibility();
        }



        private void InitGraph(Profile profile, Client client)
        {
            this.Cursor = Cursors.WaitCursor;
            _Graph = new Graph(profile, pnlGraph.Size, client);
            pboxGraph.Location = new Point(0, 0);
            ReDrawGraph();
            ShowStepFromGraph(null);
            ScrBarGraph_Visibility();
            this.Cursor = Cursors.Default;
        }

        private void ReDrawGraph()
        {
            this.Cursor = Cursors.WaitCursor;
            pboxGraph.Image = _Graph.GetImage();
            pboxGraph.Size = new Size(_Graph.Width, _Graph.Height);

            if (_Graph.Width == _Graph.MaxSizeOfImage)
                btnZoomIn.Enabled = false;
            else
                btnZoomIn.Enabled = true;

            this.Cursor = Cursors.Default;
        }

        private void btnZoomReset_Click(object sender, EventArgs e)
        {
            Profile p = _Graph.Profile;
            Client c = _Graph.Client;
            InitGraph(p, c);
        }

      

        private void cboxGraphSerialized_CheckedChanged(object sender, EventArgs e)
        {
            if (cboxGraphSerialized.Checked)
            {
                Profile p = _Graph.Profile;
                Client c = _Graph.Client;
                Profile s = new Profile(p.Title, p.SerializedStepList, new List<Loop>(), p.Options);
                InitGraph(s, c);
            }
            else
                UpdateGUI();
        }


        private void pboxGraph_MouseMove(object sender, MouseEventArgs e)
        {
            if (_Graph == null)
                return;

            int i = _Graph.StepAtMousePosition(pboxGraph.PointToClient(Cursor.Position).X);
            if (i < 0)
            {
                ShowStepFromGraph(null);
                return;
            }

            Step step = _Graph.Profile.Steps[i];
            ShowStepFromGraph(step);
        }
        private void pboxGraph_MouseLeave(object sender, EventArgs e)
        {
            ShowStepFromGraph(null);
        }




        private void ShowStepFromGraph(Step s)
        {
            if (s == null)
            {
                for(int i=0;i<dgvSteps.RowCount;i++)
                    if(dgvSteps.Rows[i].DefaultCellStyle.BackColor != _StepNotSelectedColor)
                        dgvSteps.Rows[i].DefaultCellStyle.BackColor = _StepNotSelectedColor;
                return;
            }


            // Set row color of dgvSteps
            for (int i = 0; i < dgvSteps.RowCount; i++)
            {
                if (s.PK == i)
                {
                    if (cboxGraphSerialized.Checked)
                    {
                        if (dgvSteps.Rows[i].DefaultCellStyle.BackColor != _StepNotSelectedColor)
                            dgvSteps.Rows[i].DefaultCellStyle.BackColor = _StepNotSelectedColor;
                    }
                    else
                    {
                        if (dgvSteps.Rows[i].DefaultCellStyle.BackColor != _StepSelectionColor)
                            dgvSteps.Rows[i].DefaultCellStyle.BackColor = _StepSelectionColor;
                    }
                }
                else
                {
                    if (dgvSteps.Rows[i].DefaultCellStyle.BackColor != _StepNotSelectedColor)
                        dgvSteps.Rows[i].DefaultCellStyle.BackColor = _StepNotSelectedColor;

                }
            }
        }






        private void ScrBarGraph_Scroll(object sender, ScrollEventArgs e)
        {
            int x = ScrollBarValueToOffset(ScrBarGraph.Value);
            pboxGraph.Location = new Point(x, 0);
        }





        private void ScrBarGraph_Visibility()
        {
            if (pboxGraph.Width <= pnlGraph.Width)
            {
                ScrBarGraph.Visible = false;
                return;
            }
            else
                ScrBarGraph.Visible = true;
        }


        private Int32 ScrollBarValueToOffset(Int32 sb)
        {
            float r = ((float)pboxGraph.Width - pnlGraph.Width) / (ScrBarGraph.Maximum - 9);
            return -1 * (int)Math.Round(r) * sb;
        }

        private void dgvSteps_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 3 || e.ColumnIndex == 4)
            {
                dgvSteps.EndEdit();
                UpdateGUI();
            }
        }

        private void saveImageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            sfdSave.Title = "Save profile image";
            sfdSave.FileName = txtbTitle.Text + ".bmp";
            sfdSave.Filter = "BMP|*.bmp";
            DialogResult dr = sfdSave.ShowDialog();
            if (dr != DialogResult.OK)
                return;

            try
            {
                pboxGraph.Image.Save(sfdSave.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
            }
            catch (Exception ex)
            {
                String msg = String.Format("Could not save profile image!\n\n{0}.", ex.Message);
                MessageBox.Show(msg, "Save profile image", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        private void cboxInterfaces_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cboxInterfaces.SelectedIndex < 0)
                return;

            Interface intface = _Interfaces[cboxInterfaces.SelectedIndex];
            lboxClientOnIF.Items.Clear();
            foreach (Client client in _Clients.FindAll(delegate(Client c) { return (c.InterfaceID == intface.ID); }))
                lboxClientOnIF.Items.Add(client.ToString());


            gbClientOnIF.Text = "Clients linked to "+intface.ID.ToString();
            gbCommandIF.Text = "Commands sent to " + intface.ToString();
            lboxIF.Items.Clear();
        }


        


        private void cbANSIX328_CheckedChanged(object sender, EventArgs e)
        {
            if (cbANSIX328.Checked)
            {
                cbLF.Enabled = false;
                cbCR.Enabled = false;
            }
            else
            {
                cbLF.Enabled = true;
                cbCR.Enabled = true;
            }

        }

        private void txtbReturnDataWaitms_TextChanged(object sender, EventArgs e)
        {
            int i;
            if( !Int32.TryParse(txtbReturnDataWaitms.Text, out i))
                txtbReturnDataWaitms.Text = "1000";
        }





        #region Import profile from client

        private void MenuProfileImport_Click(object sender, EventArgs e)
        {
            // Menu option is only enabled when profile tab is selected.

            DialogResult dr = MessageBox.Show("Changes to the current profile will be lost!\n\nAre you sure you want to continue?", "Import profile", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
            if (dr != DialogResult.Yes)
                return;


            Client client = _Clients[cboxClient.SelectedIndex];

            if (!PromptProgramNo.ShowDialog(client.AvailableRAMs.Min(), client.AvailableRAMs.Max()))
                return;

            int program = PromptProgramNo.Value;

            if (!client.AvailableRAMs.Contains(program))
            {
                string msg = string.Format("Program {0:0} is not available on client {1:00000}", program, client.MIDS);
                MessageBox.Show(msg, "Import program", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }


            pnlImExport.Dock = DockStyle.Fill;
            menu.Enabled = false;
            pnlImExport.Visible = true;
            tabControl.Visible = false;
            lblImExport.Text = String.Format("Importing profile #{0:00} from {1} {2:00000}", program, client.TypeDescription, client.MIDS);
            bgworkerImport.RunWorkerAsync(new object[] { client, program });
        }

        private void bgworkerImport_DoWork(object sender, DoWorkEventArgs e)
        {
            object[] a = e.Argument as object[];
            Client client = (Client)a[0];
            int program = (int)a[1];
            Profile profile = client.ReadProfile(program, ref bgworkerImport);

            if (profile.Title == "Cancelled")
                e.Cancel = true;
            else
                e.Result = profile;
        }

        private void bgworkerImport_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progBarImport.Value = e.ProgressPercentage;
            try
            {
                Client client = _Clients[cboxClient.SelectedIndex];
                DataTransferLog l = client.DataTransferLogs[client.DataTransferLogs.Count - 1];
                lblImportAction.Text = String.Format("Tx: {0}, Rx {1}", l.Command, l.ReturnData);
                lblImportAction.Visible = true;
            }
            catch { lblImportAction.Visible = false; }
        }

        private void bgworkerImport_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            if ((e.Cancelled == true))
            {
                this.Text = FormHeaderText;
                NewProfile(8);
            }

            else if (!(e.Error == null))
            {
                //this.tbProgress.Text = ("Error: " + e.Error.Message);
            }

            else
            {
                Profile profile = (Profile)e.Result;
                ProfileToGUI(profile);
                this.Text = FormHeaderText + " [" + profile.Title + "]";
            }

            // Return GUI to usable state always
            tabControl.Visible = true;
            pnlImExport.Visible = false;
            menu.Enabled = true;
        }

        private void btnCancelImport_Click(object sender, EventArgs e)
        {
            // Cancel import or export if running
            if (bgworkerImport.IsBusy)
            {
                if (bgworkerImport.WorkerSupportsCancellation == true)
                {
                    bgworkerImport.CancelAsync();
                }
            }

            if (bgworkerExport.IsBusy)
            {
                if (bgworkerExport.WorkerSupportsCancellation == true)
                {
                    bgworkerExport.CancelAsync();
                }
            }
        }

        #endregion Import profile from client


        #region Export profile
        private void MenuProfileStore_Click(object sender, EventArgs e)
        {
            // Menu option is only enabled when profile tab is selected.

            Client client = _Clients[cboxClient.SelectedIndex];

            string wng = string.Format("Storing the profile overwrites the existing profile in {0:00000}.\n\nAre you sure you want to overwrite?", client.MIDS);
            DialogResult dr = MessageBox.Show(wng, "Store profile", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
            if (dr != DialogResult.Yes)
                return;

            if (!PromptProgramNo.ShowDialog(client.AvailableRAMs.Min(), client.AvailableRAMs.Max()))
                return;

            int program = PromptProgramNo.Value;

            if (!client.AvailableRAMs.Contains(program))
            {
                string msg = string.Format("Program {0:0} is not available on client {1:00000}", program, client.MIDS);
                MessageBox.Show(msg, "Store profile", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }

            Profile profile = new Profile();


#warning HELE UPLOAD MIKMAK MET BGW


            pnlImExport.Dock = DockStyle.Fill;
            menu.Enabled = false;
            pnlImExport.Visible = true;
            tabControl.Visible = false;
            lblImExport.Text = String.Format("Exporting profile #{0:00} to {1} {2:00000}", program, client.TypeDescription, client.MIDS);
            bgworkerExport.RunWorkerAsync(new object[] { client, program, profile});
        }

        private void bgworkerExport_DoWork(object sender, DoWorkEventArgs e)
        {
#warning Perform the writeprofile
            
            object[] a = e.Argument as object[];
            Client client = (Client)a[0];
            int program = (int)a[1];
            Profile profile = (Profile)a[2];
            
            bool success = client.WriteProfile(profile, program, ref  bgworkerExport);


            DETECT WHEN CANCELLED in client.WriteProfile
            if (profile.Title == "Cancelled")
                e.Cancel = true;
            else
                e.Result = profile;
            
        }

        private void bgworkerExport_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progBarImport.Value = e.ProgressPercentage;
            try
            {
                Client client = _Clients[cboxClient.SelectedIndex];
                DataTransferLog l = client.DataTransferLogs[client.DataTransferLogs.Count - 1];
                lblImportAction.Text = String.Format("Tx: {0}, Rx {1}", l.Command, l.ReturnData);
                lblImportAction.Visible = true;
            }
            catch { lblImportAction.Visible = false; }
        }

        private void bgworkerExport_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
#warning Finish up
            /*
            if ((e.Cancelled == true))
            {
                this.Text = FormHeaderText;
                NewProfile(8);
            }

            else if (!(e.Error == null))
            {
                //this.tbProgress.Text = ("Error: " + e.Error.Message);
            }

            else
            {
                Profile profile = (Profile)e.Result;
                ProfileToGUI(profile);
                this.Text = FormHeaderText + " [" + profile.Title + "]";
            }

            // Return GUI to usable state always
            tabControl.Visible = true;
            pnlImport.Visible = false;
            menu.Enabled = true;
           */
        }

        #endregion Export profile
    }
}
