/******************************************************************************
Copyright (C) 2005 Matteo Lucarelli

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
******************************************************************************/

#include "stdafx.h"
#include "wSerial.h"
#include <string.h>

wSerial::wSerial(DWORD buffersdim,LPCTSTR port,DWORD baudrate,BYTE parity,BYTE bytesize,BYTE stopbits)
{
	m_Error=WSERIALERR_NONE;

	m_BufferMaxDim = buffersdim;
	m_Buffer = new char[m_BufferMaxDim];
	m_BufferString = new char[m_BufferMaxDim+1];
	m_tmpBuffer = new char[m_BufferMaxDim+1];
	
	m_BufferDim=0;
	*m_BufferString=0;
	mh_Port=NULL;

	mh_Port = CreateFile(port,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);

	if (mh_Port!=INVALID_HANDLE_VALUE){
		if (SetupComm(mh_Port,m_BufferMaxDim,m_BufferMaxDim)){	
			if (PurgeComm(mh_Port,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_TXABORT|PURGE_RXABORT)){
				if (reset(baudrate,parity,bytesize,stopbits)){
					COMMTIMEOUTS to;
					if (GetCommTimeouts(mh_Port,&to)){
						to.ReadIntervalTimeout=MAXDWORD; 
						to.ReadTotalTimeoutMultiplier=0;
						to.ReadTotalTimeoutConstant=0;
						to.WriteTotalTimeoutMultiplier=0;
						to.WriteTotalTimeoutConstant=0;
						if (!SetCommTimeouts(mh_Port,&to))
							m_Error=WSERIALERR_ACCESS;
					}
					else m_Error=WSERIALERR_ACCESS;
				}
			}
			else m_Error=WSERIALERR_ACCESS;
		}
		else m_Error=WSERIALERR_ACCESS;
	}
	else m_Error=WSERIALERR_NOHANDLE;
}

wSerial::~wSerial()
{
	if (mh_Port) CloseHandle(mh_Port);
	delete m_Buffer;
	delete m_BufferString;
	delete m_tmpBuffer;
}

bool wSerial::reset(DWORD baudrate,BYTE parity,BYTE bytesize,BYTE stopbits)
{
	if (mh_Port){
		DCB dcb;
		dcb.DCBlength=sizeof(DCB);
		if (GetCommState(mh_Port, &dcb)){
			dcb.BaudRate = baudrate;
			dcb.fBinary = TRUE; 
			dcb.fParity = parity!=0?TRUE:FALSE;
			dcb.fOutxCtsFlow = FALSE;
			dcb.fOutxDsrFlow = FALSE;
			dcb.fDtrControl = DTR_CONTROL_ENABLE;
			dcb.fDsrSensitivity = FALSE;
			dcb.fTXContinueOnXoff = TRUE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			dcb.fErrorChar = FALSE;
			dcb.fNull = FALSE;
			dcb.fRtsControl = RTS_CONTROL_ENABLE;
			dcb.fAbortOnError = FALSE;
			dcb.ByteSize = bytesize;
			dcb.Parity = parity;
			dcb.StopBits = stopbits;
			if (SetCommState(mh_Port, &dcb)){
				return true;
			}
			else m_Error=WSERIALERR_ACCESS;
		}
		else m_Error=WSERIALERR_ACCESS;
	}
	else m_Error=WSERIALERR_NOHANDLE;

	return false;
}

bool wSerial::read()
{
	unsigned long dim=0;
	*(m_tmpBuffer)=0;
 
	if (ReadFile(mh_Port, m_tmpBuffer, m_BufferMaxDim, &dim, NULL)!=0){
		if (dim!=0){
			if (dim==m_BufferMaxDim){
				memcpy(m_Buffer,m_tmpBuffer,(size_t)m_BufferMaxDim);
				m_BufferDim = m_BufferMaxDim;
			}else{
				if ((dim+m_BufferDim)<=m_BufferMaxDim){
					memcpy((m_Buffer+m_BufferDim),m_tmpBuffer,(size_t)dim);
					m_BufferDim += dim;
				}else{
					memmove(m_Buffer,(m_Buffer+m_BufferDim+dim-m_BufferMaxDim),(size_t)(m_BufferMaxDim-dim));
					memcpy((m_Buffer+m_BufferMaxDim-dim),m_tmpBuffer,(size_t)dim);
					m_BufferDim = m_BufferMaxDim ;
				}
			}
			memcpy(m_BufferString,m_Buffer,(size_t)m_BufferDim);
			*(m_BufferString+m_BufferDim)=0;
		}
		else m_Error=WSERIALERR_NODATA;
	}else m_Error=WSERIALERR_ACCESS;

	return false;
}

bool wSerial::write(char* buff, DWORD len)
{
	unsigned long wrote=0;

	if (WriteFile(mh_Port, buff, len, &wrote, NULL)){
		if (wrote==len){
			return true;
		}
		else m_Error=WSERIALERR_UNCOMPLETE;
	}
	else m_Error=WSERIALERR_ACCESS;
	return false;;
}

void wSerial::clear()
{
	// flush system buffer
	read();

	// clear local buffers
	m_BufferDim=0;
	memset(m_Buffer,0,m_BufferMaxDim);
	*(m_BufferString)=0;
}