using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace MoveTest
{
    public partial class Form1 : Form
    {
        FileStream fS;          //Stream for correct file access
        StreamReader sR;        //Stream for nice reading
        Int64 seekPosition;     //Store current read position, probably not needed?

        double xPosNum = 0; double yPosNum = 0;     //Creates and instantiates
        double zPosNum = 0; double xVelNum = 0;     //variables to store axis
        double yVelNum = 0; double zVelNum = 0;     //position and velocity

        bool active = false;    //Used to determine if communications to controller open

        Bitmap pngFile;
        Graphics drawToResultBox;
        Brush blackBrush, whiteBrush;
        Pen crossHairPen;
        Image results;

        SerialCommunications serialCom;
        delegate void SetTextCallback(string text);

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>Is called when the button labelled Initialise is called. Initialises dll/controller, sets up streams to read in log information.</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void initialise_Click(object sender, EventArgs e)
        {
            uint initResult = MoveClass.initialise();   //Call the dll function initialise and store the result

            if (initResult == 0)                        //No error
            {
                //MessageBox.Show("Initalised", Convert.ToString(initResult));
                
                fS = new FileStream("log.txt", 
                    FileMode.Open, FileAccess.Read, 
                    FileShare.ReadWrite);               //Open filestream with correct access priorities
                sR = new StreamReader(fS);              //Open stream to read data

                updateFromLog.Enabled = true;           //Start timer to read from log file
                active = true;                          //It's running now
            }
            else                                        //Error!
            {
               MessageBox.Show("Failed", 
                   Convert.ToString(initResult));       //Display error
            }
        
        }

        /// <summary>Is called when button labelled Disable drives is clicked. Disables the drives and changes status label.</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void disable_Click(object sender, EventArgs e)
        {
            uint disableResult = MoveClass.disableAxes();   //Call dll function to disable the axes and store result

            if (disableResult == 0)                         //No error
            {
                //MessageBox.Show(Convert.ToString(disableResult));
                driveStatus.Text = "Drives disabled";       //Display this in text box
            }
            else                                            //Error
            {
                MessageBox.Show("Disable failed", 
                    Convert.ToString(disableResult));       //Display message and error code
            }
        }

        /// <summary>Is called when button labelled Move axis is clicked. Moves an axis...</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void moveAxis_Click(object sender, EventArgs e)
        {
            uint moveResult;            //Initialise variable to store result of move operation

            int distance = Convert.ToInt32(distanceBox.Text);   //Convert text from boxes to
            uint speed = Convert.ToUInt32(feedrateBox.Text);    //numbers and store in variables

            if (xButton.Checked) moveResult = MoveClass.incX(distance, speed);  //Check which axis button is selected
            if (yButton.Checked) moveResult = MoveClass.incY(distance, speed);  //and call appropriate dll function
            if (zButton.Checked) moveResult = MoveClass.incZ(distance, speed);  //with the above variabales as parameters

            //MessageBox.Show(Convert.ToString(moveResult));
        }

        //Sort this tat out
        private void moveAbsoluteButton_Click(object sender, EventArgs e)
        {
            //int x = Convert.ToInt32(absX.Text);
            //int y = Convert.ToInt32(absY.Text);
            //int z = Convert.ToInt32(absZ.Text);
            int[] distanceArray = new int[] { Convert.ToInt32(absX.Text), 
                Convert.ToInt32(absY.Text), Convert.ToInt32(absZ.Text) };

            //int res = Convert.ToInt32(textBox3.Text);
            uint feedrate = Convert.ToUInt32(feedrateBox.Text);
            uint[] speedArray = new uint[] { feedrate, feedrate, feedrate };

            //int errReturn = MoveClass.moveDistance(x, y, feedrate);
            uint errReturn = MoveClass.moveAbsolute(ref distanceArray,
                ref speedArray);

            MessageBox.Show(Convert.ToString(errReturn));
        }

        /// <summary>Called when button labelled Enable Drives is clicked. Enables the drives and changes status label.</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void enableDrives_Click(object sender, EventArgs e)
        {
            uint initResult = MoveClass.enableAxes();   //Calls dll function to enable the drives and stores returned error result

            if (initResult == 0)                        //No error
            { 
                driveStatus.Text = "Drives enabled";    //Change status label
            }
            else                                        //Error
            { MessageBox.Show("Drive enable Failed", 
                Convert.ToString(initResult)); }        //Display it
        }

        /// <summary>Called when form closes, used to clean up file and controller resources</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (active)         //When controller has not been explicitly shutdown
            {
                MoveClass.disableAxes();    //Disable and release axes, might be done by shutdown?
                MoveClass.shutdown();       //Shuts down communications
            }

            if (fS != null)     //Only when initialise has succeeded and log file opened
            {
                fS.Close();     //Close both streams
                sR.Close();
            }

            if (drawToResultBox != null)
            {
                drawToResultBox.Dispose();
                blackBrush.Dispose();
                whiteBrush.Dispose();
            }

            if (serialCom != null)
            {
                serialCom.Dispose();
            }
        }

        /// <summary>Called 10 times per second to update text box with log from dll</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void updateFromLog_Tick(object sender, EventArgs e)
        {
            sR.BaseStream.Seek(seekPosition, SeekOrigin.Begin); //Sets cursor position to last read from point, unnecessary?
            logBox.Text += sR.ReadToEnd();                      //Reads all text from current point in log and appends to text box
            seekPosition = sR.BaseStream.Position;              //Sets variable to current file position, unnecessary?

            logBox.Select(logBox.Text.Length, 0);       //Positions text box cursor at the end
            logBox.ScrollToCaret();                     //and scrolls to it
        }

        /// <summary>Called when button labelled SHUTDOWN is pressed. Shuts down</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void stopButton_Click(object sender, EventArgs e)
        {
            MoveClass.shutdown();               //Calls dll function to close communication with the controller
            active = false;                     //Stopped running
        }

        /// <summary>Updates text boxes with stage's current position and velocity</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void updatePosVelTimer_Tick(object sender, EventArgs e)
        {
            /*
            MoveClass.getMAxisPV(xPosNum, yPosNum, zPosNum, xVelNum,
                yVelNum, zVelNum);      //Call dll function to fill variables
                                                //with data from stage
            */
            double[] values = new double[]{xPosNum, yPosNum, zPosNum,
                xVelNum, yVelNum,zVelNum};
            MoveClass.getMAxisPV(ref values);

            xPos.Text = "x: " + Convert.ToString(xPosNum);  //Place numbers into
            yPos.Text = "y: " + Convert.ToString(yPosNum);  //appropriate text boxes
            zVel.Text = "z: " + Convert.ToString(zPosNum);
            xVel.Text = "x: " + Convert.ToString(xVelNum);
            yVel.Text = "y: " + Convert.ToString(yVelNum);
            zVel.Text = "z: " + Convert.ToString(zVelNum);
        }

        /// <summary>Called when button labelled Update/Stop is pressed. Enables/Disables timer to update position and velocity information</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void updatePosVelButton_Click(object sender, EventArgs e)
        {
            if (updatePosVelTimer.Enabled)              //When timer is running
            {
                updatePosVelTimer.Enabled = false;      //Turn it off
                updatePosVelButton.Text = "Update";     //And reset button label
            }
            else                                        //When timer is not running
            {
                updatePosVelTimer.Enabled = true;       //Turn it on
                updatePosVelButton.Text = "Stop";       //And set label to Stop
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            foreach (string s in System.IO.Ports.SerialPort.GetPortNames())
                comPortsBox.Items.Add(s);
        }

        private void openImageButton_Click(object sender, EventArgs e)
        {
            DialogResult dialogResult = openFileDialog1.ShowDialog();

            if (dialogResult == DialogResult.OK)
            {
                firstImageBox.Load(openFileDialog1.FileName);
                pngFile = (Bitmap)firstImageBox.Image;
                results = new Bitmap(resultImageBox.Width, resultImageBox.Height);
                drawToResultBox = Graphics.FromImage(results);//resultImageBox.CreateGraphics();
                blackBrush = new SolidBrush(Color.Black);
                whiteBrush = new SolidBrush(Color.White);
                crossHairPen = new Pen(Color.Red, 1.0f);
            }
        }

        private void runPretendCycleButton_Click(object sender, EventArgs e)
        {
            drawToResultBox.Clear(Color.Gray);

            for (int i = 0; i < pngFile.Width; ++i)
            {
                //horizontalHair.Top = resultImageBox.Top + i;
                //pictureBox1.Top = resultImageBox.Top + i;
                //resultImageBox_Paint(sender, new PaintEventArgs(
                //    resultImageBox.CreateGraphics(), new Rectangle(0, i, 
                //    resultImageBox.Width, 2)));
                //drawToResultBox.DrawLine(crossHairPen, 0, i + 1, 
                //    resultImageBox.Width, i + 1);

                for (int j = 0; j < pngFile.Height; ++j)
                {
                    //verticalHair.Left = resultImageBox.Left + j;
                    //pictureBox1.Left = resultImageBox.Left + j;
                    
                    if (pngFile.GetPixel(j, i).B != 0)
                    {
                        drawToResultBox.FillRectangle(whiteBrush, new Rectangle(
                            j, i, 1, 1));

                    }

                    //drawToResultBox.DrawLine(crossHairPen, 0, j + 1,
                    //    resultImageBox.Width, j + 1);
                    //resultImageBox_Paint(sender, new PaintEventArgs(
                    //    resultImageBox.CreateGraphics(), 
                    //    new Rectangle(0, j, resultImageBox.Width, 1)));

                    //this.Text = Convert.ToString(i) + " " + Convert.ToString(j) +
                    //    " " + Convert.ToString(pngFile.GetPixel(j, i));
                    //Application.DoEvents();
                    //System.Threading.Thread.Sleep(5);
                }
                
            }

            resultImageBox_Paint(sender, new PaintEventArgs(resultImageBox.CreateGraphics(),
                new Rectangle(0, 0, resultImageBox.Width, resultImageBox.Height)));
        }

        private void resultImageBox_Paint(object sender, PaintEventArgs e)
        {
            if (results != null)
                e.Graphics.DrawImageUnscaledAndClipped(results, e.ClipRectangle);
        }

        private void openSerialPortButton_Click(object sender, EventArgs e)
        {
            if (serialCom != null)
                serialCom.Dispose();
            
            serialCom = new SerialCommunications(comPortsBox.Text);
            serialCom.serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort_DataReceived);
            //serialCom.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort_DataReceived);
        }

        void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (this.receiveSerialDataBox.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(setText);
                this.Invoke(d, new object[] { serialCom.serialPort.ReadExisting() });
            }
            else
            {
                receiveSerialDataBox.Text += serialCom.serialPort.ReadExisting() + "\n";
                receiveSerialDataBox.Select(receiveSerialDataBox.Text.Length, 0);
                receiveSerialDataBox.ScrollToCaret();
            }
        }

        void setText(string text)
        {
            receiveSerialDataBox.Text += text + "\n";
            receiveSerialDataBox.Select(receiveSerialDataBox.Text.Length, 0);
            receiveSerialDataBox.ScrollToCaret();
        }

        private void sendSerialDataButton_Click(object sender, EventArgs e)
        {
            serialCom.Send(sendDataToSerialBox.Text);
            sentSerialDataBox.Text += "\n\r" + sendDataToSerialBox.Text + "\n";
            sendDataToSerialBox.Clear();

            sentSerialDataBox.Select(sentSerialDataBox.Text.Length, 0);
            sentSerialDataBox.ScrollToCaret();
        }

        private void sendDataToSerialBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter & serialCom != null)
            {
                sendSerialDataButton_Click(sender, new EventArgs());
                e.Handled = true;
            }
        }

        
    }
}