#include "InMemoryBuffer.h"

#include "Either.h"
#include "Text.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;
}

//! Reads from the resource
//! \param bytes An array to be filled with bytes from the resource
//! \param len How many bytes should be read from the resource
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);
    }
}

//! Writes to the resource
//! \param bytes An array containing the bytes to be written to the resource
//! \param len How many bytes should be written to the resource
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 + 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);
}

//! Reads the next line of the resource
//! \return The read line as Text with line break
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(&c, 1);
    }
    return ret;
}

//! Checks whether the resource has been fully read
//!  return 1 if the resource has been fully read. 0 otherwise
bool InMemoryBuffer::istEnde() const
{
    return readPos < writePos;
}

//! Sets the position of the byte to be read next
//! \param pos The index of the byte
//! \param ende 1 if the index counts from the end of the resource. 0 if the index
//! counts from the beginning of the resource
void InMemoryBuffer::setLPosition(__int64 pos, bool ende)
{
    if (ende)
        readPos = MAX(0, maxWritePos - (int)pos);
    else
        readPos = MIN(maxWritePos, (int)pos);
}

//! Sets the position of the byte to be written next
//! \param pos The index of the byte
//! \param ende 1 if the index counts from the end of the resource. 0 if the index
//! counts from the beginning of the resource
void InMemoryBuffer::setSPosition(__int64 pos, bool ende)
{
    if (ende)
        writePos = MAX(0, maxWritePos - (int)pos);
    else
        writePos = MIN(maxWritePos, (int)pos);
}

//! Returns the index of the byte from the resource that would be read next
//! return -1 if an error occurred. Otherwise the position of the read pointer
__int64 InMemoryBuffer::getLPosition() const
{
    return readPos;
}

//! Returns the index of the byte from the resource that would be written next
//! return -1 if an error occurred. Otherwise the position of the write pointer
__int64 InMemoryBuffer::getSPosition() const
{
    return writePos;
}

//! Returns the number of bytes to be read
__int64 InMemoryBuffer::getSize() const
{
    return maxWritePos;
}