#include "InMemoryBuffer.h"
#include "Text.h"
#include "Either.h"

using namespace Framework;

InMemoryBuffer::InMemoryBuffer()
    : ReferenceCounter()
{
    buffer = new char *[ 1 ];
    buffer[ 0 ] = new char[ 256 ];
    numBuffers = 1;
    readPos = 0;
    writePos = 0;
    maxWritePos = 0;
}

InMemoryBuffer::~InMemoryBuffer()
{
    for( int i = 0; i < numBuffers; i++ )
        delete[] buffer[ i ];
    delete[]buffer;
}

//! Ließt aus der Datei
//! \param bytes Ein Array, der mit Bytes aus der Resource gefüllt werden soll
//! \param len Wie viele Bytes aus der Resource gelesen werden sollen
void InMemoryBuffer::lese( char *bytes, int len )
{
    if( readPos < writePos )
    {
        len = MIN( len, writePos - readPos );
        int readCount = 0;
        do
        {
            int curPos = readPos % 256;
            int tmp = MIN( len, 256 - curPos );
            memcpy( bytes + readCount, buffer[ readPos / 256 ] + curPos, tmp );
            readCount += tmp;
            readPos += tmp;
            len -= tmp;
        } while( len > 0 );
    }
}

//! Schreibt in die Resource
//! \param bytes Ein Array, der die Bytes enthält, welche in die Resource geschrieben werden soll
//! \param len Wie viele Bytes in die Resource geschrieben werden sollen
void InMemoryBuffer::schreibe( const char *bytes, int len )
{
    if( numBuffers * 256 < writePos + len )
    {
        int newSize = ( writePos + len ) / 256 + 1;
        char **newBuffer = new char *[ newSize ];
        memcpy( newBuffer, buffer, sizeof( char * ) * numBuffers );
        memset( newBuffer + sizeof( char * ) * numBuffers, 0, sizeof( char * ) * ( newSize - numBuffers ) );
        delete[] buffer;
        buffer = newBuffer;
        numBuffers = newSize;
    }
    int writeCount = 0;
    do
    {
        int curPos = writePos % 256;
        int tmp = MIN( len, 256 - curPos );
        if( !buffer[ writePos / 256 ] )
            buffer[ writePos / 256 ] = new char[ 256 ];
        memcpy( buffer[ writePos / 256 ] + curPos, bytes + writeCount, tmp );
        writeCount += tmp;
        writePos += tmp;
        len -= tmp;
    } while( len > 0 );
    maxWritePos = MAX( maxWritePos, writePos );
}

//! Ließt die nächste zeile der Resource ein
//! \return Die gelesene Zeile als Text mit zeilenumbruch
Text *InMemoryBuffer::leseZeile()
{
    if( istEnde() )
        return 0;
    Text *ret = new Text( "" );
    for( char c = 0; c != '\n' && readPos < writePos; )
    {
        lese( &c, 1 );
        if( c )
            ret->append( (const char *)&c, 1 );
    }
    return ret;
}

//! Prüft, ob die Resource vollständig gelesen wurde
//!  return 1, wenn die Resource vollständig gelesen wurde. 0, sonst
bool InMemoryBuffer::istEnde() const
{
    return readPos < writePos;
}

//! Setzt die Position des Bytes, das als nächstes gelesen werden soll
//! \param pos Der Index des Bytes
//! \param ende 1, wenn der Index vom ende der Resource zählt. 0, wenn der Index vom Beginn der Resource zählt
void InMemoryBuffer::setLPosition( __int64 pos, bool ende )
{
    if( ende )
        readPos = MAX( 0, maxWritePos - (int)pos );
    else
        readPos = MIN( maxWritePos, (int)pos );
}

//! Setzt die Position des Bytes, das als nächstes geschrieben werden soll
//! \param pos Der Index des Bytes
//! \param ende 1, wenn der Index vom ende der Resource zählt. 0, wenn der Index vom Beginn der Resource zählt
void InMemoryBuffer::setSPosition( __int64 pos, bool ende )
{
    if( ende )
        writePos = MAX( 0, maxWritePos - (int)pos );
    else
        writePos = MIN( maxWritePos, (int)pos );
}

//! Gibt den Index des Bytes aus der Resource zurück, welches als nächstes gelesen werden würde
//! return -1, falls ein Fehler aufgetreten ist. Sonst die Position des Lesezeigers
__int64 InMemoryBuffer::getLPosition() const
{
    return readPos;
}

//! Gibt den Index des Bytes aus der Resource zurück, welches als nächstes geschrieben werden würde
//! return -1, falls ein Fehler aufgetreten ist. Sonst die Position des Schreibzeigers
__int64 InMemoryBuffer::getSPosition() const
{
    return writePos;
}

//! Gibt die Anzahl der zu lesenden Bytes zurück
__int64 InMemoryBuffer::getSize() const
{
    return maxWritePos;
}