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

#include "InStream.h"
#include <cstdio>
#include <cstdlib>

/// reads a single unsigned char to the input buffer/stream as 2 hex chars
InStream& InStream::readCharHex( signed char& value )
{
    char chars[3];
    read( chars, 2 );
    chars[2] = 0;
    value = strtol( chars, NULL, 16 );
    return *this;
}

InStream& InStream::readUCharHex( unsigned char& value )
{
    char chars[3];
    read( chars, 2 );
    chars[2] = 0;
    value = strtol( chars, NULL, 16 );
    return *this;
}

InStream& InStream::readLine( char* buf, unsigned int bufSize )
{
    unsigned int i = 0;
    for( ; i < bufSize - 1 && !eof(); ++i )
    {
        if( read( buf + i, 1 ).bytesRead() > 0 )
        {
            if( 0xD == buf[i] || 0xA == buf[i] )
            {
                if( i == 0 && 0xA == *buf && bufSize > 1 )
                {
                    --buf;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            break;
        }
    }
    if( bufSize > 0 )
    {
        buf[i] = 0;
    }
    lastRead_ = i;
    return *this;
}

/// reads a single short to the input buffer/stream as 4 hex chars
InStream& InStream::readShortHex( short& value )
{
    char chars[5];
    read( chars, 4 );
    chars[4] = 0;
    value = strtol( chars, NULL, 16 );
    return *this;
}

/// reads a single unsigned short to the input buffer/stream as 4 hex chars
InStream& InStream::readUShortHex( unsigned short& value )
{
    char chars[5];
    read( chars, 4 );
    chars[4] = 0;
    value = strtol( chars, NULL, 16 );
    return *this;
}

/// 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& InStream::readUShortCompact( unsigned short& value )
{
    //if(1==1) return read(( char* )&value, sizeof( value ) ).gcount() == sizeof( value );
    signed char firstChar;
    if( readChar( firstChar ).bytesRead() == 1 )
    {
        //printf("theChar & 0x01 = %d, theChar & 0x02 = %d\n", firstChar & 0x01, firstChar & 0x02);
        if( ( firstChar & 0x01 ) == 0 )
        {
            value = ( ( unsigned char )firstChar ) >> 1;
            ++lastRead_;
        }
        else if( ( firstChar & 0x02 ) == 0 )
        {
            value = ( unsigned char )firstChar;
            if( read( ( ( char* )&value ) + 1, 1 ).bytesRead() == 1 )
            {
                value >>= 2;
                lastRead_ += 2;
            }
        }
        else if( read( ( char* )&value, 2 ).bytesRead() == 2 )
        {
            lastRead_ += 3;
        }
    }
    return *this;
}

/// reads a signed long long integer as 1 to 9 bytes,
/// encoded as follows:
/// xxxxxxx0 - 7 bit value, encoded in 1 byte
/// xxxxxx01+1byte - 14 bit value, encoded in 2 bytes
/// xxxxx011+2bytes - 21 bit value, encoded in 3 bytes
/// xxxx0111+3bytes - 28 bit value, encoded in 4 bytes
/// xxx01111+4bytes - 35 bit value, encoded in 5 bytes
/// xx011111+5bytes - 42 bit value, encoded in 6 bytes
/// x0111111+6bytes - 49 bit value, encoded in 7 bytes
/// 01111111+7bytes - 56 bit value, encoded in 8 bytes
/// 11111111+8bytes - 64 bit value, encoded in 9 bytes
InStream& InStream::readLongLongCompact( long long& value )
{
    //if(1 == 1) return read(( char* )&value, sizeof( value ) ).gcount() == sizeof( value );
    signed char firstChar;
    if( readChar( firstChar ).bytesRead() == 1 )
    {
        if( ( firstChar & 0x01 ) == 0 )
        {
            value = ( ( unsigned char )firstChar ) >> 1;
            if( value & 0x40LL )
            {
                value |= 0xFFFFFFFFFFFFFF80LL;
            }
            ++lastRead_;
        }
        else
        {
            value = ( unsigned char )firstChar;
            if( ( firstChar & 0x02 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 1 ).bytesRead() == 1 )
                {
                    value >>= 2;
                    if( value & 0x2000LL )
                    {
                        value |= 0xFFFFFFFFFFFFC000LL;
                    }
                    lastRead_ += 2;
                }
            }
            else if( ( firstChar & 0x04 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 2 ).bytesRead() == 2 )
                {
                    value >>= 3;
                    if( value & 0x100000LL )
                    {
                        value |= 0xFFFFFFFFFFE00000LL;
                    }
                    lastRead_ += 3;
                }
            }
            else if( ( firstChar & 0x08 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 3 ).bytesRead() == 3 )
                {
                    value >>= 4;
                    if( value & 0x8000000LL )
                    {
                        value |= 0xFFFFFFFFF0000000LL;
                    }
                    lastRead_ += 4;
                }
            }
            else if( ( firstChar & 0x10 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 4 ).bytesRead() == 4 )
                {
                    value >>= 5;
                    if( value & 0x400000000LL )
                    {
                        value |= 0xFFFFFFF800000000LL;
                    }
                    lastRead_ += 5;
                }
            }
            else if( ( firstChar & 0x20 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 5 ).bytesRead() == 5 )
                {
                    value >>= 6;
                    if( value & 0x20000000000LL )
                    {
                        value |= 0xFFFFFC0000000000LL;
                    }
                    lastRead_ += 6;
                }
            }
            else if( ( firstChar & 0x40 ) == 0 )
            {
                if( read( ( ( char* )&value ) + 1, 6 ).bytesRead() == 6 )
                {
                    value >>= 7;
                    if( value & 0x1000000000000LL )
                    {
                        value |= 0xFFFE000000000000LL;
                    }
                    lastRead_ += 7;
                }
            }
            else if( ( firstChar & 0x80 ) == 0 )
            {
                if( read( ( ( char* )&value ), 7 ).bytesRead() == 7 )
                {
                    if( value & 0x80000000000000LL )
                    {
                        value |= 0xFF00000000000000LL;
                    }
                    lastRead_ += 8;
                }
            }
            else if( read( ( char* )&value, 8 ).bytesRead() == 8 )
            {
                lastRead_ += 9;
            }
        }
    }
    return *this;
}

// Fills buffer with given number of bytes until character is matched
// Buffer should be at least num+1 bytes.
InStream& InStream::readUntil( char* buf, unsigned int num, unsigned char match )
{
    unsigned int bytesRead = 0;
    bool done( false );
    while( !done && num - bytesRead > 0 )
    {
        if( read( buf, 1 ).bytesRead() != 1
                || *buf == match )
        {
            done = true;
        }
        ++bytesRead;
        ++buf;
    }
    *buf = 0;
    lastRead_ = bytesRead;
    return *this;
}
