/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT 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 3 of the License, or     |
   |     (at your option) any later version.                                   |
   |                                                                           |
   |   MRPT 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 MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */

#ifndef CSERIALPORT_H
#define CSERIALPORT_H

#include <MRPT/config.h>
#include <MRPT/UTILS/CStream.h>
#include <queue>

#include <lib_hwdrivers/link_pragmas.h>


namespace HWDRIVERS
{
/** A communications serial port built as an implementation of a UTILS::CStream.
 * On communication errors (eg. the given port number does not exist, timeouts,...), most of the methods will
 * raise an exception of the class "std::exception"
 *
 *  The serial port to open is passed in the constructor in the form of a string description,
 *   which is platform dependent.
 *
 *  In windows they are numbered "COM1"-"COM4" and "\\.\COMXXX" for numbers above.
 *    It is recomended to always use the prefix "\\.\" despite the actual port number.
 *
 *  In Linux the name must refer to the device, for example: "ttyUSB0","ttyS0". If the name string does not
 *   start with "/" (an absolute path), the constructor will assume the prefix "/dev/".
 *
 *  History:
 *    - 1/DEC/2005:  (JLBC) First version
 *    - 20/DEC/2006: (JLBC) Integration into the MRPT framework
 *    - 12/DEC/2007: (JLBC) Added support for Linux.
 *
 * \todo Add the internal buffer to the Windows implementation also
 */
class CSerialPort : public CStream
{
	friend class PosixSignalDispatcherImpl;
public:
	/** Constructor
	 * \param portName The serial port to open. See comments at the begining of this page.
	 * \param openNow Whether to try to open the port now. If not selected, the port should be open later with "open()".
	 *
	 */
	CSerialPort( const std::string &portName, bool openNow = true );

	/** Destructor
	*/
	virtual ~CSerialPort();

	/** Open the port. If is already open results in no action.
	* \exception std::exception On communication errors
	*/
	void  open();

	/** Close the port. If is already closed, results in no action.
	*/
	void  close();

	/** Returns if port has been correctly open.
	*/
	bool  isOpen();

	/** Purge tx and rx buffers.
	   * \exception std::exception On communication errors
	   */
	void  purgeBuffers();

	/** Changes the configuration of the port.
	*  \param parity  0:No parity, 1:Odd, 2:Even (WINDOWS ONLY: 3:Mark, 4:Space)
	*  \param baudRate The desired baud rate Accepted values: 50 - 230400
	*  \param bits Bits per word (typ. 8) Accepted values: 5,6,7,8.
	*  \param nStopBits Stop bits (typ. 1) Accepted values: 1,2
	*  \param enableFlowControl Whether to enable the hardware flow control (RTS/CTS) (default=no)
	* \exception std::exception On communication errors
	*/
	void  setConfig(
		int baudRate,
		int parity = 0,
		int bits = 8,
		int nStopBits = 1,
		bool enableFlowControl=false);

	/** Changes the timeouts of the port, in milliseconds.
	* \exception std::exception On communication errors
	*/
	void  setTimeouts(
	    int		ReadIntervalTimeout,
	    int		ReadTotalTimeoutMultiplier,
	    int		ReadTotalTimeoutConstant,
	    int		WriteTotalTimeoutMultiplier,
	    int		WriteTotalTimeoutConstant );


	/** Implements the virtual method responsible for reading from the stream.
	* \exception std::exception On communication errors
	 */
	size_t  Read(void *Buffer, size_t Count);

	/** Implements the virtual method responsible for writing to the stream.
	 *  Write attempts to write up to Count bytes to Buffer, and returns the number of bytes actually written.
	* \exception std::exception On communication errors
	 */
	size_t  Write(const void *Buffer, size_t Count);


	/** Introduces a pure virtual method for moving to a specified position in the streamed resource.
	 *   he Origin parameter indicates how to interpret the Offset parameter. Origin should be one of the following values:
	 *	- sFromBeginning	(Default) Offset is from the beginning of the resource. Seek moves to the position Offset. Offset must be >= 0.
	 *	- sFromCurrent		Offset is from the current position in the resource. Seek moves to Position + Offset.
	 *	- sFromEnd			Offset is from the end of the resource. Offset must be <= 0 to indicate a number of bytes before the end of the file.
	 * \return Seek returns the new value of the Position property.
	 */
	size_t Seek(long Offset, CStream::TSeekOrigin Origin = sFromBeginning)
	{
		MRPT_TRY_START
		MRPT_UNUSED_PARAM(Origin);
		MRPT_UNUSED_PARAM(Offset);
		THROW_EXCEPTION("Method not applicable to serial communications port CStream!");
		MRPT_TRY_END
	}

	/** Returns the total amount of bytes in the stream.
	 */
	size_t getTotalBytesCount()
	{
		MRPT_TRY_START
		THROW_EXCEPTION("Method not applicable to serial communications port CStream!");
		MRPT_TRY_END
	}

	/** Method for getting the current cursor position, where 0 is the first byte and TotalBytesCount-1 the last one.
	 */
	size_t getPosition()
	{
		MRPT_TRY_START
		THROW_EXCEPTION("Method not applicable to serial communications port CStream!");
		MRPT_TRY_END
	}

protected:

	/** The complete name of the serial port device (i.e. "\\.\COM10","/dev/ttyS2",...)
	  */
	std::string 	m_serialName;
	int				m_baudRate;
	int				m_totalTimeout_ms,m_interBytesTimeout_ms;

#ifdef MRPT_OS_WINDOWS
	// WINDOWS
	void		*hCOM;
#else
	// LINUX
	/** The file handle (-1: Not open)
	  */
	int 		hCOM;

    /** This method is called when the specified POSIX signal is
     * received by the PosixSignalDispatcher that is managing
     * this handler.
     */
    void HandlePosixSignal( int signalNumber );

    /** Circular buffer used to store the received data.
     */
    std::queue<uint8_t> m_inputBuffer;

	/** Waits for a character up to "msTimeout" milliseconds.
	  * \return true if OK, false on timeout or errors.
	  */
	inline bool ReadByte(
		unsigned char  &readByte,
		const int msTimeout);


#endif

}; // end of class

} // end of namespace


#endif
