/*
 * 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.
 */

#ifndef BU_BLOB_BUILDER_H
#define BU_BLOB_BUILDER_H

#include "bu/config.h"
#include "bu/sharedcore.h"

namespace Bu
{
    class Blob;
    class BlobBuilder;

    /** @cond DEVEL */
    class BlobBuilderCore
    {
    friend class BlobBuilder;
    friend class SharedCore<BlobBuilder, BlobBuilderCore>;
    private:
        class Chunk
        {
        public:
            Chunk( const char *pSrc, int32_t iLength );
            ~Chunk();

            void append( const char *&pSrc, int32_t &iLength );

            /**
             * Splits this chunk into two chunks, and fixes the links.
             *@param iIndex The first byte to be in the new chunk, if this is
             * zero or less then it has no effect.
             *@returns A pointer to the new chunk, or null if no work was
             * done.
             */
            Chunk *split( int32_t iIndex );

            int32_t iLength;
            char *pData;
            Chunk *pNext;
        };

        BlobBuilderCore();
        BlobBuilderCore( const Bu::Blob &rSrc );
        BlobBuilderCore( const BlobBuilderCore &rSrc );
        virtual ~BlobBuilderCore();

        void clear();
        void append( const char *pSrc, int32_t iLength );
        void prepend( const char *pSrc, int32_t iLength );
        void insert( int32_t iBefore, const char *pSrc, int32_t iLength );
        void set( const char *pSrc, int32_t iLength );
        void copyTo( void *pDestRaw, int32_t iLength ) const;
        char getAt( int32_t iIndex ) const;

        Chunk *pFirst;
        Chunk *pLast;
        int32_t iLength;
    };
    /** @endcond */

    /**
     * This makes creating a Blob piece at a time easy and fast.  You can
     * append, prepend, or insert with reasonable efficiency. The underlying
     * data structure is currently a linked list, and the individual links are
     * actually block allocated memory. If an amount of data is appended that
     * is less than the minimum page size then the minimum page size is
     * allocated. Subsequent appends will share that allocated buffer until it
     * is full, at which point a new buffer will be created.  If an amount
     * greater than the minimum page size is appended then any extra space in
     * the last buffer is used, and then the rest is placed in a single buffer.
     * At most one buffer will be allocated for any given append or prepend
     * operation.  Due to the nature of insert, up to two buffers could be
     * allocated.
     *
     * Insert operations will take O(N) time to find the location to insert to
     * in the linked list, at that point the foud chunk will be split into two
     * and the new data will be added at the new location. This will also
     * attempt to share the existing buffers, but if there's extra it will
     * allocate a new buffer for the remaining data.
     */
    class BlobBuilder : public Bu::SharedCore<BlobBuilder, BlobBuilderCore>
    {
    protected:
        using SharedCore<BlobBuilder, BlobBuilderCore>::core;
        using SharedCore<BlobBuilder, BlobBuilderCore>::_hardCopy;

    public:
        BlobBuilder();
        BlobBuilder( const Blob &rSrc );
        BlobBuilder( const BlobBuilder &rSrc );
        virtual ~BlobBuilder();

        void set( const Blob &rSrc );
        void set( const char *pSrc, int32_t iLength );
        void set( const char *pSrc );
        void append( const Blob &rSrc );
        void append( const char *pSrc, int32_t iLength );
        void append( const char *pSrc );
        void prepend( const Blob &rSrc );
        void prepend( const char *pSrc, int32_t iLength );
        void prepend( const char *pSrc );
        void insert( int32_t iBefore, const Blob &rSrc );
        void insert( int32_t iBefore, const char *pSrc, int32_t iLength );
        void insert( int32_t iBefore, const char *pSrc );
        void clear();

        int32_t getSize() const;
        Blob getBlob() const;
        void copyTo( void *pDestRaw, int32_t iDestSize ) const;
        char operator[]( int32_t iIndex ) const;

        BlobBuilder &operator=( const Blob &rSrc );
        BlobBuilder &operator=( const char *pSrc );
        BlobBuilder &operator=( const char chr );
        BlobBuilder &operator+=( const Blob &rSrc );
        BlobBuilder &operator+=( const char *pSrc );
        BlobBuilder &operator+=( const char chr );
        BlobBuilder &operator<<( const Blob &rSrc );
        BlobBuilder &operator<<( const char *pSrc );
        BlobBuilder &operator<<( const char chr );
    private:
    };
};

#endif
