#include "Stream.h"
#include <stdexcept>
#include <iostream>
#include <cstring>
#include <sys/socket.h>

Stream::Stream(size_t minSize)
{
    dataLength = getSize(minSize);
    data = new unsigned char[dataLength];
}

Stream::~Stream()
{
    delete[] data;
}

size_t Stream::getSize(size_t minSize)
{
    size_t size = 1;
    while(size < minSize && size != 0)
    {
        size <<= 1;
    }
    if(size == 0)
    {
        throw runtime_error("size exceeds max possible size");
    }
}

bool Stream::hasData() const
{
    return readIndex < writeIndex;
}

void Stream::resize(size_t minSize)
{
    size_t newSize = getSize(minSize);
    //cout << "resize from " << dataLength << " to " << newSize << endl;
    unsigned char* newData = new unsigned char[newSize];
    memcpy(newData, data, writeIndex);
    delete[] data;
    data = newData;
    dataLength = newSize;
}

void Stream::write(const void* writeData, size_t length)
{
    if(writeIndex + length > dataLength)
    {
        resize(writeIndex + length);
    }
    for(size_t i = 0; i < length; i++)
    {
        data[writeIndex] = ((unsigned char*) writeData)[i];
        writeIndex++;
    }
}

void Stream::writeChar(char c)
{
    write(&c, sizeof(char));
}

void Stream::writeShort(short s)
{
    write(&s, sizeof(short));
}

void Stream::writeInt(int i)
{
    write(&i, sizeof(int));
}

void Stream::writeLong(long l)
{
    write(&l, sizeof(long));
}

void Stream::writeUnsignedChar(unsigned char uc)
{
    write(&uc, sizeof(unsigned char));
}

void Stream::writeUnsignedShort(unsigned short us)
{
    write(&us, sizeof(unsigned short));
}

void Stream::writeUnsignedInt(unsigned int ui)
{
    write(&ui, sizeof(unsigned int));
}

void Stream::writeUnsignedLong(unsigned long ul)
{
    write(&ul, sizeof(unsigned long));
}

void Stream::read(void* buffer, size_t length)
{
    if(readIndex + length > writeIndex)
    {
        throw runtime_error("stream read over data length");
    }
    for(size_t i = 0; i < length; i++)
    {
        ((unsigned char*) buffer)[i] = data[readIndex];
        readIndex++;
    }
}

char Stream::readChar()
{
    char c;
    read(&c, sizeof(char));
    return c;
}

short Stream::readShort()
{
    short s;
    read(&s, sizeof(short));
    return s;
}

int Stream::readInt()
{
    int i;
    read(&i, sizeof(int));
    return i;
}

long Stream::readLong()
{
    long l;
    read(&l, sizeof(long));
    return l;
}

unsigned char Stream::readUnsignedChar()
{
    unsigned char uc;
    read(&uc, sizeof(unsigned char));
    return uc;
}

unsigned short Stream::readUnsignedShort()
{
    unsigned short us;
    read(&us, sizeof(unsigned short));
    return us;
}

unsigned int Stream::readUnsignedInt()
{
    unsigned int ui;
    read(&ui, sizeof(unsigned int));
    return ui;
}

unsigned long Stream::readUnsignedLong()
{
    unsigned long ul;
    read(&ul, sizeof(unsigned long));
    return ul;
}

void Stream::readSocket(int socket)
{
    writeIndex = 0;
    readIndex = 0;
    
    size_t bufferOffset = 0;
    size_t freeSpace = dataLength - writeIndex;
    
    while(true)
    {
        ssize_t readLength = recv(socket, data + bufferOffset, freeSpace, MSG_DONTWAIT);        
        if(readLength <= 0)
        {
            break;
        }
        writeIndex += readLength;
        if(readLength < freeSpace)
        {    
            break;
        }
        else
        {
            resize(dataLength + 1);
            freeSpace = dataLength - writeIndex;
            bufferOffset += readLength;
        }
    }
}

void Stream::sendToSocket(int socket)
{
    size_t bufferOffset = 0;
    size_t sendLength = writeIndex;
    
    while(sendLength > 0)
    {
        ssize_t writtenLength = send(socket, data + bufferOffset, sendLength, MSG_NOSIGNAL);
        if(writtenLength == -1)
        {
            perror("Cannot send data");
            return;
        }
        sendLength -= writtenLength;
        bufferOffset += writtenLength;
    }
}