/* GMM (C) 2018 Stephane Charette <stephanecharette@gmail.com>
 * $Id: SerialCommWin32.cpp 2637 2018-10-16 05:55:54Z stephane $
 */


/** @file
 * This file is only included when building the Windows version of GMM.
 * See SerialCommStub.cpp for the Linux equivalent which does nothing.
 */


#include "GMM.hpp"


void TabCanvas::open_serial_device(const std::string & device_name)
{
	comm_handle = CreateFile(device_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (comm_handle == INVALID_HANDLE_VALUE)
	{
		const std::string str = "failed to open serial communication device " + device_name;
		warnings_and_errors.push_back(str);
		throw Lox::Exception(LOX_WHERE, str);
	}

	const std::string comm_str = cfg().get_str(prefix + "_comm_str");
	LOG_MSG("device=" << device_name << ", str=" << comm_str);

	DCB dcb = {0};
	dcb.DCBlength = sizeof(dcb);
	BOOL result = BuildCommDCBA(comm_str.c_str(), &dcb);
	if (result == FALSE)
	{
		const std::string str = "failed to build DCB with \"" + comm_str + "\"";
		warnings_and_errors.push_back(str);
		throw Lox::Exception(LOX_WHERE, str);
	}

	result = SetCommState(comm_handle, &dcb);
	if (result == FALSE)
	{
		const std::string str = "failure in call to SetCommState() for device " + device_name;
		warnings_and_errors.push_back(str);
		throw Lox::Exception(LOX_WHERE, str);
	}

	COMMTIMEOUTS timeouts					= {0};
	timeouts.ReadIntervalTimeout			= 10;
	timeouts.ReadTotalTimeoutConstant		= 10;
	timeouts.ReadTotalTimeoutMultiplier		= 10;
	timeouts.WriteTotalTimeoutConstant		= 10;
	timeouts.WriteTotalTimeoutMultiplier	= 10;
	LOG_MSG(
		"max time between read: "			<< timeouts.ReadIntervalTimeout			<<
		" read multiplier of chars: "		<< timeouts.ReadTotalTimeoutMultiplier	<<
		" read constant in millisecs: "		<< timeouts.ReadTotalTimeoutConstant	<<
		" write multiplier of chars: "		<< timeouts.WriteTotalTimeoutMultiplier	<<
		" write constant in millisecs: "	<< timeouts.WriteTotalTimeoutConstant	);

	result = SetCommTimeouts(comm_handle, &timeouts);
	if (result == FALSE)
	{
		const std::string str = "failure in call to SetCommTimeouts() for device " + device_name;
		warnings_and_errors.push_back(str);
		throw Lox::Exception(LOX_WHERE, str);
	}

	GetCommState(comm_handle, &dcb);
	LOG_MSG(
		"device="				<< device_name					<<
		" Baud="				<< dcb.BaudRate					<<
		" Binary="				<< (int)dcb.fBinary				<<
		" Parity="				<< (int)dcb.fParity				<<
		" OutxCtsFlow="			<< (int)dcb.fOutxCtsFlow		<<
		" OutxDsrFlow="			<< (int)dcb.fOutxDsrFlow		<<
		" DtrControl="			<< (int)dcb.fDtrControl			<<
		" DsrSensitivity="		<< (int)dcb.fDsrSensitivity		<<
		" TXContinueOnXoff="	<< (int)dcb.fTXContinueOnXoff	<<
		" OutX="				<< (int)dcb.fOutX				<<
		" InX="					<< (int)dcb.fInX				<<
		" ErrorChar="			<< (int)dcb.ErrorChar			<<
		" Null="				<< (int)dcb.fNull				<<
		" RtsControl="			<< (int)dcb.fRtsControl			<<
		" AbortOnError="		<< (int)dcb.fAbortOnError		<<
		" XonLim="				<< (int)dcb.XonLim				<<
		" XoffLim="				<< (int)dcb.XoffLim				<<
		" ByteSize="			<< (int)dcb.ByteSize			<<
		" Parity="				<< (int)dcb.Parity				<<
		" StopBits="			<< (int)dcb.StopBits			);

	return;
}


void TabCanvas::close_serial_device(void)
{
	CloseHandle(comm_handle);

	comm_handle = INVALID_HANDLE_VALUE;

	return;
}


std::string TabCanvas::read_from_device(VBytes & read_buffer)
{
	DWORD number_of_bytes_read = 0;
	ReadFile(comm_handle, read_buffer.data(), read_buffer.size(), &number_of_bytes_read, nullptr);
	read_buffer[number_of_bytes_read] = '\0';

	return std::string(reinterpret_cast<char*>(read_buffer.data()), number_of_bytes_read);
}
