#ifndef __SERIAL_PORT_H__
#define __SERIAL_PORT_H__

//#include "stdafx.h"

#ifndef CSERIALPORT_EXT_CLASS
#define CSERIALPORT_EXT_CLASS
#endif

#define ERR_BUFFER	4096

#ifdef __cplusplus
extern "C" {
#endif

	/////////////////////////////////////////// Classes ///////////////////////////////////////////
	class CSERIALPORT_EXT_CLASS CSerialException : public CException
	{
	public:
		// Constructors / Destructors
		CSerialException(DWORD dwError);

		// Methods
#ifdef _DEBUG
		virtual void Dump(CDumpContext& dc) const;
#endif
		virtual BOOL GetErrorMessage(__out_ecount_z(nMaxError) LPTSTR lpszError, __in UINT nMaxError, __out_opt PUINT pnHelpContext = NULL);
		CString GetErrorMessage();

		// Data members
		DWORD m_dwError;

	protected:
		DECLARE_DYNAMIC(CSerialException)
	};

	class CSerialPort
	{
	public:
		//Enums
		enum FlowControl
		{
			NoFlowControl,
			CtsRtsFlowControl,
			CtsDtrFlowControl,
			DsrRtsFlowControl,
			DsrDtrFlowControl,
			XonXoffFlowControl
		};

		enum Parity
		{
			NoParity = 0,
			OddParity = 1,
			EvenParity = 2,
			MarkParity = 3,
			SpaceParity = 4
		};

		enum StopBits
		{
			OneStopBit,
			OnePointFiveStopBits,
			TwoStopBits
		};

		//Constructors / Destructors
		CSerialPort();
		virtual ~CSerialPort();

		//General Methods
		void	Open(int nPort, DWORD dwBaud = 9600, Parity parity = NoParity, BYTE dataBits = 8,
			StopBits stopBits = OneStopBit, FlowControl flowCtl = NoFlowControl, BOOL bOverlapped = FALSE);
		void	Open(LPCTSTR pszPort, DWORD dwBaud = 9600, Parity parity = NoParity, BYTE dataBits = 8,
			StopBits stopBits = OneStopBit, FlowControl flowCtl = NoFlowControl, BOOL bOverlapped = FALSE);
		void	Close();
		void	Attach(HANDLE hComm);
		HANDLE	Detach();
		operator HANDLE() const { return m_hComm; };
		BOOL IsOpen() const { return m_hComm != INVALID_HANDLE_VALUE; };
#ifdef _DEBUG
		void Dump(CDumpContext& dc) const;
#endif

		// Reading / Writting Methods
		DWORD Read(void* lpBuf, DWORD dwCount);
		void Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesRead = NULL);
		void ReadEx(void* lpBuf, DWORD dwCount);
		DWORD Write(const void* lpBuf, DWORD dwCount);
		void Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesWritten = NULL);
		void WriteEx(const void* lpBuf, DWORD dwCount);
		void TransmitChar(char cChar);
		void GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait);
		void CancelIO();
		DWORD BytesWaiting();
		BOOL DataWaiting(DWORD dwTimeout);

		// Configuration Methods
		void GetConfig(COMMCONFIG& config);
		static void GetDefaultConfig(int nPort, COMMCONFIG& config);
		void SetConfig(COMMCONFIG& config);
		static void SetDefaultConfig(int nPort, COMMCONFIG& config);

		// Misc RS232 Methods
		void ClearBreak();
		void SetBreak();
		void ClearError(DWORD& dwErrors);
		void GetStatus(COMSTAT& stat);
		void GetState(DCB& dcb);
		void SetState(DCB& dcb);
		void Escape(DWORD dwFunc);
		void ClearDTR();
		void ClearRTS();
		void SetDTR();
		void SetRTS();
		void SetXOFF();
		void SetXON();
		void GetProperties(COMMPROP& porperties);
		void GetModemStatus(DWORD& dwModemStatus);

		// Timeouts
		void SetTimeouts(COMMTIMEOUTS& timeouts);
		void GetTimeouts(COMMTIMEOUTS& timeouts);
		void Set0Timeout();
		void Set0WriteTimeout();
		void Set0ReadTimeout();

		// Event Methods
		void SetMask(DWORD dwMask);
		void GetMask(DWORD& dwMask);
		void WaitEvent(DWORD& dwMask);
		BOOL WaitEvent(DWORD& dwMask, OVERLAPPED& overlapped);

		// Queue Methods
		void Flush();
		void Purge(DWORD dwFlags);
		void TerminateOutstandingWrites();
		void TerminateOutstandingReads();
		void ClearWriteBuffer();
		void ClearReadBuffer();
		void Setup(DWORD dwInQueue, DWORD dwOutQueue);

		// Overridables
		virtual void OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped);

		// Static methods
		static void ThrowSerialException(DWORD dwError = 0);

	protected:
		// Typedefs
		typedef BOOL(WINAPI CANCELIO)(HANDLE);
		typedef CANCELIO* LPCANCELIO;

		// Static methods
		static void WINAPI _OnCompletion(DWORD dwErrorCode, DWORD dwCount, LPOVERLAPPED lpOverlapped);

		// Member variables
		HANDLE		m_hComm;		// Handle to the comms port
		HANDLE		m_hEvent;		// A event handle we need for internal synchronisation
		HINSTANCE	m_hKernel32;	// Kernel32 handle
		LPCANCELIO	m_lpfnCancelIO;	// CancelIO function pointer
	};

#ifdef __cplusplus
}
#endif

#endif	// __SERIAL_PORT_H__