/** \file
 *
 *  Contains the UartStreamBase class declaration.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#ifndef UARTSTREAMBASE_H_
#define UARTSTREAMBASE_H_

#include "io/IOStream.h"
#include "utils/Timestamp.h"

#include <termios.h>
#include "../logger/LoggerBase.h"

/**
 *  Implements both InStream and OutStream for
 *  the LPC3XXX Standard UART
 *
 *  \ingroup io
 */
class UartStreamBase: public IOStream
{
public:

    /// Baudrates as per termios
    typedef enum { B_50,    //  = B50,
                   B_75,    //  = B75,
                   B_110,   //  = B110,
                   B_134,   //  = B134,
                   B_150,   //  = B150,
                   B_200,   //  = B200,
                   B_300,   //  = B300,
                   B_600,   //  = B600,
                   B_1200,  //  = B1200,
                   B_1800,  //  = B1800,
                   B_2400,  //  = B2400,
                   B_4800,  //  = B4800,
                   B_9600,  //  = B9600,
                   B_19200, //  = B19200,
                   B_38400, //  = B38400,
                   B_57600, //  = B57600,
                   B_115200, // = B115200,
                   B_230400, // = B230400,
                   /*B_460800, // = B460800,
                   B_921600, // = B921600, */
                 } Baudrate;

    /// UARTS
    typedef enum { NOT_UART,   // Non-UART file or device
                   UART1_TX0,  // High Speed UART
                   UART2_TX1,  // High Speed UART
                   UART3_S1,   // Standard UART. Half duplex handshaking available
                   UART4_S2,   // Standard UART
                   UART5_S0,   // Standard UART
                   UART6_S3,   // Standard UART. Half duplex handshaking available
                   UART7_TX2,  // High Speed UART
                   UART_A0,  // Load controller UART#0
                   UART_A1,  // Load controller UART#1
                   UART_A2,  // Load controller UART#2
                   UART_A3,  // Load controller UART#3
                   UART_A4,  // Load controller UART#4
                   UART_A5,  // Load controller UART#5
                   UART_A6,  // Load controller UART#6
                   UART_A7,  // Load controller UART#7
                   UART_B0,  // Load controller UART#8
                   UART_B1,  // Load controller UART#9
                   UART_B2, // Load controller UART#10
                   UART_B3, // Load controller UART#11
                   UART_B4,  // Load controller UART#12
                   UART_B5,  // Load controller UART#13
                   UART_B6,  // Load controller UART#14
                   UART_B7,  // Load controller UART#15
                   UART_C0,  // Load controller UART#16
                   UART_C1,  // Load controller UART#17
                   UART_C2,  // Load controller UART#18
                   UART_C3,  // Load controller UART#19
                   UART_C4,  // Load controller UART#20
                   UART_C5,  // Load controller UART#21
                   UART_C6,  // Load controller UART#22
                   UART_C7,  // Load controller UART#23
                   UART_OTHER, // Other UART
                 } UartId;

    /// UART Errors
    typedef enum { OK = 0,
                   BUFFER_FULL,
                   TIMEOUT,
                   CANNOT_OPEN_SERIAL_PORT,
                   CANNOT_GET_DEVICE_SETTINGS,
                   BAD_BAUD_RATE_FOR_PORT,
                   BAD_PARAMETERS_FOR_PORT,
                   PORT_NOT_OPEN,
                   WRITE_CHAR_LOST
                 } UartError;

    /// Constructor
    UartStreamBase( const Str& filename, Baudrate baudrate, const double timeoutSec,
                    LoggerBase& logger, size_t bufferSize = 1, bool blocking = false, bool debug = false );

    /// Destructor
    ~UartStreamBase();

    /// Returns uart name
    const char* getName()
    {
        return uartIdToString( uartId_ );
    }

    /// Returns uart device name
    const char* getDevice()
    {
        return uartToDevice( uartId_ );
    }

    UartStreamBase& open()
    {
        return openAtBaudrate( defaultBaudrate_ );
    }

    UartStreamBase& openAtBaudrate( UartStreamBase::Baudrate baud );

    /// If length is unspecified, strlen is used to determine the length
    OutStream& write( const char* data, size_t length = 0xFFFFFFFF );

    /// Fills buffer until: buffer size is exceeded, CR, LF, or ETX character is received, or the serial timeout has expired
    inline UartStreamBase& readLine( char* buf, size_t bufsize )
    {
        return readLines( buf, bufsize, 1 );
    }

    /// Fills buffer with given number of lines (CR/LF) until buffer size is exceeded or timeout has expired or ETX character is received
    UartStreamBase& readLines( char* buf, size_t bufsize, size_t lines );

    // Fills buffer with given number of bytes unless timeout is exceeded
    InStream& read( char* buf, size_t num );

    // Fills buffer until character is matched unless timeout is exceeded
    // Do not read more than num bytes
    UartStreamBase& readUntil( char* buf, size_t num, const unsigned char match, bool flushUntil = false )
    {
        return readUntil( buf, num, ( const char* )&match, 1, flushUntil );
    }

    // Fills buffer until character string is matched unless timeout is exceeded
    // Do not read more than num bytes
    // If flushUntil is true, flush bytes up until match.
    UartStreamBase& readUntil( char* buf, size_t num, const char* match, unsigned int matchLen, bool flushUntil = false );

    // Fills buffer until character string is matched unless timeout is exceeded
    // Do not read more than num bytes
    // If flushUntil is true, flush bytes up until match.
    UartStreamBase& readUntil( char* buf, size_t num, const unsigned char* match, unsigned int matchLen, bool flushUntil = false );

    // Returns true if the buffer contains the indicated byte
    size_t canReadUntil( const unsigned char match );

    // Returns true if the buffer contains the indicated bytes
    size_t canReadUntil( const char* match,  int matchLen = 0,  int maxCheck = 0 );

    // Returns true if the buffer contains the indicated bytes
    size_t canReadUntil( const unsigned char* match,  int matchLen = 0,  int maxCheck = 0 );

    /// Flushes CR and LF characters out of the buffer until there are no more characters.
    /// May need to "push" a character back onto the stream if necessary.
    UartStreamBase& flushCRLF();

    /// Flushes any characters out of the buffer until there are no more characters,
    /// or if num is positive, until the specified number of characters are flushed.
    UartStreamBase& flush( int num = -1 );

    UartStreamBase& close();

    UartStreamBase& setStartTimeout( const double timeout )
    {
        startTimeout_ = timeout;
        return *this;
    }

    UartStreamBase& resetStartTimeout()
    {
        startTimeout_ = defaultStartTimeout_;
        return *this;
    }

    const char* uartIdToString( UartId uart );

    UartStreamBase::UartError getError()
    {
        return lastError_;
    }

    bool hasError()
    {
        return lastError_ != OK;
    }

    const char* errorString();

    virtual bool eof()
    {
        return isReadable();
    }

    virtual bool isReadable()
    {
        return ( serPortFd_ != -1 );
    }

    virtual bool isWritable()
    {
        return ( serPortFd_ != -1 );
    }

    virtual void waitForBufferEmpty()
    {}

    // Returns number of data bytes in the input buffer after polling the serial port
    size_t dataAvailable();

    // Returns number of data bytes in the input buffer
    size_t dataBuffered();

    void setDebug( bool debugOn )
    {
        debug_ = debugOn;
    }

    virtual void enableUART( void )
    {}

    virtual void disableUART( void )
    {}

    void fillInBuffer();

protected:

    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    UartStreamBase( const UartStreamBase& old ); // disallow copy constructor

    int serPortFd_;
    Str filename_;
    UartId uartId_;
    UartStreamBase::Baudrate defaultBaudrate_;
    Timespan startTimeout_;
    Timespan defaultStartTimeout_;
    Timespan betweenTimeout_;
    UartStreamBase::UartError lastError_;
    LoggerBase logger_;
    char pushedChar_;
    char* pushedChars_;
    size_t bufferSize_;
    int bufferPos_;
    bool blocking_;
    bool debug_;

    void serGetChar( unsigned char* c, int* success, const Timespan& timeout = Timespan::INVALID_TIMESPAN );
    virtual void initializePort( UartStreamBase::UartId )
    {}

    const char* uartToDevice( UartId uart );
    static UartId DeviceToUART( const Str& name );
    static const speed_t BaudToSpeed( Baudrate baud );
    static const Timespan SLEEP_TIME;

};

#endif /*UARTSTREAMBASE_H_*/

