//-------------------------------
// File:			SerialPort.cpp
// Function:		Serial port media driver
// Supported Toolchain(s):
//		- Microsoft Visual C++
// Date created:	22-May-2006
// Created by:		JJo
//-------------------------------

//-------------------------------
// Includes

#include "SerialPort.h"

//-------------------------------
// Class

/**
 * Name:		SerialPort
 * Effect:		Constructor for the SerialPort class
 * Inputs:
 *		- Name of COM port to open
 */
SerialPort::SerialPort(const char * name,
					   unsigned long baudRate,
					   unsigned char parity,
					   unsigned char stopBits) :
m_name(name) {

	// Local variables
	DCB dcb;

	// Open communication port
	this->m_handle = CreateFile(name,
								GENERIC_READ | GENERIC_WRITE,
								0,
								NULL,
								OPEN_EXISTING,
								0,
								NULL);
	
	if (this->m_handle == INVALID_HANDLE_VALUE) {

		CFileException::ThrowOsError(GetLastError(), name);
	}

	// Retrieve existing configuration, modify it and reconfigure port
	if (!GetCommState(this->m_handle, &dcb)) {

		CloseHandle(this->m_handle);
		CFileException::ThrowOsError(GetLastError(), name);
	}

	dcb.BaudRate = baudRate;
	dcb.fBinary = true;
	if (parity == NOPARITY) {
		
		dcb.fParity = false;
	}
	else {

		dcb.fParity = true;
	}
	dcb.fOutxCtsFlow = false;
	dcb.fOutxDsrFlow = false;
	dcb.fDtrControl = DTR_CONTROL_DISABLE;
	dcb.fDsrSensitivity = false;
	dcb.fTXContinueOnXoff = true;
	dcb.fOutX = false;
	dcb.fInX = false;
	dcb.fErrorChar = false;
	dcb.fNull = false;
	dcb.fRtsControl = RTS_CONTROL_DISABLE;
	dcb.fAbortOnError = true;
	dcb.StopBits = stopBits;
	dcb.wReserved = 0;
	dcb.ByteSize = 8;

	if (!SetCommState(this->m_handle, &dcb)) {

		CloseHandle(this->m_handle);
		CFileException::ThrowOsError(GetLastError(), name);
	}

	COMMTIMEOUTS c;
	c.ReadIntervalTimeout = 100;
	c.ReadTotalTimeoutConstant = 500;
	c.ReadTotalTimeoutMultiplier = 0;
	c.WriteTotalTimeoutConstant = 5000;
	c.WriteTotalTimeoutMultiplier = 0;
	SetCommTimeouts(this->m_handle, &c);
}

/**
 * Name:		~SerialPort
 * Effect:		Destructor for the SerialPort class
 */
SerialPort::~SerialPort() {

	// Close file
	CloseHandle(this->m_handle);
}

/**
 * Name:		Write
 * Effect:		Writes data to the serial port
 * Inputs:
 *		- Data buffer to write
 *		- Size of buffer
 * Output:		Number of bytes written.
 */
unsigned long SerialPort::Write(const void * data, unsigned int size) {
	
	// Local variables
	unsigned long bytes;

	// Check parameters
	ASSERT(data != NULL);

	// Write to serial port
	if (WriteFile(this->m_handle, data, size, &bytes, NULL) == 0) {

		CFileException::ThrowOsError(GetLastError(), this->m_name);
	}

	return bytes;
}

/**
 * Name:		Read
 * Effect:		Reads data from the serial port
 * Inputs:
 *		- Buffer to store the data
 *		- Size of buffer
 * Output:		Number of bytes received.
 */
unsigned long SerialPort::Read(void * data, unsigned int size) {

	// Local variables
	unsigned long bytes;

	// Check parameters
	ASSERT(data != NULL);

	// Read from serial port
	if (ReadFile(this->m_handle, data, size, &bytes, NULL) == 0) {

		CFileException::ThrowOsError(GetLastError(), this->m_name);
	}

	if (bytes == 0) {

		CFileException * e = new CFileException(CFileException::hardIO, GetLastError());
		e->m_strFileName = this->m_name;
		throw e;
	}

	return bytes;
}

/**
 * Name:		Poll
 * Purpose:		Indicates the number of bytes which can be returned by a single read()
 * Output:		Number of bytes in the input buffer
 */
unsigned long SerialPort::Poll() {

	// Local variables
	COMSTAT comstat;
	unsigned long error;

	if (ClearCommError(this->m_handle, &error, &comstat) == 0) {

		CFileException::ThrowOsError(GetLastError());
	}

	return comstat.cbInQue;
}