/*
+-------------------------------- DISCLAIMER ---------------------------------+
|                                                                             |
| This application program is provided to you free of charge as an example.   |
| Despite the considerable efforts of Euresys personnel to create a usable    |
| example, you should not assume that this program is error-free or suitable  |
| for any purpose whatsoever.                                                 |
|                                                                             |
| EURESYS does not give any representation, warranty or undertaking that this |
| program is free of any defect or error or suitable for any purpose. EURESYS |
| shall not be liable, in contract, in torts or otherwise, for any damages,   |
| loss, costs, expenses or other claims for compensation, including those     |
| asserted by third parties, arising out of or in connection with the use of  |
| this program.                                                               |
|                                                                             |
+-----------------------------------------------------------------------------+
*/

// GrablinkSerialCommunicationDlg.cpp : implementation file
//

#include "stdafx.h"
#include "clserialLibrary.h"
#include "GrablinkSerialCommunication.h"
#include "GrablinkSerialCommunicationDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// Dialog Data
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CGrablinkSerialCommunicationDlg dialog



CGrablinkSerialCommunicationDlg::CGrablinkSerialCommunicationDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_GRABLINKSERIALCOMMUNICATION_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CGrablinkSerialCommunicationDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_COMBO_PORTS, availablePorts);
    DDX_Control(pDX, IDC_COMBO_BAUDRATES, baudRates);
    DDX_Control(pDX, IDC_OPENBUTTON, openButton);
    DDX_Control(pDX, IDC_CLOSEBUTTON, closeButton);
    DDX_Control(pDX, IDC_SENDBUTTON, sendButton);
    DDX_Control(pDX, IDC_READBUTTON, readButton);
    DDX_Control(pDX, IDC_SENDCOMMAND, sendCommand);
    DDX_Control(pDX, IDC_READMESSAGE, readMessage);
}

BEGIN_MESSAGE_MAP(CGrablinkSerialCommunicationDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_CBN_SELCHANGE(IDC_COMBO_PORTS, &CGrablinkSerialCommunicationDlg::OnCbnSelchangeComboPorts)
    ON_CBN_SELCHANGE(IDC_COMBO_BAUDRATES, &CGrablinkSerialCommunicationDlg::OnCbnSelchangeBaudrate)
    ON_BN_CLICKED(IDC_OPENBUTTON, &CGrablinkSerialCommunicationDlg::OnBnClickedOpenbutton)
    ON_BN_CLICKED(IDC_CLOSEBUTTON, &CGrablinkSerialCommunicationDlg::OnBnClickedClosebutton)
    ON_BN_CLICKED(IDC_SENDBUTTON, &CGrablinkSerialCommunicationDlg::OnBnClickedSendbutton)
    ON_BN_CLICKED(IDC_READBUTTON, &CGrablinkSerialCommunicationDlg::OnBnClickedReadbutton)
END_MESSAGE_MAP()


// CGrablinkSerialCommunicationDlg message handlers

BOOL CGrablinkSerialCommunicationDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    availablePorts.EnableWindow(FALSE);
    baudRates.EnableWindow(FALSE);
    sendCommand.EnableWindow(FALSE);
    readMessage.SetWindowTextW(L"Select a serial port");
    closeButton.EnableWindow(FALSE);
    openButton.EnableWindow(FALSE);
    sendButton.EnableWindow(FALSE);
    readButton.EnableWindow(FALSE);

    // Retrieve the number of serial ports
    UINT32 numPorts = 0;
    try
    {
        cl.GetNumSerialPorts(&numPorts);
    }
    catch (Euresys::clseregl::ClSerialError &)
    {
        return TRUE;
    }

    // Retrieve the identifier of each port           
    for (UINT32 i = 0; i < numPorts; i++)
    {
        UINT32 bufferSize = 0;
        try
        {
            // Retrieve the buffer size
            cl.GetSerialPortIdentifier(i, nullptr, &bufferSize);
        }
        catch (Euresys::clseregl::ClSerialError &error)
        {
            if (error.getStatus() != CL_ERR_BUFFER_TOO_SMALL)
            {
                std::string message(error.what());
                MessageBox(std::wstring(message.begin(), message.end()).c_str(), L"GrablinkSerialCommunication error", MB_OK|MB_ICONERROR);
                continue;
            }
        }
        char *textPort = new char[bufferSize];
        try
        {
            // Retrieve the port identifier
            cl.GetSerialPortIdentifier(i, textPort, &bufferSize);
            CString portIdentifier((char *)textPort);
            availablePorts.AddString(portIdentifier);
        }
        catch (Euresys::clseregl::ClSerialError &error)
        {
            std::string message(error.what());
            MessageBox(std::wstring(message.begin(), message.end()).c_str(), L"GrablinkSerialCommunication error", MB_OK|MB_ICONERROR);
        }
        delete[] textPort;
    }

    if (numPorts > 0)
    {
        availablePorts.EnableWindow(TRUE);
    }

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CGrablinkSerialCommunicationDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CGrablinkSerialCommunicationDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CGrablinkSerialCommunicationDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}


void CGrablinkSerialCommunicationDlg::OnCbnSelchangeComboPorts()
{
    serialIndex = (UINT32)availablePorts.GetCurSel();

    // Update the GUI
    openButton.EnableWindow(TRUE);
}


void CGrablinkSerialCommunicationDlg::OnBnClickedOpenbutton()
{
    try
    {
        // Open the serial port
        cl.SerialInit(serialIndex);

        // Retrieve supported baud rates
        UINT32 supportedBaudRates = 0;
        cl.GetSupportedBaudRates(&supportedBaudRates);

        // Update the GUI
        previousSelectedBaudRate = -1;
        readMessage.SetWindowTextW(L"Select a baud rate");
        sendCommand.SetWindowTextW(L"");
        closeButton.EnableWindow(TRUE);
        openButton.EnableWindow(FALSE);
        sendButton.EnableWindow(TRUE);
        readButton.EnableWindow(TRUE);
        sendCommand.EnableWindow(TRUE);
        availablePorts.EnableWindow(FALSE);
        for (int i = 0; i < Euresys::clseregl::CL_BAUDRATES_SIZE; i++)
        {
            std::pair<UINT32, CString> clBaudRate = Euresys::clseregl::CL_BAUDRATES[i];
            if (supportedBaudRates & clBaudRate.first)
            {
                baudRates.AddString(clBaudRate.second);
                baudRates.SetItemData(i, clBaudRate.first);
            }
        }
        if (supportedBaudRates > 0)
        {
            baudRates.EnableWindow(TRUE);
        }
    }
    catch (Euresys::clseregl::ClSerialError &error)
    {
        CString message(error.what());
        readMessage.SetWindowTextW(message);
    }
}


void CGrablinkSerialCommunicationDlg::OnBnClickedClosebutton()
{
    try
    {
        // Open the serial port
        cl.SerialClose();

        // Update the GUI
        readMessage.SetWindowTextW(L"");
        sendCommand.SetWindowTextW(L"");
        closeButton.EnableWindow(FALSE);
        openButton.EnableWindow(TRUE);
        sendButton.EnableWindow(FALSE);
        readButton.EnableWindow(FALSE);
        sendCommand.EnableWindow(FALSE);
        baudRates.ResetContent();
        baudRates.EnableWindow(FALSE);
        availablePorts.EnableWindow(TRUE);
    }
    catch (Euresys::clseregl::ClSerialError &error)
    {
        CString message(error.what());
        readMessage.SetWindowTextW(message);
    }
}


void CGrablinkSerialCommunicationDlg::OnBnClickedSendbutton()
{
    try
    {
        // Write the command to the port
        UINT32 numBytes = (UINT32)sendCommand.GetWindowTextLengthW() + 1; // 1 additional carriage return
        CString text;
        sendCommand.GetWindowTextW(text);
        CT2A buffer(text);
        buffer[numBytes - 1] = '\x0D'; // append carriage return (needed by some cameras)
        cl.SerialWrite(buffer, &numBytes, 5000);

        // Empty the text boxes
        sendCommand.SetWindowTextW(L"");
        readMessage.SetWindowTextW(L"");
    }
    catch (Euresys::clseregl::ClSerialError &error)
    {
        CString message(error.what());
        readMessage.SetWindowTextW(message);
    }
}


void CGrablinkSerialCommunicationDlg::OnBnClickedReadbutton()
{
    try
    {
        // Retrieve the number of bytes in the read buffer
        UINT32 numBytes = 0;
        cl.GetNumBytesAvail(&numBytes);

        if (numBytes == 0)
        {
            readMessage.SetWindowTextW(L"<NO DATA>");
        }
        else
        {
            // Retrieve the data in the read buffer
            char *receivedData = new char[numBytes];
            try
            {
                cl.SerialRead(receivedData, &numBytes, 5000);
                CString data(receivedData, (int)numBytes);
                readMessage.SetWindowTextW(data);
            }
            catch (Euresys::clseregl::ClSerialError &error)
            {
                CString message(error.what());
                readMessage.SetWindowTextW(message);
            }
            delete[] receivedData;
        }
    }
    catch (Euresys::clseregl::ClSerialError &error)
    {
        CString message(error.what());
        readMessage.SetWindowTextW(message);
    }
}


void CGrablinkSerialCommunicationDlg::OnCbnSelchangeBaudrate()
{
    try
    {
        // Set new baud rate
        UINT32 baudRate = (UINT32)baudRates.GetItemData(baudRates.GetCurSel());
        cl.SetBaudRate(baudRate);

        // Update the GUI
        readMessage.SetWindowTextW(L"");
        previousSelectedBaudRate = baudRates.GetCurSel();
    }
    catch (Euresys::clseregl::ClSerialError &error)
    {
        baudRates.SetCurSel(previousSelectedBaudRate);
        CString message(error.what());
        readMessage.SetWindowTextW(message);
    }
}

