// ==========================================================================
// Author:  Yee Hsu
// Date:    6/11/2010
//
// Desc:    Serial Port Connector. Connects to Serial Port COM#
//          Allows communication between serial port and device.
// ==========================================================================

// SerialPort.cpp: implementation of the CSerialPort class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SerialPort.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSerialPort::CSerialPort()
{
    m_portHandle=INVALID_HANDLE_VALUE;
}

CSerialPort::~CSerialPort()
{
    if(IsOpen())
        ClosePort();
}

void CSerialPort::monitor_data()
{
    DWORD thread_id;
    monitor_data_thread_handle = CreateThread(0, 0, CSerialPort::monitor_data_thread, this, 0, &thread_id);
}

DWORD WINAPI CSerialPort::monitor_data_thread(LPVOID arg)
{
    CSerialPort    *pSerialPort;
    pSerialPort = (CSerialPort *) arg;
    pSerialPort->monitor_data_thread_terminate = false;
    bool done;
    BOOL success;
    DWORD bytes;
    byte buf[TMP_BUFFER_1024];

    while (!pSerialPort->monitor_data_thread_terminate)
    {
        memset(buf, 0, sizeof(buf));
        success = ReadFile (pSerialPort->m_portHandle, buf, sizeof(buf), &bytes,NULL);
        done = success != 0;

        if (!done)
        {
            DWORD rxgle = GetLastError ();
            ASSERT (rxgle == ERROR_IO_PENDING);
        }
        else
        {
            if (pSerialPort->CallBackFunc_Data_Func && !pSerialPort->monitor_data_thread_terminate)
            {
                pSerialPort->CallBackFunc_Data_Func(buf, bytes, pSerialPort->data_func_context);
            }
        }
        Sleep(1000);
    }
    return 0;
}

long CSerialPort::WriteData(const pbyte data, const int dwLength)
{
    bool done;
    BOOL success;
    DWORD bytes;

    if (dwLength == 0) /* Be pessimistic and assume WriteFile would choke on zero length */
    {
        return 0;
    }
    else
    {
        success = WriteFile(m_portHandle, data, dwLength, &bytes,NULL);
        done = success != 0;

        if (!done)
        {
            DWORD txgle = GetLastError ();
            ASSERT (txgle == ERROR_IO_PENDING);
            return 0;
        }
    }
    return bytes;
}

BOOL CSerialPort::OpenPort(const int nPort, const int nBaudRate)
{
    DCB dcb;
    BOOL success;
    COMMTIMEOUTS timeouts;
    // TODO: Add your dispatch handler code here
    CString portName;
    portName.Format(_T("COM%d:"), nPort);

    m_portHandle = CreateFile (portName,
        GENERIC_READ | GENERIC_WRITE,
        WIN32_NO_SHARING, // mandatory
        0,
        OPEN_EXISTING, // mandatory
        0,//FILE_FLAG_OVERLAPPED,
        WIN32_NO_TEMPLATE_FILE // mandatory
        );

    if (m_portHandle == INVALID_HANDLE_VALUE)
    {
        //CString msg;
        //msg.Format(_T("Error %d"),GetLastError());
        //MessageBox(msg);
        return FALSE;
    }

    memset (&dcb, 0, sizeof (DCB));
    dcb.DCBlength = sizeof (DCB);
    //success = BuildCommDCB (cfgStr, &dcb); /* Note BuildCommDCB() turns off both hardware and software flow control */
    dcb.BaudRate=nBaudRate;
    dcb.fParity = FALSE;
    dcb.fNull = FALSE;
    dcb.StopBits = ONESTOPBIT;
    dcb.Parity = NOPARITY;
    dcb.ByteSize = 8;

    success = SetCommState (m_portHandle, &dcb);
    ASSERT ((_T("SetCommState"), success));

    if (!success)
    {
        CloseHandle (m_portHandle);
        m_portHandle=INVALID_HANDLE_VALUE;
        //MessageBox(_T("SetCommState Error"));
        return FALSE;
    }

    /*
    timeouts.ReadIntervalTimeout = WIN32_NO_TIMEOUT;
    timeouts.ReadTotalTimeoutMultiplier = WIN32_NO_TIMEOUT;
    timeouts.ReadTotalTimeoutConstant = WIN32_NO_TIMEOUT;
    timeouts.WriteTotalTimeoutMultiplier = WIN32_NO_TIMEOUT;
    timeouts.WriteTotalTimeoutConstant = WIN32_NO_TIMEOUT;
    */

    timeouts.ReadIntervalTimeout = 3;
    timeouts.ReadTotalTimeoutMultiplier = 3;
    timeouts.ReadTotalTimeoutConstant = 2;
    timeouts.WriteTotalTimeoutMultiplier = 3;
    timeouts.WriteTotalTimeoutConstant = 2;

    success = SetCommTimeouts (m_portHandle, &timeouts);
    ASSERT ((_T("SetCommTimeouts"), success));

    if (!success)
    {
        CloseHandle (m_portHandle);
        m_portHandle=INVALID_HANDLE_VALUE;
        //MessageBox(_T("SetCommTimeouts Error"));
        return FALSE;
    }
    monitor_data();

    //SystemParametersInfo(SPI_GETBATTERYIDLETIMEOUT,0,&m_batteryPowerTimeout,SPIF_UPDATEINIFILE);
    //SystemParametersInfo(SPI_GETEXTERNALIDLETIMEOUT,0,&m_acPowerTimeout,SPIF_UPDATEINIFILE);
    //SystemParametersInfo(SPI_SETBATTERYIDLETIMEOUT,0,NULL,SPIF_UPDATEINIFILE);
    //SystemParametersInfo(SPI_SETEXTERNALIDLETIMEOUT,0,NULL,SPIF_UPDATEINIFILE);

    return TRUE;
}

void CSerialPort::Set_Data_Callback(void (*CallBackFunctionName)(void *,unsigned long,void *), void *context)
{
    if (CallBackFunctionName)
    {
        CallBackFunc_Data_Func=CallBackFunctionName;
        data_func_context=context;
    }
}

BOOL CSerialPort::ClosePort()
{
    if (monitor_data_thread_handle != INVALID_HANDLE_VALUE)        
    {
        monitor_data_thread_terminate = true;
    }
    Sleep(0);
    if (m_portHandle != INVALID_HANDLE_VALUE)
    {
        CloseHandle (m_portHandle);
    }
    m_portHandle = INVALID_HANDLE_VALUE;

    //SystemParametersInfo(SPI_SETBATTERYIDLETIMEOUT,m_batteryPowerTimeout,NULL,SPIF_UPDATEINIFILE);
    //SystemParametersInfo(SPI_SETEXTERNALIDLETIMEOUT,m_acPowerTimeout,NULL,SPIF_UPDATEINIFILE);

    return TRUE;
}

BOOL CSerialPort::IsOpen()
{
    return m_portHandle!=INVALID_HANDLE_VALUE;
}

BOOL CSerialPort::SetDTR(const BOOL bEnable)
{
    DCB dcb;
    if(m_portHandle==INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    if(GetCommState(m_portHandle,&dcb)==FALSE)
        return FALSE;
    if(bEnable)
    {
    //  dcb.fDtrControl=DTR_CONTROL_ENABLE;
        dcb.fRtsControl=RTS_CONTROL_ENABLE;
    }
    else
    {
    //  dcb.fDtrControl=DTR_CONTROL_DISABLE;
        dcb.fRtsControl=RTS_CONTROL_DISABLE;
    }
    if(SetCommState(m_portHandle,&dcb)==FALSE)
        return FALSE;
    return TRUE;
}