using System;
using System.Runtime.InteropServices;
using System.IO.Ports;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using Euresys.clseregl;


namespace GrablinkSerialCommunication
{
    public partial class Form1 : Form
    {
        // Handle to the serial port
        IntPtr serialRef = System.IntPtr.Zero;

        // Index of the serial port
        UInt32 serialIndex = 0;

        // Number of serial ports
        UInt32 numPorts = 0;

        // Baud rates table
        Tuple<UInt32, String>[] CL_BAUDRATES =
        {
            Tuple.Create(CL.BAUDRATE_9600, "9600"),
            Tuple.Create(CL.BAUDRATE_19200, "19200"),
            Tuple.Create(CL.BAUDRATE_38400, "38400"),
            Tuple.Create(CL.BAUDRATE_57600, "57600"),
            Tuple.Create(CL.BAUDRATE_115200, "115200"),
            Tuple.Create(CL.BAUDRATE_230400, "230400"),
            Tuple.Create(CL.BAUDRATE_460800, "460800"),
            Tuple.Create(CL.BAUDRATE_921600, "921600"),
        };

        // Previous baud rate selected
        int previousSelectedBaudRate = -1;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            sendCommand.Enabled = false;
            baudRates.Enabled = false;
            readMessage.Text = "Select a serial port";
            openButton.Enabled = false;
            sendButton.Enabled = false;
            readButton.Enabled = false;
            closeButton.Enabled = false;

            // Add path to clseregl in DLL search path
            using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\cameralink"))
            {
                CL.SetDllDirectory((string)registryKey.GetValue("CLSERIALPATH"));
            }

            // Retrieve the number of serial ports
            try
            {
                CL.GetNumSerialPorts(out numPorts);
            }
            catch (Euresys.clSerialException error)
            {
                readMessage.Text = error.Message;
            }
            catch (System.Exception error)
            {
                MessageBox.Show(error.Message, "GrablinkSerialCommunication - Initialization error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Close();
                return;
            }

            // Retrieve the identifier of each port           
            String portIdentifier = "";
            for (UInt32 i = 0; i < numPorts; i++)
            {
                UInt32 bufferSize = 0;
                try
                {
                    // Retrieve the buffer size
                    CL.GetSerialPortIdentifier(i, System.IntPtr.Zero, out bufferSize);
                }
                catch (Euresys.clSerialException error)
                {
                    if (error.Status != CL.ERR_BUFFER_TOO_SMALL)
                    {
                        MessageBox.Show(error.Message, "GrablinkSerialCommunication error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                }
                IntPtr textPort = Marshal.AllocHGlobal((int)bufferSize + 1);
                try
                {
                    // Retrieve the port identifier
                    CL.GetSerialPortIdentifier(i, textPort, out bufferSize);
                    portIdentifier = Marshal.PtrToStringAnsi(textPort);
                    availablePorts.Items.Add(portIdentifier);
                }
                catch (Euresys.clSerialException error)
                {
                    MessageBox.Show(error.Message, "GrablinkSerialCommunication error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                Marshal.FreeHGlobal(textPort);
            }
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Open the serial port
                CL.SerialInit(serialIndex, out serialRef);

                // Retrieve supported baud rates
                UInt32 supportedBaudRates;
                CL.GetSupportedBaudRates(serialRef, out supportedBaudRates);

                // Update the GUI
                previousSelectedBaudRate = -1;
                readMessage.Text = "Select a baud rate";
                sendCommand.Text = "";
                closeButton.Enabled = true;
                openButton.Enabled = false;
                sendButton.Enabled = true;
                readButton.Enabled = true;
                sendCommand.Enabled = true;
                availablePorts.Enabled = false;
                foreach (var clBaudRate in CL_BAUDRATES)
                {
                    if ((supportedBaudRates & clBaudRate.Item1) != 0)
                    {
                        baudRates.Items.Add(clBaudRate.Item2);
                    }
                }
                if (supportedBaudRates > 0)
                {
                    baudRates.Enabled = true;
                }
            }
            catch (Euresys.clSerialException error)
            {
                readMessage.Text = error.Message;
            }
        }

        private void closeButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Close the serial port
                CL.SerialClose(serialRef);

                // Update the GUI
                readMessage.Text = "";
                sendCommand.Text = "";
                sendCommand.Enabled = false;
                availablePorts.Enabled = true;
                baudRates.Items.Clear();
                baudRates.Enabled = false;;
                sendButton.Enabled = false;
                readButton.Enabled = false;
                closeButton.Enabled = false;
                openButton.Enabled = true;
            }
            catch (Euresys.clSerialException error)
            {
                readMessage.Text = error.Message;
            }
        }

        private void sendButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Write the command to the port
                sendCommand.Text += Convert.ToChar(13);
                UInt32 numBytes = (UInt32)sendCommand.Text.Length;
                CL.SerialWrite(serialRef, sendCommand.Text, out numBytes, 5000);

                // Empty the text boxes
                sendCommand.Text = "";
                readMessage.Text = "";
            }
            catch (Euresys.clSerialException error)
            {
                readMessage.Text = error.Message;
                return;
            }
        }

        private void readButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Retrieve the number of bytes in the read buffer
                UInt32 numBytes;
                CL.GetNumBytesAvail(serialRef, out numBytes);

                if (numBytes == 0)
                {
                    readMessage.Text = "<NO DATA>";
                }
                else
                {
                    // Retrieve the data in the read buffer
                    IntPtr receivedData = Marshal.AllocHGlobal((int)numBytes + 1);
                    CL.SerialRead(serialRef, receivedData, out numBytes, 5000);
                    String data = Marshal.PtrToStringAnsi(receivedData, (int)numBytes - 1);
                    Marshal.FreeHGlobal(receivedData);
                    readMessage.Text = data;
                }
            }
            catch (Euresys.clSerialException error)
            {
                readMessage.Text = error.Message;
            }
        }

        private void comboBoxSelectionChanged(object sender, EventArgs e)
        {
            serialIndex = (UInt32)availablePorts.SelectedIndex;

            // Update the GUI
            openButton.Enabled = true;
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (baudRates.SelectedItem == null)
            {
                baudRates.SelectedIndex = previousSelectedBaudRate;
                return;
            }
            try
            {
                // Set new baud rate
                UInt32 baudRate = 0;
                String selectedBaudRate = baudRates.Text;
                foreach (var clBaudRate in CL_BAUDRATES)
                {
                    if (selectedBaudRate == clBaudRate.Item2)
                    {
                        baudRate = clBaudRate.Item1;
                    }
                }
                CL.SetBaudRate(serialRef, baudRate);

                // Update the GUI
                readMessage.Text = "";
                previousSelectedBaudRate = baudRates.SelectedIndex;
            }
            catch (Euresys.clSerialException error)
            {
                baudRates.SelectedIndex = previousSelectedBaudRate;
                readMessage.Text = error.Message;
            }
        }
    }
}
