//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// geometry/IndexDataManager.cpp: Defines the IndexDataManager, a class that
// runs the Buffer translation process for index buffers.

#include "libGLESv2/geometry/IndexDataManager.h"

#include "common/debug.h"

#include "libGLESv2/Buffer.h"
#include "libGLESv2/geometry/backend.h"

namespace
{
    enum { INITIAL_INDEX_BUFFER_SIZE = sizeof(gl::Index) * 8192 };
}

namespace gl
{

IndexDataManager::IndexDataManager(Context *context, BufferBackEnd *backend)
    : mContext(context), mBackend(backend)
{
    mStreamBuffer = mBackend->createIndexBuffer(INITIAL_INDEX_BUFFER_SIZE);
}

IndexDataManager::~IndexDataManager()
{
    delete mStreamBuffer;
}

namespace
{

template <class InputIndexType>
void copyIndices(const InputIndexType *in, GLsizei count, Index *out, GLuint *minIndex, GLuint *maxIndex)
{
    Index first = *in;
    GLuint minIndexSoFar = first;
    GLuint maxIndexSoFar = first;

    for (GLsizei i = 0; i < count; i++)
    {
        if (minIndexSoFar > *in) minIndexSoFar = *in;
        if (maxIndexSoFar < *in) maxIndexSoFar = *in;

        *out++ = *in++;
    }

    // It might be a line loop, so copy the loop index.
    *out = first;

    *minIndex = minIndexSoFar;
    *maxIndex = maxIndexSoFar;
}

}

TranslatedIndexData IndexDataManager::preRenderValidate(GLenum mode, GLenum type, GLsizei count, Buffer *arrayElementBuffer, const void *indices)
{
    ASSERT(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_BYTE);
    ASSERT(count > 0);

    TranslatedIndexData translated;

    translated.count = count;

    std::size_t requiredSpace = spaceRequired(type, count);

    if (requiredSpace > mStreamBuffer->size())
    {
        std::size_t newSize = std::max(requiredSpace, 2 * mStreamBuffer->size());

        TranslatedIndexBuffer *newStreamBuffer = mBackend->createIndexBuffer(newSize);

        delete mStreamBuffer;
        mStreamBuffer = newStreamBuffer;
    }

    mStreamBuffer->reserveSpace(requiredSpace);

    size_t offset;
    void *output = mStreamBuffer->map(requiredSpace, &offset);

    translated.buffer = mStreamBuffer;
    translated.offset = offset;

    translated.indices = static_cast<const Index*>(output);

    if (arrayElementBuffer != NULL)
    {
        indices = static_cast<const GLubyte*>(arrayElementBuffer->data()) + reinterpret_cast<GLsizei>(indices);
    }

    Index *out = static_cast<Index*>(output);

    if (type == GL_UNSIGNED_SHORT)
    {
        const GLushort *in = static_cast<const GLushort*>(indices);

        copyIndices(in, count, out, &translated.minIndex, &translated.maxIndex);
    }
    else
    {
        const GLubyte *in = static_cast<const GLubyte*>(indices);

        copyIndices(in, count, out, &translated.minIndex, &translated.maxIndex);
    }

    mStreamBuffer->unmap();

    return translated;
}

std::size_t IndexDataManager::spaceRequired(GLenum type, GLsizei count)
{
    return (count + 1) * sizeof(Index); // +1 because we always leave an extra for line loops
}

}
