#include "MeshBuilder.hpp"
#include "mof/private/GraphicsDeviceImpl.hpp"
#include "mof/ConsoleIO.hpp"
#include <stdexcept>
#include "mof/private/MeshDisposer.hpp"
#include <memory>
#include <stdlib.h>
#include "mof/FilePath.hpp"
#include "mof/private/VertexFVF.hpp"
#include "mof/Material.hpp"
#include "mof/Texture.hpp"
#include "mof/private/Mesh.hpp"

struct mof::MeshBuilder::Impl{
	std::shared_ptr<mof::MeshDisposer> pMeshDisposer;
	unsigned long nMaterials;
	Material* materials;
	std::shared_ptr<Texture>* textures;


	Impl()
		: pMeshDisposer() , nMaterials(0) ,
		  materials(NULL) , textures(NULL){
	}

	~Impl(){
		delete[] materials;
		delete[] textures;
	}

};

mof::MeshBuilder::MeshBuilder(const mof::tstring& path )
: m_pImpl(new Impl())
{

	LPDIRECT3DDEVICE9 pDevice = mof::GraphicsDevice::getRawDevice();
	HRESULT hr;
	LPD3DXBUFFER pD3DXMtrlBuffer;	//マテリアル用バッファ
	m_pImpl->pMeshDisposer = std::shared_ptr<mof::MeshDisposer>( new mof::MeshDisposer() );

	//ファイルロード
	hr = D3DXLoadMeshFromX(path.c_str() , D3DXMESH_MANAGED, pDevice,
		&(m_pImpl->pMeshDisposer->pAdjacency) ,
		&pD3DXMtrlBuffer , NULL, 
		&(m_pImpl->nMaterials) , 
		&(m_pImpl->pMeshDisposer->pMesh) );
	if(hr != D3D_OK)throw std::invalid_argument("Failed --- D3DXLoadMeshFromX");

	//各種インスタンス取得
	D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
	try{
		m_pImpl->materials = new mof::Material[m_pImpl->nMaterials];
		m_pImpl->textures = new std::shared_ptr<mof::Texture>[m_pImpl->nMaterials];
		
	
		for( DWORD i = 0; i< m_pImpl->nMaterials; i++ )
			{
			//マテリアルのコピー
			D3DMATERIAL9 mat = d3dxMaterials[i].MatD3D;
			mat.Ambient = mat.Diffuse;
			mof::Material tmp;
			memcpy(
				static_cast<void*>(&tmp) , 
				static_cast<const void*>(&mat) ,
				sizeof(D3DMATERIAL9)
				);
			
			m_pImpl->materials[i] = tmp;

			//テクスチャファイルの読み込み
		   if( d3dxMaterials[i].pTextureFilename != NULL && 
		      lstrlen(d3dxMaterials[i].pTextureFilename) > 0 )
			{
				mof::otstringstream os;
				mof::FilePath path(path);
				os << path.dir().c_str() << _T("/") << d3dxMaterials[i].pTextureFilename;
				m_pImpl->textures[i] = std::shared_ptr<mof::Texture>(new mof::Texture( os.str()));
			}
		}

	}
	catch( std::exception& e){
		pD3DXMtrlBuffer->Release();	//バッファ解放
		throw e;
	}
	pD3DXMtrlBuffer->Release();	//バッファ解放

	LPD3DXMESH pMesh = NULL;
	/*
	if(m_pImpl->pMeshDisposer->pMesh->GetFVF() != mof::getFVF<VertexXYZNUV>())
	{
		//メッシュに法線がない場合書き込む
		m_pImpl->pMeshDisposer->pMesh->CloneMeshFVF(
			m_pImpl->pMeshDisposer->pMesh->GetOptions() ,
			mof::getFVF<VertexXYZNUV>() ,
			pDevice , &pMesh );
	
		LPD3DXMESH tmp = m_pImpl->pMeshDisposer->pMesh;
		m_pImpl->pMeshDisposer->pMesh = pMesh;
		tmp->Release();
	}
	*/

}
	
mof::MeshBuilder::~MeshBuilder(){}

mof::Graphics3D* mof::MeshBuilder::construct() const{
	return new mof::Mesh
		(
			m_pImpl->pMeshDisposer ,
			m_pImpl->nMaterials , 
			m_pImpl->materials ,
			m_pImpl->textures 
		);
}
