/*
 *  ARC++
 *
 *  Copyright (c) 2007-2008 Steven Noonan.
 *
 *  Licensed under the New BSD License.
 *
 */

#include "universal_include.h"

#include "Network/packet.h"

#include <zlib.h>

Packet::Packet()
{
    m_bufferIndex = 0;
    m_serializedBuffer = NULL;
    m_size = m_serializedSize = -1;
}

Packet::Packet ( const char *_serializedData, size_t _dataLength )
{
    m_bufferIndex = 0;
    m_serializedBuffer = NULL;
    m_size = m_serializedSize = -1;

    m_serializedSize = (int)_dataLength;
    m_serializedBuffer = new char[_dataLength];
    memcpy ( m_serializedBuffer, _serializedData, _dataLength );

    // Now deserialize it.
    Deserialize();

}

Packet::~Packet()
{
    for ( size_t i = 0; i < m_buffer.size(); i++ )
    {
        if ( m_buffer.valid ( i ) )
        {
            delete m_buffer[i];
            m_buffer.remove ( i );
        }
    }
    CacheDamaged();
}

void Packet::CacheDamaged()
{
    m_size = -1;
    delete [] m_serializedBuffer;
    m_serializedBuffer = NULL;
}

void Packet::AddBufferData ( const void *_data, size_t _length )
{
    ARCReleaseAssert ( _data != NULL );
    ARCReleaseAssert ( _length > 0 );
    CacheDamaged();
    m_buffer.insert ( new DataBuffer ( _data, _length ) );
}

void Packet::AddBufferString ( const char *_string )
{
    ARCReleaseAssert ( _string != NULL );
    m_size = -1;
    CacheDamaged();
    m_buffer.insert ( new DataBuffer ( _string ) );
}

int Packet::GetBufferData ( void *_data, size_t _length ) const
{
    DataBuffer *buffer = m_buffer[m_bufferIndex++];
    if ( buffer->getSize() != _length )
        throw m_bufferIndex - 1;
    memcpy ( _data, buffer->getData(), _length );
    return 0;
}

int Packet::GetBufferString ( char *&_string, size_t &_length ) const
{
    DataBuffer *buffer = m_buffer[m_bufferIndex++];
    _length = buffer->getSize();
    if ( _length > 1024 )
        throw m_bufferIndex - 1;
    _string = new char[_length+1];
    strncpy ( _string, buffer->getData(), _length );
    _string[_length] = 0;
    return 0;
}

size_t Packet::Size() const
{
    // Is the size cached?
    if ( m_size != -1 ) return m_size;

    // Nope, let's iterate through and find the byte size.
    size_t totalSize = 0;
    for ( size_t i = 0; i < m_buffer.size(); i++ )
        if ( m_buffer.valid ( i ) )
            totalSize += m_buffer.get(i)->getSize();

    m_size = (int)totalSize;

    return totalSize;
}

size_t Packet::SerializedSize() const
{
    // Is the size cached?
    if ( m_serializedSize != -1 ) return m_serializedSize;

    // Nope, let's iterate through and find the byte size.
    size_t totalSize = 0;
    for ( size_t i = 0; i < m_buffer.size(); i++ )
    {
        if ( !m_buffer.valid ( i ) ) continue;
        DataBuffer *buffer = m_buffer.get(i);
        totalSize += buffer->getSize() + 2;
        if ( buffer->isString() ) totalSize--;
    }

    m_serializedSize = (int)totalSize + 5;

    return m_serializedSize;
}

void Packet::Deserialize ()
{
    short dataSize;
    char *ptr = m_serializedBuffer;
    while ( ptr - m_serializedBuffer < m_serializedSize )
    {
        memcpy ( &dataSize, ptr, sizeof ( dataSize ) );
        ARCReleaseAssert ( dataSize > 0 && dataSize < 16384 );
        ptr += sizeof ( dataSize );
        m_buffer.insert ( new DataBuffer ( ptr, dataSize ) );
        ptr += dataSize;
    }
}

void Packet::Serialize ( const char *&_data, size_t &_dataSize ) const
{

    _dataSize = SerializedSize();

    if ( m_serializedBuffer != NULL ) {
        _data = m_serializedBuffer;
        return;
    }

    m_serializedBuffer = new char[SerializedSize()];

    char *ptr = m_serializedBuffer;
    unsigned short dataSize;

    dataSize = SerializedSize() - 2;
    memcpy ( ptr, &dataSize, sizeof(dataSize) );
    ptr += sizeof(dataSize);

    for ( size_t i = 0; i < m_buffer.size(); i++ )
    {
        if ( !m_buffer.valid(i) ) continue;
        DataBuffer *buf = m_buffer.get(i);
        ARCReleaseAssert ( buf != NULL );
        ARCReleaseAssert ( buf->getData() != NULL );
        ARCReleaseAssert ( buf->getSize() > 0 );
        dataSize = (short)buf->getSize();
        if ( buf->isString() ) dataSize--;
        memcpy ( ptr, &dataSize, sizeof(dataSize) );
        ptr += sizeof(dataSize);
        memcpy ( ptr, buf->getData(), dataSize );
        ptr += dataSize;
    }

    unsigned char bufEnd = '\3';
    dataSize = 1;
    memcpy ( ptr, &dataSize, sizeof(dataSize) );
    ptr += sizeof(dataSize);
    memcpy ( ptr, &bufEnd, 1 );
    ptr += dataSize;

    _data = m_serializedBuffer;
}
