#ifndef _QEXTSERIALBASE_H_
#define _QEXTSERIALBASE_H_

#include <QIODevice>
#include <QFile>
#include <QThread>
#include <QMutex>

/*if all warning messages are turned off, flag portability warnings to be turned off as well*/
#ifdef _TTY_NOWARN_
#define _TTY_NOWARN_PORT_
#endif

/*macros for thread support*/
#define LOCK_MUTEX() mutex->lock()
#define UNLOCK_MUTEX() mutex->unlock()

/*macros for warning and debug messages*/
#ifdef _TTY_NOWARN_PORT_
#define TTY_PORTABILITY_WARNING(s)
#else
#define TTY_PORTABILITY_WARNING(s) qWarning(s)
#endif /*_TTY_NOWARN_PORT_*/
#ifdef _TTY_NOWARN_
#define TTY_WARNING(s)
#else
#define TTY_WARNING(s) qWarning(s)
#endif /*_TTY_NOWARN_*/


/*line status constants*/
#define LS_CTS  0x01
#define LS_DSR  0x02
#define LS_DCD  0x04
#define LS_RI   0x08
#define LS_RTS  0x10
#define LS_DTR  0x20
#define LS_ST   0x40
#define LS_SR   0x80

/*error constants*/
#define E_NO_ERROR                   0
#define E_INVALID_FD                 1
#define E_NO_MEMORY                  2
#define E_CAUGHT_NON_BLOCKED_SIGNAL  3
#define E_PORT_TIMEOUT               4
#define E_INVALID_DEVICE             5
#define E_BREAK_CONDITION            6
#define E_FRAMING_ERROR              7
#define E_IO_ERROR                   8
#define E_BUFFER_OVERRUN             9
#define E_RECEIVE_OVERFLOW          10
#define E_RECEIVE_PARITY_ERROR      11
#define E_TRANSMIT_OVERFLOW         12
#define E_READ_FAILED               13
#define E_WRITE_FAILED              14

/*!
 * Enums for port settings.
 */
enum NamingConvention 
{
    WIN_NAMES,
    IRIX_NAMES,
    HPUX_NAMES,
    SUN_NAMES,
    DIGITAL_NAMES,
    FREEBSD_NAMES,
    LINUX_NAMES
};

enum BaudRateType 
{
    BAUD50,                //POSIX ONLY
    BAUD75,                //POSIX ONLY
    BAUD110,
    BAUD134,               //POSIX ONLY
    BAUD150,               //POSIX ONLY
    BAUD200,               //POSIX ONLY
    BAUD300,
    BAUD600,
    BAUD1200,
    BAUD1800,              //POSIX ONLY
    BAUD2400,
    BAUD4800,
    BAUD9600,
    BAUD14400,             //WINDOWS ONLY
    BAUD19200,
    BAUD38400,
    BAUD56000,             //WINDOWS ONLY
    BAUD57600,
    BAUD76800,             //POSIX ONLY
    BAUD115200,
    BAUD128000,            //WINDOWS ONLY
    BAUD256000             //WINDOWS ONLY
};

enum DataBitsType 
{
    DATA_5,
    DATA_6,
    DATA_7,
    DATA_8
};

enum ParityType 
{
    PAR_NONE,
    PAR_ODD,
    PAR_EVEN,
    PAR_MARK,               //WINDOWS ONLY
    PAR_SPACE
};

enum StopBitsType 
{
    STOP_1,
    STOP_1_5,               //WINDOWS ONLY
    STOP_2
};

enum FlowType 
{
    FLOW_OFF,
    FLOW_HARDWARE,
    FLOW_XONXOFF
};

/**
 * structure to contain port settings
 */
struct PortSettings 
{
    BaudRateType BaudRate;
    DataBitsType DataBits;
    ParityType Parity;
    StopBitsType StopBits;
    FlowType FlowControl;
    long Timeout_Millisec;
};

/*!
 * \author Stefan Sander
 * \author Michal Policht
 * 
 * A common base class for Win_QextSerialBase, Posix_QextSerialBase and QextSerialPort.
 */
class QextSerialBase : public QIODevice 
{
	Q_OBJECT

	public:
		enum QueryMode {
			Polling,
			EventDriven
		};

	protected:
	    QMutex* mutex;
	    QString port;
	    PortSettings Settings;
	    ulong lastErr;
		QextSerialBase::QueryMode _queryMode;

	    virtual qint64 readData(char * data, qint64 maxSize)=0;
	    virtual qint64 writeData(const char * data, qint64 maxSize)=0;

	public:
	    QextSerialBase();
	    QextSerialBase(const QString & name);
	    virtual ~QextSerialBase();
	    virtual void construct();
	    virtual void setPortName(const QString & name);
	    virtual QString portName() const;
	    
	    /**!
	     * Get query mode.
	     * 	\return query mode.
	     */
		inline QextSerialBase::QueryMode queryMode() const { return _queryMode; };

		/*!
		 * Set desired serial communication handling style. You may choose from polling 
		 * or event driven approach. This function does nothing when port is open; to 
		 * apply changes port must be reopened.
		 * 
		 * In event driven approach read() and write() functions are acting
		 * asynchronously. They return immediately and the operation is performed in 
		 * the background, so they doesn't freeze the calling thread.
		 * To determine when operation is finished, QextSerialPort runs separate thread 
		 * and monitors serial port events. Whenever the event occurs, adequate signal 
		 * is emitted.
		 * 
		 * When polling is set, read() and write() are acting synchronously. Signals are
		 * not working in this mode and some functions may not be available. The advantage
		 * of polling is that it generates less overhead due to lack of signals emissions
		 * and it doesn't start separate thread to monitor events.
		 * 
		 * Generally event driven approach is more capable and friendly, although some 
		 * applications may need as low overhead as possible and then polling comes.
		 * 
		 * \param mode query mode.
		 */
		virtual void setQueryMode(QueryMode mode);

//		virtual void setBlockingRead(bool block) = 0; ///< @todo implement.

	    virtual void setBaudRate(BaudRateType)=0;
	    virtual BaudRateType baudRate() const;
	    virtual void setDataBits(DataBitsType)=0;
	    virtual DataBitsType dataBits() const;
	    virtual void setParity(ParityType)=0;
	    virtual ParityType parity() const;
	    virtual void setStopBits(StopBitsType)=0;
	    virtual StopBitsType stopBits() const;
	    virtual void setFlowControl(FlowType)=0;
	    virtual FlowType flowControl() const;
	    virtual void setTimeout(long)=0;
	
	    virtual bool open(OpenMode mode)=0;
	    virtual bool isSequential() const;
	    virtual void close()=0;
	    virtual void flush()=0;
	
	    virtual qint64 size() const=0;
	    virtual qint64 bytesAvailable()=0;
	    virtual bool atEnd() const;
	
	    virtual void ungetChar(char c)=0;
	    virtual qint64 readLine(char * data, qint64 maxSize);
	
	    virtual ulong lastError() const;
	    virtual void translateError(ulong error)=0;
	
	    virtual void setDtr(bool set=true)=0;
	    virtual void setRts(bool set=true)=0;
	    virtual ulong lineStatus()=0;

	signals:
		/**
		 * This signal is emitted whenever port settings are updated.
		 * 	\param valid \p true if settings are valid, \p false otherwise.
		 * 
		 * 	@todo implement.
		 */
//		void validSettings(bool valid);

		/*!
		 * This signal is emitted whenever dsr line has changed its state. You may
		 * use this signal to check if device is connected.
		 * 	\param status \p true when DSR signal is on, \p false otherwise.
		 * 
		 * 	\see lineStatus().
		 */
		void dsrChanged(bool status);
};

#endif
