#pragma once
#include "mof/VertexBuffer.hpp"
#include "mof/private/GraphicsDeviceImpl.hpp"
#include <d3dx9.h>
#include "mof/VertexTypes.hpp"
#include "mof/private/VertexFVF.hpp"
#include <stdlib.h>

template class mof::VertexBuffer<mof::VertexXYZRHWCUV>;
template class mof::VertexBuffer<mof::VertexXYZRHWC>;
template class mof::VertexBuffer<mof::VertexXYZCUV>;
template class mof::VertexBuffer<mof::VertexXYZNUV>;
template class mof::VertexBuffer<mof::VertexXYZC>;


template <class T>
struct mof::VertexBuffer<T>::Impl{
	IDirect3DVertexBuffer9* pBuffer;
	mof::PRIMITIVE_TYPE primitiveType;
	
	
	Impl(mof::PRIMITIVE_TYPE primitiveType_);
	~Impl();
};


template <class T>
mof::VertexBuffer<T>::Impl::Impl(mof::PRIMITIVE_TYPE primitiveType_ )
: pBuffer(NULL) , primitiveType(primitiveType_) 
{	
}

template <class T>
mof::VertexBuffer<T>::Impl::~Impl()
{	
	if(pBuffer != NULL)pBuffer->Release();
}


template <class T>
mof::VertexBuffer<T>::VertexBuffer(const T& front , const T& back , mof::PRIMITIVE_TYPE primitiveType )
: m_pImpl(new Impl( primitiveType ))
{
	int length = &back - &front + 1;
	if(length <= 0)throw std::invalid_argument("不正な頂点配列指定");
	
	const LPDIRECT3DDEVICE9 pDevice = mof::GraphicsDevice::getRawDevice();
	HRESULT hr = pDevice->CreateVertexBuffer( sizeof(T) * length , 0 , mof::getFVF<T>() , D3DPOOL_DEFAULT , &m_pImpl->pBuffer , NULL);
	if(FAILED(hr))throw std::runtime_error("Failed -- CreateVertexBuffer");
	
	T* pBuffer;
	hr = m_pImpl->pBuffer->Lock( 0, 0 ,(LPVOID*)&pBuffer , 0);
	if(FAILED(hr))throw std::invalid_argument(std::string("頂点バッファのロックに失敗しました"));
	
	memcpy( pBuffer , &front , sizeof(T) * length );
	m_pImpl->pBuffer->Unlock();
}

template <class T>
mof::VertexBuffer<T>::VertexBuffer()
: m_pImpl(new Impl(mof::PRIMITIVE_TYPE_TRIANGLELIST)) // ダミーのプリミティブタイプ
{
}


template<class T>
mof::VertexBuffer<T>::~VertexBuffer(){
}


template<class T>
int mof::VertexBuffer<T>::getLength() const{
	if (!m_pImpl->pBuffer) return 0;
	D3DVERTEXBUFFER_DESC desc;
	m_pImpl->pBuffer->GetDesc(&desc);
	return desc.Size / sizeof(T);
}


template<class T>
mof::PRIMITIVE_TYPE mof::VertexBuffer<T>::getPrimitiveType() const{
	return m_pImpl->primitiveType;
}


template<class T>
void  mof::VertexBuffer<T>::draw() const{
	if (!m_pImpl->pBuffer) return;
	HRESULT hr = E_FAIL;

	const LPDIRECT3DDEVICE9 pDevice = mof::GraphicsDevice::getRawDevice();

	pDevice->SetFVF(mof::getFVF<T>());
	pDevice->SetStreamSource(0 , m_pImpl->pBuffer , 0 , sizeof(T));

	
	int length = getLength();

	if(m_pImpl->primitiveType == mof::PRIMITIVE_TYPE_TRIANGLESTRIP){
		hr = pDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP , 0 , length -2 );
	}
	else if(m_pImpl->primitiveType == mof::PRIMITIVE_TYPE_TRIANGLELIST){
		hr = pDevice->DrawPrimitive( D3DPT_TRIANGLELIST , 0 , length /3 );
	}
	
	if(FAILED(hr))throw std::runtime_error("Failed -- DrawPrimitive");

}

