/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#include "bu/blob.h"
#include "bu/blobbuilder.h"
#include "bu/exceptioninvaliditerator.h"
#include "bu/exceptionindexoutofbounds.h"

// Temporary
#include "bu/string.h"

#include <string.h>

Bu::Blob::Blob() :
    pData( 0 ),
    iSize( 0 )
{
}

Bu::Blob::Blob( const Bu::Blob &rSrc ) :
    pData( 0 ),
    iSize( rSrc.iSize )
{
    pData = new char[rSrc.iSize+1];
    memcpy( pData, rSrc.pData, iSize+1 );
}

Bu::Blob::Blob( const class BlobBuilder &rSrc ) :
    pData( 0 ),
    iSize( 0 )
{
    iSize = rSrc.getSize();
    pData = new char[iSize+1];

    rSrc.copyTo( pData, iSize );
    pData[iSize] = '\0';
}

Bu::Blob::Blob( const char *pSrc ) :
    pData( 0 ),
    iSize( 0 )
{
    for(; iSize < __INT_MAX__ && pSrc[iSize]; iSize++ ) { }
    pData = new char[iSize+1];
    memcpy( pData, pSrc, iSize+1 );
}

Bu::Blob::Blob( const void *pSrc, int32_t iSize ) :
    pData( 0 ),
    iSize( iSize )
{
    pData = new char[iSize+1];
    memcpy( pData, pSrc, iSize );
    pData[iSize] = '\0';
}

Bu::Blob::Blob( int32_t iSize ) :
    pData( 0 ),
    iSize( iSize )
{
    pData = new char[iSize+1];
    memset( pData, iSize+1, 1 );
}

Bu::Blob::Blob( const Bu::Blob::const_iterator &iStart ) :
    pData( 0 ),
    iSize( 0 )
{
    if( !iStart )
        return;

    iSize = iStart.pBlob->iSize - iStart.iIndex;
    pData = new char[iSize+1];
    memcpy( pData, iStart.pBlob->pData+iStart.iIndex, iSize );
}

Bu::Blob::Blob( const Bu::Blob::const_iterator &iStart,
        const Bu::Blob::const_iterator &iEnd ) :
    pData( 0 ),
    iSize( 0 )
{
    if( !iStart )
        return;

    if( iEnd )
    {
        iSize = iEnd.iIndex - iStart.iIndex;
    }
    else
    {
        iSize = iStart.pBlob->iSize - iStart.iIndex;
    }
    
    pData = new char[iSize+1];
    memcpy( pData, iStart.pBlob->pData+iStart.iIndex, iSize );
    pData[iSize] = 0;
}

Bu::Blob::Blob( const Bu::String &rSrc ) :
    pData( 0 ),
    iSize( rSrc.getSize() )
{
    pData = new char[rSrc.getSize()+1];
    memcpy( pData, rSrc.getStr(), iSize+1 );
}

Bu::Blob::~Blob()
{
    delete[] pData;
    pData = 0;
    iSize = 0;
}

void Bu::Blob::setSize( int32_t iSize )
{
    delete[] pData;
    pData = new char[iSize+1];
    memset( pData, iSize, 1 );
    this->iSize = iSize;
}

int32_t Bu::Blob::getSize() const
{
    return iSize;
}

void Bu::Blob::setData( const char *pSrc, int32_t iSize )
{
    delete[] pData;
    pData = new char[iSize+1];
    memset( pData, iSize, 1 );
    this->iSize = iSize;
    memcpy( pData, pSrc, iSize );
}

char *Bu::Blob::getData() const
{
    return pData;
}

char *Bu::Blob::c_str() const
{
    return pData;
}

char &Bu::Blob::operator[]( int32_t iIndex )
{
    if( iIndex < 0 || iIndex >= iSize || pData == NULL )
        throw Bu::ExceptionIndexOutOfBounds();

    return pData[iIndex];
}

char Bu::Blob::operator[]( int32_t iIndex ) const
{
    if( iIndex < 0 || iIndex >= iSize || pData == NULL )
        throw Bu::ExceptionIndexOutOfBounds();

    return pData[iIndex];
}

bool Bu::Blob::isEmpty() const
{
    return pData != NULL && iSize == 0;
}

bool Bu::Blob::isNull() const
{
    return pData == NULL;
}

bool Bu::Blob::isNullOrEmpty() const
{
    return pData == NULL || iSize == 0;
}

Bu::Blob &Bu::Blob::operator=( const Bu::Blob &rRhs )
{
    delete[] pData;

    iSize = rRhs.iSize;
    pData = new char[iSize+1];
    memcpy( pData, rRhs.pData, iSize+1 );

    return *this;
}

Bu::Blob &Bu::Blob::operator=( const char *pRhs )
{
    delete[] pData;

    for(iSize = 0; iSize < __INT_MAX__ && pRhs[iSize]; iSize++ ) { }
    pData = new char[iSize+1];
    memcpy( pData, pRhs, iSize+1 );

    return *this;
}

bool Bu::Blob::operator==( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return true;

    if( iSize != rRhs.iSize )
        return false;

    for( int32_t j = 0; j < iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return false;
    }

    return true;
}

bool Bu::Blob::operator==( const char *pRhs ) const
{
    if( pData == pRhs )
        return true;

    for( int32_t j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
            return false;
    }

    if( pRhs[iSize] == '\0' )
        return true;

    return false;
}

bool Bu::Blob::operator!=( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return false;

    if( iSize != rRhs.iSize )
        return true;

    for( int32_t j = 0; j < iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return true;
    }

    return false;
}

bool Bu::Blob::operator!=( const char *pRhs ) const
{
    if( pData == pRhs )
        return false;

    int32_t j;
    for( j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
        {
            return true;
        }
    }

    if( !(iSize == j && pRhs[j] == '\0') )
    {
        return true;
    }

    return false;
}

bool Bu::Blob::operator<( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return false;

    for( int32_t j = 0; j < iSize && j < rRhs.iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return pData[j] < rRhs.pData[j];
    }

    if( iSize < rRhs.iSize )
        return true;

    return false;
}

bool Bu::Blob::operator<( const char *pRhs ) const
{
    if( pData == pRhs )
        return false;

    for( int32_t j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
            return pData[j] < pRhs[j];
    }

    if( pRhs[iSize] == '\0' )
        return false;

    return true;
}

bool Bu::Blob::operator<=( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return true;

    for( int32_t j = 0; j < iSize && j < rRhs.iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return pData[j] < rRhs.pData[j];
    }

    return iSize <= rRhs.iSize;
}

bool Bu::Blob::operator<=( const char *pRhs ) const
{
    if( pData == pRhs )
        return true;

    for( int32_t j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
            return pData[j] < pRhs[j];
    }

    if( pRhs[iSize] == '\0' )
        return true;

    return true;
}

bool Bu::Blob::operator>( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return false;

    for( int32_t j = 0; j < iSize && j < rRhs.iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return pData[j] > rRhs.pData[j];
    }

    if( iSize > rRhs.iSize )
        return true;

    return false;
}

bool Bu::Blob::operator>( const char *pRhs ) const
{
    if( pData == pRhs )
        return false;

    int32_t j;
    for( j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
            return pData[j] > pRhs[j];
    }

    if( pRhs[j] == '\0' && iSize-1 > j )
        return true;

    return false;
}

bool Bu::Blob::operator>=( const Bu::Blob &rRhs ) const
{
    if( pData == rRhs.pData )
        return true;

    for( int32_t j = 0; j < iSize && j < rRhs.iSize; j++ )
    {
        if( pData[j] != rRhs.pData[j] )
            return pData[j] > rRhs.pData[j];
    }

    return iSize >= rRhs.iSize;
}

bool Bu::Blob::operator>=( const char *pRhs ) const
{
    if( pData == pRhs )
        return true;

    int32_t j;
    for( j = 0; j < iSize && pRhs[j]; j++ )
    {
        if( pData[j] != pRhs[j] )
            return pData[j] > pRhs[j];
    }

    if( pRhs[j] == '\0' )
        return true;

    return true;
}

Bu::Blob::iterator Bu::Blob::begin()
{
    return iterator( this, true );
}

Bu::Blob::const_iterator Bu::Blob::begin() const
{
    return const_iterator( this, true );
}

Bu::Blob::iterator Bu::Blob::end()
{
    return iterator();
}

Bu::Blob::const_iterator Bu::Blob::end() const
{
    return const_iterator();
}

Bu::Blob::iterator Bu::Blob::rbegin()
{
    return iterator( this, false );
}

Bu::Blob::const_iterator Bu::Blob::rbegin() const
{
    return const_iterator( this, false );
}

Bu::Blob::iterator Bu::Blob::rend()
{
    return iterator();
}

Bu::Blob::const_iterator Bu::Blob::rend() const
{
    return const_iterator();
}

/////
// Iterators
//

Bu::Blob::iterator::iterator( Bu::Blob *pBlob, bool bForward ) :
    pBlob( pBlob ),
    iIndex( bForward?0:pBlob->iSize-1 ),
    bForward( bForward )
{
}

Bu::Blob::iterator::iterator( Bu::Blob *pBlob, int32_t iIndex, bool bForward ) :
    pBlob( pBlob ),
    iIndex( iIndex ),
    bForward( bForward )
{
}

Bu::Blob::iterator::iterator() :
    pBlob( NULL ),
    iIndex( -1 ),
    bForward( true )
{
}

Bu::Blob::iterator::iterator( const Bu::Blob::iterator &rSrc ) :
    pBlob( rSrc.pBlob ),
    iIndex( rSrc.iIndex ),
    bForward( rSrc.bForward )
{
}

bool Bu::Blob::iterator::isValid() const
{
    if( !pBlob )
        return false;

    if( iIndex < 0 || iIndex >= pBlob->iSize )
        return false;

    return true;
}

Bu::Blob::iterator::operator bool() const
{
    return isValid();
}

char &Bu::Blob::iterator::operator *()
{
    if( !isValid() )
        throw Bu::ExceptionInvalidIterator();

    return pBlob->pData[iIndex];
}

Bu::Blob::iterator &Bu::Blob::iterator::operator++( int32_t )
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator++()
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator--( int32_t )
{
    if( bForward )
        --iIndex;
    else
        ++iIndex;

    return *this;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator--()
{
    if( bForward )
        --iIndex;
    else
        ++iIndex;

    return *this;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator+=( int32_t iDelta )
{
    if( bForward )
        iIndex += iDelta;
    else
        iIndex -= iDelta;

    return *this;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator-=( int32_t iDelta )
{
    if( bForward )
        iIndex -= iDelta;
    else
        iIndex += iDelta;

    return *this;
}

Bu::Blob::iterator Bu::Blob::iterator::operator+( int32_t iDelta ) const
{
    return iterator( pBlob, iIndex + iDelta, bForward );
}

Bu::Blob::iterator Bu::Blob::iterator::operator-( int32_t iDelta ) const
{
    return iterator( pBlob, iIndex - iDelta, bForward );
}

bool Bu::Blob::iterator::operator==( const Bu::Blob::iterator &rRhs )
{
    if( isValid() == false && rRhs.isValid() == false )
        return true;

    return pBlob == rRhs.pBlob && iIndex == rRhs.iIndex;
}

bool Bu::Blob::iterator::operator==( const Bu::Blob::const_iterator &rRhs )
{
    if( isValid() == false && rRhs.isValid() == false )
        return true;

    return pBlob == rRhs.pBlob && iIndex == rRhs.iIndex;
}

bool Bu::Blob::iterator::operator!=( const Bu::Blob::iterator &rRhs )
{
    if( isValid() != rRhs.isValid() )
        return true;

    return pBlob != rRhs.pBlob || iIndex != rRhs.iIndex;
}

bool Bu::Blob::iterator::operator!=( const Bu::Blob::const_iterator &rRhs )
{
    if( isValid() != rRhs.isValid() )
        return true;

    return pBlob != rRhs.pBlob || iIndex != rRhs.iIndex;
}

Bu::Blob::iterator &Bu::Blob::iterator::operator=(
        const Bu::Blob::iterator &rRhs )
{
    pBlob = rRhs.pBlob;
    iIndex = rRhs.iIndex;
    bForward = rRhs.bForward;

    return *this;
}


Bu::Blob::const_iterator::const_iterator( const Blob *pBlob, bool bForward ) :
    pBlob( pBlob ),
    iIndex( bForward?0:pBlob->iSize-1 ),
    bForward( bForward )
{
}
Bu::Blob::const_iterator::const_iterator( const Blob *pBlob, int32_t iIndex,
        bool bForward ) :
    pBlob( pBlob ),
    iIndex( iIndex ),
    bForward( bForward )
{
}

Bu::Blob::const_iterator::const_iterator() :
    pBlob( NULL ),
    iIndex( -1 ),
    bForward( true )
{
}

Bu::Blob::const_iterator::const_iterator(
        const Bu::Blob::const_iterator &rSrc ) :
    pBlob( rSrc.pBlob ),
    iIndex( rSrc.iIndex ),
    bForward( rSrc.bForward )
{
}

Bu::Blob::const_iterator::const_iterator( const Bu::Blob::iterator &rSrc ) :
    pBlob( rSrc.pBlob ),
    iIndex( rSrc.iIndex ),
    bForward( rSrc.bForward )
{
}

bool Bu::Blob::const_iterator::isValid() const
{
    if( !pBlob )
        return false;

    if( iIndex < 0 || iIndex >= pBlob->iSize )
        return false;

    return true;
}

Bu::Blob::const_iterator::operator bool() const
{
    return isValid();
}

char Bu::Blob::const_iterator::operator *() const
{
    if( !isValid() )
        throw Bu::ExceptionInvalidIterator();

    return pBlob->pData[iIndex];
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator++( int )
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator++()
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator--( int )
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator--()
{
    if( bForward )
        ++iIndex;
    else
        --iIndex;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator+=( int32_t iDelta )
{
    if( bForward )
        iIndex += iDelta;
    else
        iIndex -= iDelta;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator-=( int32_t iDelta )
{
    if( bForward )
        iIndex -= iDelta;
    else
        iIndex += iDelta;

    return *this;
}

Bu::Blob::const_iterator Bu::Blob::const_iterator::operator+( int32_t iDelta )
    const
{
    return const_iterator( pBlob, iIndex + iDelta, bForward );
}

Bu::Blob::const_iterator Bu::Blob::const_iterator::operator-( int32_t iDelta )
    const
{
    return const_iterator( pBlob, iIndex - iDelta, bForward );
}

bool Bu::Blob::const_iterator::operator==( const Bu::Blob::iterator &rRhs )
{
    if( isValid() == false && rRhs.isValid() == false )
        return true;

    return pBlob == rRhs.pBlob && iIndex == rRhs.iIndex;
}

bool Bu::Blob::const_iterator::operator==(
        const Bu::Blob::const_iterator &rRhs )
{
    if( isValid() == false && rRhs.isValid() == false )
        return true;

    return pBlob == rRhs.pBlob && iIndex == rRhs.iIndex;
}

bool Bu::Blob::const_iterator::operator!=( const Bu::Blob::iterator &rRhs )
{
    if( isValid() != rRhs.isValid() )
        return true;

    return pBlob != rRhs.pBlob || iIndex != rRhs.iIndex;
}

bool Bu::Blob::const_iterator::operator!=(
        const Bu::Blob::const_iterator &rRhs )
{
    if( isValid() != rRhs.isValid() )
        return true;

    return pBlob != rRhs.pBlob || iIndex != rRhs.iIndex;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator=(
        const Bu::Blob::iterator &rRhs )
{
    pBlob = rRhs.pBlob;
    iIndex = rRhs.iIndex;
    bForward = rRhs.bForward;

    return *this;
}

Bu::Blob::const_iterator &Bu::Blob::const_iterator::operator=(
        const Bu::Blob::const_iterator &rRhs )
{
    pBlob = rRhs.pBlob;
    iIndex = rRhs.iIndex;
    bForward = rRhs.bForward;

    return *this;
}

/////
// Helper functions
//

template<> uint32_t Bu::__calcHashCode<Bu::Blob>( const Bu::Blob &k )
{
    int32_t sz = k.getSize();
    const char *s = k.getData();

    uint32_t iPos = 0;
    for( int32_t j = 0; j < sz; j++, s++ )
    {
        iPos = *s + (iPos << 6) + (iPos << 16) - iPos;
    }

    return iPos;
}

template<> bool Bu::__cmpHashKeys<Bu::Blob>(
        const Bu::Blob &a, const Bu::Blob &b )
{
    return a == b;
}

#include <stdio.h>
template<> void Bu::__tracer_format<Bu::Blob>( const Bu::Blob &v )
{
    printf("(%d)\"%s\"", v.getSize(), v.getData() );
}

#include "bu/formatter.h"
Bu::Formatter &Bu::operator<<( Bu::Formatter &rOut, const Bu::Blob &b )
{
    rOut.writeAligned( b.getData(), b.getSize() );
    return rOut;
}

Bu::Formatter &Bu::operator>>( Bu::Formatter &fIn, Bu::Blob &b )
{
    Bu::String sTmp = fIn.readToken();
    b.setData( sTmp.getStr(), sTmp.getSize() );
    return fIn;
}

Bu::ArchiveBase &Bu::operator<<( Bu::ArchiveBase &ar, const Bu::Blob &b )
{
    int32_t iSize = b.getSize();
    ar << iSize;
    ar.write( b.getData(), iSize );
    return ar;
}

Bu::ArchiveBase &Bu::operator>>( Bu::ArchiveBase &ar, Bu::Blob &b )
{
    int32_t iSize;
    ar >> iSize;
    b.setSize( iSize );
    ar.read( b.getData(), iSize );
    return ar;
}

