/** \file
 *
 *  Contains the InStream and NullInStream class declarations.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#ifndef INSTREAM_H_
#define INSTREAM_H_

#include "utils/Str.h"

/**
 *  This is a very abstract class that
 *  can be implemented by classes that
 *  want to receive stream input.
 *
 *  Provides basic input functions such as read(),
 *  as well as specialized functions for reading
 *  in data formats such as readCharHex, etc.
 *
 *  Part of an attempt to avoid the
 *  standard template library
 *
 *  \ingroup io
 */
class InStream
{
public:

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

    /// reads num bytes from stream to the buffer
    virtual InStream& read( char* buffer, size_t num ) = 0;


    /// returns name of the stream
    virtual const char* getName() = 0;

    /// indicates whether the stream can actually be read from.
    virtual bool isReadable() = 0;

    /// reads a single byte to the input buffer/stream
    InStream& readChar( signed char& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads a single unsigned char from the input buffer/stream as 2 hex chars
    InStream& readCharHex( signed char& byte );

    /// reads a single unsigned char from the input buffer/stream as 2 hex chars
    InStream& readUCharHex( unsigned char& byte );

    // Fills buffer until: buffer size is exceeded, or (CR/LF) character is received
    // Leading LF characters are skipped.
    virtual InStream& readLine( char* buf, unsigned int bufsize );

    /// reads a single short from the input buffer/stream
    InStream& readShort( short& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads a single short from the input buffer/stream
    InStream& readShort( unsigned short& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads a single short from the input buffer/stream as 4 hex chars
    InStream& readShortHex( short& word );

    /// reads a single unsigned short from the input buffer/stream as 4 hex chars
    InStream& readUShortHex( unsigned short& value );

    /// reads 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
    InStream& readUShortCompact( unsigned short& value );

    /// reads a 3-byte integer to the input buffer/stream
    /// only works on little-endian machines!
    InStream& readInt24( int& medInt )
    {
        InStream& result = read( ( char* ) & medInt, 3 );
        if( ( medInt & 0x00800000 ) == 0 )
        {
            medInt &=  0x00FFFFFF;
        }
        else
        {
            medInt |=  0xFF000000;
        }
        return result;
    }

    /// reads a 4-byte integer to the input buffer/stream
    InStream& readInt( int& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads a 8-byte integer to the input buffer/stream
    InStream& readLongLong( long long& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads 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
    InStream& readLongLongCompact( long long& value );

    /// reads a 4-byte float to the input buffer/stream
    InStream& readFloat( float& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    /// reads a 2-byte float to the input buffer/stream
    InStream& readFloat2( float& value )
    {
        value = 0;
        return read( ( ( char* )&value ) + 2, 2 );
    }

    /// reads a 3-byte float to the input buffer/stream
    InStream& readFloat3( float& value )
    {
        value = 0;
        return read( ( ( char* )&value ) + 1, 3 );
    }

    /// reads a 8-byte double to the input buffer/stream
    InStream& readDouble( double& value )
    {
        return read( ( char* )&value, sizeof( value ) );
    }

    virtual InStream& readUntil( char* buf, unsigned int num, unsigned char match );

    /// Indicates if the end of file has been reached
    virtual bool eof() = 0;

    /// Indicates the number of bytes read in the last operation
    size_t bytesRead()
    {
        return lastRead_;
    }

    /// Indicates the number of bytes read in the last operation
    size_t getBytesRead()
    {
        return lastRead_;
    }

    /// Indicates the total number of bytes read;
    size_t getTotalBytesRead()
    {
        return totalBytesRead_;
    }

protected:

    /// Protected Constructor for abstract class
    InStream()
        : lastRead_( 0 ),
          totalBytesRead_( 0 )
    {}
    ;

    inline void zeroBytesRead()
    {
        lastRead_ = 0;
    }

    inline void incBytesRead()
    {
        ++lastRead_;
        ++totalBytesRead_;
    }

    inline void incBytesRead( size_t incRead )
    {
        lastRead_ += incRead;
        totalBytesRead_ += incRead;
    }

    inline void setBytesRead( size_t lastRead )
    {
        lastRead_ = lastRead;
        totalBytesRead_ += lastRead;
    }

    inline void zeroTotalBytesRead()
    {
        zeroBytesRead();
        totalBytesRead_ = 0;
    }

private:

    size_t lastRead_;

    size_t totalBytesRead_;

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

};

class NullInStream: public InStream
{
public:

    /// doesn't really read num bytes from stream to the buffer
    virtual InStream& read( char* buffer, unsigned int num )
    {
        return *this;
    }

    /// indicates that the stream can't actually be read from.
    virtual bool isReadable()
    {
        return false;
    }

    /// Indicates that the end of file has been reached
    virtual bool eof()
    {
        return true;
    }

};

#endif /*INSTREAM_H_*/
