/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2006 Torus Knot Software Ltd
Also see acknowledgements in Readme.html

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.

You may alternatively use this source under the terms of a specific version of
the OGRE Unrestricted License provided you have obtained such a license from
Torus Knot Software Ltd.
-----------------------------------------------------------------------------
*/
#include "OgreD3D9VertexDeclaration.h"
#include "OgreD3D9Mappings.h"
#include "OgreException.h"
#include "OgreRoot.h"

namespace Ogre {

    //-----------------------------------------------------------------------
    D3D9VertexDeclaration::D3D9VertexDeclaration(LPDIRECT3DDEVICE9 device) 
        : mlpD3DDevice(device), mlpD3DDecl(NULL), mNeedsRebuild(true)
    {
    }
    //-----------------------------------------------------------------------
    D3D9VertexDeclaration::~D3D9VertexDeclaration()
    {
        SAFE_RELEASE(mlpD3DDecl);
    }
    //-----------------------------------------------------------------------
    const VertexElement& D3D9VertexDeclaration::addElement(unsigned short source, 
        size_t offset, VertexElementType theType,
        VertexElementSemantic semantic, unsigned short index)
    {
        mNeedsRebuild = true;
        return VertexDeclaration::addElement(source, offset, theType, semantic, index);
    }
    //-----------------------------------------------------------------------------
    const VertexElement& D3D9VertexDeclaration::insertElement(unsigned short atPosition,
        unsigned short source, size_t offset, VertexElementType theType,
        VertexElementSemantic semantic, unsigned short index)
    {
        mNeedsRebuild = true;
        return VertexDeclaration::insertElement(atPosition, source, offset, theType, semantic, index);
    }
    //-----------------------------------------------------------------------
    void D3D9VertexDeclaration::removeElement(unsigned short elem_index)
    {
        VertexDeclaration::removeElement(elem_index);
        mNeedsRebuild = true;
    }
	//-----------------------------------------------------------------------
	void D3D9VertexDeclaration::removeElement(VertexElementSemantic semantic, unsigned short index)
	{
		VertexDeclaration::removeElement(semantic, index);
		mNeedsRebuild = true;
	}
	//-----------------------------------------------------------------------
	void D3D9VertexDeclaration::removeAllElements(void)
	{
		VertexDeclaration::removeAllElements();
		mNeedsRebuild = true;
	}
    //-----------------------------------------------------------------------
    void D3D9VertexDeclaration::modifyElement(unsigned short elem_index, 
        unsigned short source, size_t offset, VertexElementType theType,
        VertexElementSemantic semantic, unsigned short index)
    {
        VertexDeclaration::modifyElement(elem_index, source, offset, theType, semantic, index);
        mNeedsRebuild = true;
    }
    //-----------------------------------------------------------------------
    LPDIRECT3DVERTEXDECLARATION9 D3D9VertexDeclaration::getD3DVertexDeclaration(void)
    {
        if (mNeedsRebuild)
        {
            SAFE_RELEASE(mlpD3DDecl);
            // Create D3D elements
            D3DVERTEXELEMENT9* d3delems = new D3DVERTEXELEMENT9[mElementList.size() + 1];

            VertexElementList::const_iterator i, iend;
            unsigned int idx;
            iend = mElementList.end();
            for (idx = 0, i = mElementList.begin(); i != iend; ++i, ++idx)
            {
                d3delems[idx].Method = D3DDECLMETHOD_DEFAULT;
                d3delems[idx].Offset = static_cast<WORD>(i->getOffset());
                d3delems[idx].Stream = i->getSource();
				d3delems[idx].Type = D3D9Mappings::get(i->getType());
				d3delems[idx].Usage = D3D9Mappings::get(i->getSemantic());
				// NB force index if colours since D3D uses the same usage for 
				// diffuse & specular
				if (i->getSemantic() == VES_SPECULAR)
				{
					d3delems[idx].UsageIndex = 1;
				}
				else if (i->getSemantic() == VES_DIFFUSE)
				{
					d3delems[idx].UsageIndex = 0;
				}
				else
				{
					d3delems[idx].UsageIndex = i->getIndex();
				}
            }
            // Add terminator
		    d3delems[idx].Stream = 0xff;
		    d3delems[idx].Offset = 0;
		    d3delems[idx].Type = D3DDECLTYPE_UNUSED;
		    d3delems[idx].Method = 0;
		    d3delems[idx].Usage = 0;
		    d3delems[idx].UsageIndex = 0;
            
            HRESULT hr = mlpD3DDevice->CreateVertexDeclaration(d3delems, &mlpD3DDecl);

            if (FAILED(hr))
            {
                OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, 
                    "Cannot create D3D9 vertex declaration: " + 
                    Root::getSingleton().getErrorDescription(hr), 
                    "Direct3D9VertexDeclaration::getD3DVertexDeclaration");
            }

            delete [] d3delems;

			mNeedsRebuild = false;

        }
        return mlpD3DDecl;
    }
    //-----------------------------------------------------------------------


}

