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

/**
 *  \defgroup io Input/Output
 *
 *  Input and Output classes.
 *
 *  Part of an attempt to avoid the
 *  standard template library.  Also
 *  accommodates some of the oddities
 *  of serial communications on the LPC3XXX.
 *
 */

#ifndef OUTSTREAM_H_
#define OUTSTREAM_H_

#include "utils/Str.h"
#include <cstdio>
/**
 *  This is a very abstract class that
 *  can be implemented by classes that
 *  want to send stream output.
 *
 *  Provides basic input functions such as write(),
 *  as well as specialized functions for reading
 *  in data formats such as writeCharHex, etc.
 *
 *  Part of an attempt to avoid the
 *  standard template library
 *
 *  \ingroup io
 */
class OutStream
{
public:

    /// Destructor
    virtual ~OutStream()
    {}
    ;

    /// writes num bytes from buffer to the output buffer/stream
    virtual OutStream& write( const char* buffer, size_t num ) = 0;

    /// writes num bytes from buffer to the output buffer/stream as hex chars
    virtual OutStream& writeHex( const char* buffer, size_t num );

    /// indicates whether the stream can actually be written to.
    virtual bool isWritable() = 0;

    /// writes zero-terminated buffer to the output buffer/stream
    virtual OutStream& write( const char* buffer )
    {
        return write( buffer, strlen( buffer ) );
    }

    /// writes zero-terminated buffer to the output buffer/stream
    virtual OutStream& writeHex( const char* buffer )
    {
        return writeHex( buffer, strlen( buffer ) );
    }

    /// write a Str, using stream operator
    OutStream& operator << ( const Str buffer )
    {
        return write( buffer.cStr(), buffer.length() );
    };

    /// write a Str, using stream operator
    OutStream& operator << ( const char buffer[] )
    {
        return write( buffer );
    }

    /// write a Str, using stream operator
    OutStream& operator << ( const char byte )
    {
        return writeChar( byte );
    }

    /// write a Str, using stream operator
    OutStream& operator << ( const signed char byte )
    {
        return writeChar( byte );
    }

    /// write a Str, using stream operator
    OutStream& operator << ( const unsigned char byte )
    {
        return writeChar( byte );
    }

    /// write a Str, using stream operator
    OutStream& operator << ( const int number )
    {
        return writeInteger( number );
    }

    /// writes a single byte to the output buffer/stream
    OutStream& writeChar( const signed char value )
    {
        return write( ( char* )&value, sizeof( value ) );
    }

    /// writes a single signed char to the output buffer/stream as 2 hex chars
    OutStream& writeCharHex( const signed char byte );

    /// writes a single unsigned char to the output buffer/stream as 2 hex chars
    OutStream& writeUCharHex( const unsigned char byte );

    /// writes an integer as a base-radix string
    OutStream& writeInteger( const int number, const unsigned int radix = 10 );
    OutStream& writeNumber( const double number, const unsigned int radix = 10 );

    /// writes a single short to the output buffer/stream
    OutStream& writeShort( const short value )
    {
        return write( ( const char* )&value, sizeof( value ) );
    }

    /// writes a single short to the output buffer/stream as 4 hex chars
    OutStream& writeShortHex( const short word );

    /// writes a single unsigned short to the output buffer/stream as 4 hex chars
    OutStream& writeUShortHex( const unsigned short word );

    /// writes an unsigned short integer as 1 to 3 bytes,
    /// encoded as follows:
    /// xxxxxxx0 - 7 bit value, encoded in 1 byte
    /// xxxxxx01+1byte - 14 bit value, encoded in 2 bytes
    /// xxxxx011+2bytes - 16 bit value, encoded in 3 bytes
    OutStream& writeUShortCompact( const unsigned short& value );

    /// writes a 3-byte integer to the output buffer/stream
    /// only works on little-endian machines!
    OutStream& writeInt24( const int medInt )
    {
        return write( ( const char* )&medInt, 3 );
    }

    /// writes a 4-byte integer to the output buffer/stream
    OutStream& writeInt( const int value )
    {
        return write( ( const char* )&value, sizeof( value ) );
    }

    /// writes a 8-byte integer to the output buffer/stream
    OutStream& writeLongLong( const long long value )
    {
        return write( ( const char* )&value, sizeof( value ) );
    }

    /// writes a signed long long integer as 1 to 9 bytes,
    /// encoded as follows:
    /// 0xxxxxxx - 7 bit value, encoded in 1 byte
    /// 10xxxxxx+1byte - 14 bit value, encoded in 2 bytes
    /// 110xxxxx+2bytes - 21 bit value, encoded in 3 bytes
    /// 1110xxxx+3bytes - 28 bit value, encoded in 4 bytes
    /// 11110xxx+4bytes - 35 bit value, encoded in 5 bytes
    /// 111110xx+5bytes - 42 bit value, encoded in 6 bytes
    /// 1111110x+6bytes - 49 bit value, encoded in 7 bytes
    /// 11111110+7bytes - 56 bit value, encoded in 8 bytes
    /// 11111111+8bytes - 64 bit value, encoded in 9 bytes
    OutStream& writeLongLongCompact( const long long& value );

    /// writes a 4-byte float to the output buffer/stream
    OutStream& writeFloat( const float value )
    {
        return write( ( const char* )&value, sizeof( value ) );
    }

    /// writes a 2-byte float to the output buffer/stream
    OutStream& writeFloat2( const float value )
    {
        return write( ( ( const char* )&value ) + 2, 2 );
    }

    /// writes a 3-byte float to the output buffer/stream
    OutStream& writeFloat3( const float value )
    {
        return write( ( ( const char* )&value ) + 1, 3 );
    }

    /// writes a 8-byte double to the output buffer/stream
    OutStream& writeDouble( const double value )
    {
        return write( ( const char* )&value, sizeof( value ) );
    }

    OutStream& writeDouble6( const double value )
    {
        return write( ( ( const char* )&value ) + 2, 6 );
    }

    size_t bytesWritten()
    {
        return lastWritten_;
    }

protected:

    /// Protected Constructor for abstract class
    OutStream()
        : lastWritten_( 0 )
    {}
    ;

    size_t lastWritten_;

    static const unsigned char HEX_CHARS[];

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

};

class NullOutStream: public OutStream
{
public:

    /// doesn't write num bytes from buffer to the output buffer/stream
    virtual OutStream& write( const char* buffer, unsigned int num )
    {
        return *this;
    }

    /// indicates that the stream can't actually be written to.
    virtual bool isWritable()
    {
        return false;
    }

};


#endif /*OUTSTREAM_H_*/
