/*-------------------------------------------------------------------------
 * drawElements Memory Pool Library
 * --------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Memory pool management.
 *//*--------------------------------------------------------------------*/

#include "dePoolStringBuilder.h"

#include <string.h>
#include <stdarg.h>
#include <stdio.h>

typedef struct StringBlock_s
{
    const char *str;
    struct StringBlock_s *next;
} StringBlock;

struct dePoolStringBuilder_s
{
    deMemPool *pool;
    int length;
    StringBlock *blockListHead;
    StringBlock *blockListTail;
};

dePoolStringBuilder *dePoolStringBuilder_create(deMemPool *pool)
{
    dePoolStringBuilder *builder = DE_POOL_NEW(pool, dePoolStringBuilder);
    if (!builder)
        return DE_NULL;

    builder->pool          = pool;
    builder->length        = 0;
    builder->blockListHead = DE_NULL;
    builder->blockListTail = DE_NULL;

    return builder;
}

bool dePoolStringBuilder_appendString(dePoolStringBuilder *builder, const char *str)
{
    StringBlock *block = DE_POOL_NEW(builder->pool, StringBlock);
    size_t len         = strlen(str);
    char *blockStr     = (char *)deMemPool_alloc(builder->pool, len + 1);

    if (!block || !blockStr)
        return false;

    /* Initialize block. */
    {
        char *d       = blockStr;
        const char *s = str;
        while (*s)
            *d++ = *s++;
        *d = 0;

        block->str  = blockStr;
        block->next = DE_NULL;
    }

    /* Add block to list. */
    if (builder->blockListTail)
        builder->blockListTail->next = block;
    else
        builder->blockListHead = block;

    builder->blockListTail = block;

    builder->length += (int)len;

    return true;
}

bool dePoolStringBuilder_appendFormat(dePoolStringBuilder *builder, const char *format, ...)
{
    char buf[512];
    va_list args;
    bool ok;

    va_start(args, format);
    vsnprintf(buf, DE_LENGTH_OF_ARRAY(buf), format, args);
    ok = dePoolStringBuilder_appendString(builder, buf);
    va_end(args);

    return ok;
}

/* \todo [2009-09-05 petri] Other appends? printf style? */

int dePoolStringBuilder_getLength(dePoolStringBuilder *builder)
{
    return builder->length;
}

char *dePoolStringBuilder_dupToString(dePoolStringBuilder *builder)
{
    return dePoolStringBuilder_dupToPool(builder, builder->pool);
}

char *dePoolStringBuilder_dupToPool(dePoolStringBuilder *builder, deMemPool *pool)
{
    char *resultStr = (char *)deMemPool_alloc(pool, (size_t)builder->length + 1);

    if (resultStr)
    {
        StringBlock *block = builder->blockListHead;
        char *dstPtr       = resultStr;

        while (block)
        {
            const char *p = block->str;
            while (*p)
                *dstPtr++ = *p++;
            block = block->next;
        }

        *dstPtr++ = 0;

        DE_ASSERT((int)strlen(resultStr) == builder->length);
    }

    return resultStr;
}
