#include "Mix/Tool/Win32/Core/Graphics/MapModel.h"

#include <sstream>
#include <algorithm>

#include "Mix/Tool/Win32/Core/Path.h"
#include "Mix/Tool/Win32/Core/Directory.h"

#include "Mix/Tool/Win32/Core/File/InputStream.h"
#include "Mix/Tool/Win32/Core/File/OutputStream.h"

#include "Mix/Tool/Win32/Core/Dynamics/Factory.h"
#include "Mix/Tool/Win32/Core/Dynamics/Design/Terrain.h"

#include "Mix/Tool/Win32/Core/Graphics/Manager.h"
#include "Mix/Tool/Win32/Core/Graphics/Material.h"
#include "Mix/Tool/Win32/Core/Graphics/MapMesh.h"
#include "Mix/Tool/Win32/Core/Graphics/MapViewMesh.h"
#include "Mix/Tool/Win32/Core/Graphics/Camera.h"

#include "Mix/Tool/Win32/Core/Graphics/MTMTypes.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

////////////////////////////////////////////////////////////////////////////////////////////////////
// MapModel : public
////////////////////////////////////////////////////////////////////////////////////////////////////

MapModel::MapModel( void ) : OctreeModel( L"マップモデル", false ),
m_pDynamicsDesigner( NULL )
{
	m_pDynamicsDesigner = Mix::Tool::Win32::Dynamics::Factory::CreateTerrain();
}

MapModel::~MapModel( void )
{
	MIX_DELETE( m_pDynamicsDesigner );
}

Mix::Tool::Win32::Dynamics::Design::Terrain* MapModel::GetDynamicsDesignerPtr( void )
{
	return m_pDynamicsDesigner;
}

void MapModel::OnPreImport( const OctreeModel::IMPORT_CONFIG& cfg )
{
	if( m_pDynamicsDesigner != NULL )
	{
		Mix::Tool::Win32::Dynamics::Design::Terrain::OCTREE_MODEL_CONFIG omCfg;

		omCfg.opt = cfg.opt;
		omCfg.scaling = cfg.scaling;
		omCfg.rotation = cfg.rotation;
		omCfg.translation = cfg.translation;
		omCfg.cellSize = cfg.cellSize;
		omCfg.cellError = cfg.cellError;

		m_pDynamicsDesigner->SetOctreeModelConfig( omCfg );
	}
}

bool MapModel::SaveModel( void )
{
	LogPrint( LT_INFO, L"----------------------------------------------------------------------------------------------------" );

	if( Model::IsAvailableSaveModel() == false )
	{
		LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : ファイルパスが設定されていません" );
		return false;
	}

	const wchar_t* pFilePath = Model::GetModelFilePath();
	Mix::Tool::Win32::File::OutputStream output;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 保存用メッシュのリフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( MapModel::MeshList::iterator it = m_FileMeshList.begin(); it != m_FileMeshList.end(); ++it )
	{
		( *it )->RefreshBuffer();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを開く
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Directory::Create( pFilePath, true ) == false )
	{
		LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : ディレクトリを作成できませんでした : FilePath[%s]", pFilePath );
		return false;
	}

	if( output.Open( pFilePath ) == false )
	{
		LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : ファイルを作成できませんでした : FilePath[%s]", pFilePath );
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		MTM_FILE_HEADER_1_0_0_0 fileHeader;

		fileHeader.magicNumber = MTM_MAGICNUMBER;
		fileHeader.version = MTM_VERSION;

		if( output.Write( &fileHeader, sizeof( fileHeader ) ) == false )
		{
			LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// データヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		MTM_DATA_HEADER_1_0_0_0 dataHeader;

		dataHeader.materialNum = GetMaterialSlotCount();
		dataHeader.meshNum = m_FileMeshList.size();
		dataHeader.nodeNum = m_OctNodeList.size();

		if( m_pDynamicsDesigner->IsImported() == true )
		{
			dataHeader.collShapeSize = m_pDynamicsDesigner->GetShapeSize();
			dataHeader.collPolygonNum = m_pDynamicsDesigner->GetPolygonNum();
			dataHeader.collMaterialNum = m_pDynamicsDesigner->GetPartNum();
		}
		else
		{
			dataHeader.collShapeSize = 0;
			dataHeader.collPolygonNum = 0;
			dataHeader.collMaterialNum = 0;
		}

		if( output.Write( &dataHeader, sizeof( dataHeader ) ) == false )
		{
			LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( DrawObject::WriteAllMaterials( pFilePath, output ) == false )
	{
		LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// メッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		std::vector<Mix::Tool::Win32::Graphics::MapMesh*>::const_iterator it_mesh_begin = m_FileMeshList.begin();
		std::vector<Mix::Tool::Win32::Graphics::MapMesh*>::const_iterator it_mesh_end = m_FileMeshList.end();
		std::vector<Mix::Tool::Win32::Graphics::MapMesh*>::const_iterator it_mesh;

		for( it_mesh = it_mesh_begin; it_mesh != it_mesh_end; ++it_mesh )
		{
			Mix::Tool::Win32::Graphics::MapMesh* pMesh = ( *it_mesh );

			if( pMesh->Write( output ) == false )
			{
				LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
				return false;
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ノード
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		MapModel::OctNodeList::iterator it;
		MapModel::OctNodeList::iterator it_begin = m_OctNodeList.begin();
		MapModel::OctNodeList::iterator it_end = m_OctNodeList.end();

		std::vector<MTM_NODE_1_0_0_0> nodeList;

		nodeList.reserve( m_OctNodeList.size() );

		for( it = it_begin; it != it_end; ++it )
		{
			const MapModel::OCT_NODE* src = ( *it );

			MTM_NODE_1_0_0_0 dst;

			dst.aabb = src->aabb;
			dst.mesh = src->meshNumber;
			dst.parent = ( src->pParent != NULL )? src->pParent->number : -1;
			dst.childCount = static_cast<int>( src->childs.size() );
			dst.reserve = 0;

			nodeList.push_back( dst );
		}

		if( output.Write( &( nodeList[0] ), ( sizeof( MTM_NODE_1_0_0_0 ) * nodeList.size() ) ) == false )
		{
			LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コリジョン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pDynamicsDesigner->IsImported() == true )
	{
		if( m_pDynamicsDesigner->Write( output ) == false )
		{
			LogPrint( LT_ERROR, L"  地形モデルの保存に失敗しました : 書き込み中にエラーが発生しました : FilePath[%s]", pFilePath );
			return false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルを閉じる
	////////////////////////////////////////////////////////////////////////////////////////////////////

	output.Close();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	LogPrint( LT_INFO, L"  地形モデルを保存しました : FilePath[%s]", pFilePath );

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// DrawObject
////////////////////////////////////////////////////////////////////////////////////////////////////

void MapModel::OnDraw( DrawObject::DRAW_EVENT_ARGS& args )
{
	OctreeModel::OnDraw( args );

	if( ( MIX_TEST_BIT( args.flags, DrawObject::DF_COLLISION ) == DrawObject::DF_COLLISION ) &&
		( m_pDynamicsDesigner != NULL ) &&
		( m_pDynamicsDesigner->GetOctreeModelPtr() != NULL ) )
	{
		DrawObject::DRAW_EVENT_ARGS collArgs = args;

		collArgs.flags = DrawObject::DF_BODY;

		m_pDynamicsDesigner->GetOctreeModelPtr()->OnDraw( collArgs ); 
	}
}

}}}}
