//
// Copyright (c) 2002-2014 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.
//

// Indexffer9.cpp: Defines the D3D9 IndexBuffer implementation.

#include "libGLESv2/renderer/d3d/d3d9/IndexBuffer9.h"
#include "libGLESv2/renderer/d3d/d3d9/Renderer9.h"

namespace rx
{

IndexBuffer9::IndexBuffer9(Renderer9 *const renderer) : mRenderer(renderer)
{
    mIndexBuffer = NULL;
    mBufferSize = 0;
    mIndexType = 0;
    mDynamic = false;
}

IndexBuffer9::~IndexBuffer9()
{
    SafeRelease(mIndexBuffer);
}

bool IndexBuffer9::initialize(unsigned int bufferSize, GLenum indexType, bool dynamic)
{
    SafeRelease(mIndexBuffer);

    updateSerial();

    if (bufferSize > 0)
    {
        D3DFORMAT format;
        if (indexType == GL_UNSIGNED_SHORT || indexType == GL_UNSIGNED_BYTE)
        {
            format = D3DFMT_INDEX16;
        }
        else if (indexType == GL_UNSIGNED_INT)
        {
            if (mRenderer->getRendererExtensions().elementIndexUint)
            {
                format = D3DFMT_INDEX32;
            }
            else
            {
                ERR("Attempted to create a 32-bit index buffer but renderer does not support 32-bit indices.");
                return false;
            }
        }
        else
        {
            ERR("Invalid index type %u.", indexType);
            return false;
        }

        DWORD usageFlags = D3DUSAGE_WRITEONLY;
        if (dynamic)
        {
            usageFlags |= D3DUSAGE_DYNAMIC;
        }

        HRESULT result = mRenderer->createIndexBuffer(bufferSize, usageFlags, format, &mIndexBuffer);
        if (FAILED(result))
        {
            ERR("Failed to create an index buffer of size %u, result: 0x%08x.", mBufferSize, result);
            return false;
        }
    }

    mBufferSize = bufferSize;
    mIndexType = indexType;
    mDynamic = dynamic;

    return true;
}

IndexBuffer9 *IndexBuffer9::makeIndexBuffer9(IndexBuffer *indexBuffer)
{
    ASSERT(HAS_DYNAMIC_TYPE(IndexBuffer9*, indexBuffer));
    return static_cast<IndexBuffer9*>(indexBuffer);
}

bool IndexBuffer9::mapBuffer(unsigned int offset, unsigned int size, void** outMappedMemory)
{
    if (mIndexBuffer)
    {
        DWORD lockFlags = mDynamic ? D3DLOCK_NOOVERWRITE : 0;

        void *mapPtr = NULL;
        HRESULT result = mIndexBuffer->Lock(offset, size, &mapPtr, lockFlags);
        if (FAILED(result))
        {
            ERR("Index buffer lock failed with error 0x%08x", result);
            return false;
        }

        *outMappedMemory = mapPtr;
        return true;
    }
    else
    {
        ERR("Index buffer not initialized.");
        return false;
    }
}

bool IndexBuffer9::unmapBuffer()
{
    if (mIndexBuffer)
    {
        HRESULT result = mIndexBuffer->Unlock();
        if (FAILED(result))
        {
            ERR("Index buffer unlock failed with error 0x%08x", result);
            return false;
        }

        return true;
    }
    else
    {
        ERR("Index buffer not initialized.");
        return false;
    }
}

GLenum IndexBuffer9::getIndexType() const
{
    return mIndexType;
}

unsigned int IndexBuffer9::getBufferSize() const
{
    return mBufferSize;
}

bool IndexBuffer9::setSize(unsigned int bufferSize, GLenum indexType)
{
    if (bufferSize > mBufferSize || indexType != mIndexType)
    {
        return initialize(bufferSize, indexType, mDynamic);
    }
    else
    {
        return true;
    }
}

bool IndexBuffer9::discard()
{
    if (mIndexBuffer)
    {
        void *dummy;
        HRESULT result;

        result = mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
        if (FAILED(result))
        {
            ERR("Discard lock failed with error 0x%08x", result);
            return false;
        }

        result = mIndexBuffer->Unlock();
        if (FAILED(result))
        {
            ERR("Discard unlock failed with error 0x%08x", result);
            return false;
        }

        return true;
    }
    else
    {
        ERR("Index buffer not initialized.");
        return false;
    }
}

D3DFORMAT IndexBuffer9::getIndexFormat() const
{
    switch (mIndexType)
    {
      case GL_UNSIGNED_BYTE:    return D3DFMT_INDEX16;
      case GL_UNSIGNED_SHORT:   return D3DFMT_INDEX16;
      case GL_UNSIGNED_INT:     return D3DFMT_INDEX32;
      default: UNREACHABLE();   return D3DFMT_UNKNOWN;
    }
}

IDirect3DIndexBuffer9 * IndexBuffer9::getBuffer() const
{
    return mIndexBuffer;
}

}