/* 
*	File: triggerSerialPort.cpp
*	Author: Eric Martin
*	
*	Copyright: 2013 MBARI 
*	
*	Contains two similar classes that manage queues of struct's
*	meant for interthread interactions of control messages
*	and for image names.
*	
*/

#include "triggerSerialPort.h"

/*
*	Constructor, with supplied comm port name (i.e. COM1)
*/
triggerSerialPort::triggerSerialPort(TCHAR * sNewDevice)
{
	const char funcname[] = "triggerSerialPort()";
	
	// Copy in the comm port name. 
	_tcscpy_s(sDevice,sNewDevice);
	bIsConnected = false;

	if (!openSerialPort()) {
		fprintf(stderr, "%s: ERROR Cannot open serial port `%s'\n",funcname, sDevice);
		return;
	}
	// Configure Serial Port (default settings)
	dcbSerialParams.DCBlength=sizeof(DCB);

	if (!GetCommState(hSerial,&dcbSerialParams)) {
		fprintf(stderr,"%s: ERROR: Cannot retrieve comm port state. ERRNO %lu\n",funcname, GetLastError());
		if (!closeSerialPort())
			fprintf(stderr,"%s: ERROR: Cannot close serial port. ERRNO %lu\n",funcname, GetLastError());
		bIsConnected = false; return;
	}

	// Change Comm Struct
	dcbSerialParams.BaudRate=CBR_4800; // slow for toggle
	dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
	
	// Commmit Serial Port Changes
	if (!SetCommState(hSerial,&dcbSerialParams)) {
		fprintf(stderr,"ERROR: Cannot set comm port state. ERRNO %lu\n",GetLastError());
		if (!closeSerialPort())
			fprintf(stderr,"ERROR: Cannot close serial port. ERRNO %lu\n",GetLastError());
		bIsConnected = false; return;
	}
	
	//Verify Changes Took
	if (!GetCommState(hSerial, &dcbSerialParams)) {
		fprintf(stderr, "%s: ERROR: Cannot retrieve comm port state. ERRNO %lu\n", funcname, GetLastError());
		if (!closeSerialPort())
			fprintf(stderr, "%s: ERROR: Cannot close serial port. ERRNO %lu\n", funcname, GetLastError());
		bIsConnected = false; return;
	}
	return;
}

/*
*	Change settings of already connected port.
*/
bool triggerSerialPort::changePortSettings(DWORD baudRate, BYTE byteSize, CHAR parity, BYTE stopBits) {
	int retval = true;
	const char funcname[] = "triggerSerialPort::changePortSettings()";
	if (!bIsConnected) 
		return (false);

	/* CHECK INPUT VALUES */
	// baudrate check
	if (baudRate != CBR_110	&& 	baudRate != CBR_300	&& 	baudRate != CBR_600	&&	baudRate != CBR_1200 
		&&	baudRate != CBR_2400	&&	baudRate != CBR_4800	&&	baudRate != CBR_9600	
		&&	baudRate != CBR_14400	&&	baudRate != CBR_19200	&&	baudRate != CBR_38400	
		&&	baudRate != CBR_56000	&&	baudRate != CBR_57600	&&	baudRate != CBR_115200	
		&&	baudRate != CBR_128000	&&	baudRate != CBR_256000	) 
	{
		fprintf(stderr, "%s: ERROR: Invalid baud rate [%i]\n", funcname, baudRate);
		retval = false;
	}

	//bytesize check
	if (byteSize < 5 || byteSize > 9 ) {
		fprintf(stderr, "%s: ERROR: Invalid byte size [%i]\n", funcname, byteSize);
		retval = false;
	}

	//parity check 
	if (parity != 'N' && parity != 'E' && parity != 'O') {
		fprintf(stderr, "%s: ERROR: Invalid parity [int:%i]\n", funcname, parity);
		retval = false;
	}

	//stopbit check
	if (stopBits != 2 && stopBits != 1) {
		fprintf(stderr, "%s: ERROR: Invalid stop bits [int:%i]\n", funcname, stopBits);
		retval = false;
	}

	//check all pass
	if (!retval) return false;

	/* SET PARAMETERS */
	dcbSerialParams.BaudRate = baudRate;
	dcbSerialParams.ByteSize = byteSize;
	switch (stopBits) {
	case 1:
		dcbSerialParams.StopBits = ONESTOPBIT;
		break;
	case 2:
		dcbSerialParams.StopBits = TWOSTOPBITS;
		break;
	}
	switch (parity) {
	case 'N':
		dcbSerialParams.Parity   = NOPARITY;
		break;
	case 'O':
		dcbSerialParams.Parity   = ODDPARITY;
		break;
	case 'E':
		dcbSerialParams.Parity   = EVENPARITY;
		break;
	}
	
	/* COMMIT CHANGES */
	if (!SetCommState(hSerial,&dcbSerialParams)) {
		fprintf(stderr,"%s: ERROR: Cannot set comm port state. ERRNO %lu\n",funcname,GetLastError());
		if (!closeSerialPort())
			fprintf(stderr,"%s: ERROR: Cannot close serial port. ERRNO %lu\n",funcname,GetLastError());
		bIsConnected = false; 
		return false;
	}
	
	return true;
}

/*
*	Destructor	
*/
triggerSerialPort::~triggerSerialPort(void)
{
		closeSerialPort();
}

/*
*	Open the serial port and return the connection status
*/
bool triggerSerialPort::openSerialPort(void) {

    //qt fix
    wchar_t wDevice[255];
    mbstowcs(wDevice, sDevice,strlen(sDevice)+1);
    LPWSTR lpwDevice = wDevice;
    hSerial = CreateFile(lpwDevice,
		GENERIC_READ | GENERIC_WRITE,
		0,
		0,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		0);

	if (hSerial==INVALID_HANDLE_VALUE) {
		if (GetLastError() == ERROR_FILE_NOT_FOUND)
			fprintf(stderr,"triggerSerialPort::openSerialPort(): ERROR: Invalid COM Port\n");
		else
			fprintf(stderr,"triggerSerialPort::openSerialPort(): ERROR: Unknown Error %lu\n", GetLastError());
		bIsConnected = false;
		return false;
	}
	bIsConnected = true;
	return true;
}

/*
*	Close down the port and report success or failure.
*/
bool triggerSerialPort::closeSerialPort(void)
{
	return (CloseHandle(this->hSerial)?true:false);
}

/*
*	Issue 0x00 on the serial port to trigger electronics for the 
*	camera hardware trigger input. The char should cause a transistor
*	to close a line to ground for triggering. 
*/
bool triggerSerialPort::sendTrigger() {
	const char funcname[] = "triggerSerialPort::sendTrigger()";
	if (!TransmitCommChar(hSerial,0x00)) {
		fprintf(stderr,"%s: ERROR: Cannot send zero byte. ERRNO %lu\n",funcname, GetLastError());
		if (!closeSerialPort())
			fprintf(stderr,"%s: ERROR: Cannot close serial port. ERRNO %lu\n", funcname, GetLastError());
		return false;
	}
	return true;
}

/*
*	Return the connection state 	
*/
bool triggerSerialPort::isConnected() {
	return bIsConnected;
}

/*
*	Change the state of the RTS Line
*/
bool triggerSerialPort::setRTS(int state) {
	const char funcname[] = "triggerSerialPort::setRTS()";
	int rtsState;
	switch (state) {
	case 0: //LOW
		rtsState = CLRRTS;
		break;
	case 1: //HIGH
		rtsState = SETRTS;
		break;
	default:
		fprintf(stderr, "%s: ERROR: incorrect value for state input.\n", funcname);
	}

	//Now set the state
	if (!EscapeCommFunction(hSerial, rtsState))
		return false;

	return true;
}

