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;

namespace WheelchairControl
{
    /// <summary>
    /// Form for testing purposes - manual motors control.
    /// </summary>
    public partial class FormManualControl : Form
    {
        #region Delegates

        /// <summary>
        /// Delegate of UART send method of parent form.
        /// </summary>
        /// <param name="data">Data array to be sent.</param>
        public delegate void UARTSend_Delegate(uint[] data); 

        #endregion

        #region Constants

        /// <summary>
        /// Stop command repeats.
        /// </summary>
        private const Byte STOP_REQUEST_REPEATS = 3;

        /// <summary>
        /// Command buffer data length.
        /// </summary>
        private const Byte RX_DATA_LENGTH = FormMain.RX_DATA_LENGTH;

        #endregion

        #region Private variables

        /// <summary>
        /// Singleton instance.
        /// </summary>
        private static FormManualControl instance = null;

        /// <summary>
        /// Singleton instance access lock.
        /// </summary>
        private static Object singeltonLock = new Object();

        /// <summary>
        /// Parent form serial port.
        /// </summary>
        private SerialPort serialPort_formMain;

        /// <summary>
        /// Parent form UART send method deldegate. 
        /// </summary>
        private UARTSend_Delegate UARTSendMethod;

        #endregion

        /// <summary>
        /// Singleton constructor.
        /// </summary>
        /// <param name="formMainCOMPort">Com port reference.</param>
        /// <param name="sendMethod">UART send method reference.</param>
        private FormManualControl(SerialPort formMainCOMPort, UARTSend_Delegate sendMethod)
        {
            serialPort_formMain = formMainCOMPort;
            UARTSendMethod = sendMethod;

            //Register handler
            FormMain.COMStatusChanged += delegate() {
                if (this.InvokeRequired)
                {
                    this.Invoke((MethodInvoker)delegate
                    {
                        FormManualControl_Activated(this, null);
                    });
                }
                else
                {
                    FormManualControl_Activated(this, null);
                }
            };

            InitializeComponent();
        }

        /// <summary>
        /// Singleton instance getter.
        /// </summary>
        /// <param name="formMainCOMPort">Com port reference.</param>
        /// <param name="sendMethod">UART send method reference.</param>
        /// <returns>Instance of form.</returns>
        public static FormManualControl GetInstance(SerialPort formMainCOMPort, UARTSend_Delegate sendMethod)
        {
            //Prevent possible thread race
            lock (singeltonLock)
            {
                if (instance == null || instance.IsDisposed)   //Create new singleton instance
                    instance = new FormManualControl(formMainCOMPort, sendMethod);
            }

            return instance;
        }

        private void hScrollBar1_ValueChanged(object sender, EventArgs e)
        {
            labelRightMotorSpeed.Text = hScrollBarRight.Value.ToString();
        }

        private void hScrollBar2_ValueChanged(object sender, EventArgs e)
        {
            labelLeftMotorSpeed.Text = hScrollBarLeft.Value.ToString();
        }

        private void buttonResetSpeeds_Click(object sender, EventArgs e)
        {
            hScrollBarRight.Value = 128;
            hScrollBarLeft.Value = 128;
            hScrollBarSpCmR.Value = 0;
            hScrollBarSpCmL.Value = 0;

            tsslInfo.Text = "Trackbars' values have been reset.";
        }

        private void buttonMomRight_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            outputBuffer[0] = 0xC0;
            outputBuffer[1] = 0x00;
            outputBuffer[2] = Convert.ToUInt16(labelRightMotorSpeed.Text);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Moment of the right wheel has been set to " + outputBuffer[2].ToString() + " impps.";
        }

        private void buttonMomLeft_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            outputBuffer[0] = 0xC1;
            outputBuffer[1] = 0x00;
            outputBuffer[2] = Convert.ToUInt16(labelLeftMotorSpeed.Text);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Moment of the left wheel has been set to " + outputBuffer[2].ToString() + " impps.";
        }

        private void buttonSpeedRight_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            int temp = Convert.ToInt32(labelSpeedIncmpsR.Text) + 500;

            outputBuffer[0] = 0xC2;
            outputBuffer[1] = (byte)(temp >> 8);
            outputBuffer[2] = (byte)(temp % 256);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Speed of the right wheel has been set to " + labelSpeedIncmpsR.Text + " cmps.";
        }

        private void buttonSpeedLeft_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            int temp = Convert.ToInt32(labelSpeedIncmpsL.Text) + 500;

            outputBuffer[0] = 0xC3;
            outputBuffer[1] = (byte)(temp >> 8);
            outputBuffer[2] = (byte)(temp % 256);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Speed of the left wheel has been set to " + labelSpeedIncmpsR.Text + " cmps.";
        }

        private void buttonMomBoth_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            outputBuffer[0] = 0xC4;
            outputBuffer[1] = Convert.ToUInt16(labelRightMotorSpeed.Text);
            outputBuffer[2] = Convert.ToUInt16(labelLeftMotorSpeed.Text);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Moment of both wheels has been set to: Left: " + outputBuffer[2].ToString() +
                            " impps, Right: " + outputBuffer[1].ToString() + " impps.";
        }

        private void buttonSpeedBoth_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            outputBuffer[0] = 0xC5;
            outputBuffer[1] = Convert.ToUInt16(labelRightMotorSpeed.Text);
            outputBuffer[2] = Convert.ToUInt16(labelLeftMotorSpeed.Text);
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Speed of both wheels has been set to: Left: " + outputBuffer[2].ToString() +
                           " impps, Right: " + outputBuffer[1].ToString() + " impps.";
        }

        private void hScrollBarSpCmR_ValueChanged(object sender, EventArgs e)
        {
            labelSpeedIncmpsR.Text = hScrollBarSpCmR.Value.ToString();
        }

        private void hScrollBarSpCmL_ValueChanged(object sender, EventArgs e)
        {
            labelSpeedIncmpsL.Text = hScrollBarSpCmL.Value.ToString();
        }

        private void buttonSpeedIncmpsBoth_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            //Send speed of right wheel
            Int16 temp = (Int16)(Convert.ToInt16(labelSpeedIncmpsR.Text) + 500);

            outputBuffer[0] = 0xC2;
            outputBuffer[1] = (uint)temp >> 8;
            outputBuffer[2] = (uint)temp % 256;
            UARTSendMethod(outputBuffer);

            //Send speed of left wheel
            temp = (Int16)(Convert.ToInt16(labelSpeedIncmpsL.Text) + 500);

            outputBuffer[0] = 0xC3;
            outputBuffer[1] = (uint)temp >> 8;
            outputBuffer[2] = (uint)temp % 256;
            UARTSendMethod(outputBuffer);

            tsslInfo.Text = "Speed of both wheels has been set to: Left: " + labelSpeedIncmpsL.Text +
                           " cmps, Right: " + labelSpeedIncmpsR.Text + " cmps.";
        }

        private void buttonSTOP_Click(object sender, EventArgs e)
        {
            uint[] outputBuffer = new uint[RX_DATA_LENGTH - 1];

            for (int i = 0; i < STOP_REQUEST_REPEATS; i++)    //Send multiple times, just for sure
            {
                outputBuffer[0] = 0xC4;   //Send stop (128 = zero moment)
                outputBuffer[1] = 128;
                outputBuffer[2] = 128;
                UARTSendMethod(outputBuffer);
            }

            tsslInfo.Text = "STOP signal has been sent.";
        }

        private void FormManualControl_Activated(object sender, EventArgs e)
        {
            //Com port status info & controls enable
            if (serialPort_formMain.IsOpen)
            {
                tssLabelComPortState.Text = "COM Port Status: OPEN";

                //Enable buttons
                foreach (Control controlItem in this.Controls)
                    controlItem.Enabled = true;
            }
            else
            {
                tssLabelComPortState.Text = "COM Port Status: CLOSED";
                tsslInfo.Text = "Port closed, controls are disabled!";

                //Disable buttons (leave status strip enabled)
                foreach (Control controlItem in this.Controls)
                    if (controlItem.GetType() != typeof(StatusStrip))
                        controlItem.Enabled = false;
            }
        }
    }
}
