#pragma once
#include <mof/script/Environment.hpp>
#include <mof/widget/TextCompiler.hpp>
#include <mof/Font.hpp>
#include <mof/Application.hpp>
#include <mof/ConsoleIO.hpp>
#include "mof/EventScheduler.hpp"
#include "mof/OnceEventCondition.hpp"
#include "mof/KeyPressedEventCondition.hpp"
#include <mof/widgets.hpp>
#include <mof/streams.hpp>
#include <mof/Matrix3D.hpp>
#include <mof/Sprite.hpp>
#include <mof/Camera.hpp>
#include <mof/AmbientLight.hpp>
#include <mof/DirectionalLight.hpp>
#include <mof/script/ObjectData.hpp>
#include <mof/script/rendering_packet.hpp>
#include <mof/utilities.hpp>
#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <mof/sound/SoundDevice.hpp>
#include <mof/sound/StaticSoundBuffer.hpp>
#include <mof/sound/StreamSoundBuffer.hpp>
#include <mof/script/rendering_packet.hpp>
#include <vector>
#include <set>
#include <memory>

namespace
{
	using namespace boost::intrusive;
	using namespace mof::script;
	typedef member_hook<rendering_packet, rendering_packet::queue_hook_t, &rendering_packet::queue_hook_> rendering_queue_set_hook;
	typedef member_hook<rendering_packet, rendering_packet::dead_hook_t, &rendering_packet::dead_hook_> rendering_dead_list_hook;
}

namespace mof
{
namespace script
{
	struct thread_t : boost::noncopyable
	{
		thread_t()
			: waiting_(false)
		{}
		
		thread_t(thread_t&& rh)
			: scheduler_(std::move(rh.scheduler_)), waiting_(rh.waiting_)
		{}


		void update() {
			scheduler_.update();
		}

		bool waiting_;///< Interpreterにコマンド実行の停止を伝えるための状態変数
		mof::EventScheduler scheduler_; ///< キー入力待機など
	};
	
			


//{{{ Impl
	struct Environment::Impl
	{
		std::map<size_t, thread_t> threads;
     	std::shared_ptr<mof::InputReceiver> input_;
		std::vector<std::shared_ptr<mof::script::MessageData>> message_data_list_;
		std::vector<std::shared_ptr<mof::script::MenuData>> menu_data_list_;
		std::vector<mof::script::PictureData::ptr> picture_data_list_;
		std::vector<mof::script::BoardData::ptr> board_data_list_;
		std::vector<mof::script::ParticlegenData::ptr> particlegen_data_list_;
		std::vector<mof::script::SolidData::ptr> solid_data_list_;
		std::vector<mof::script::vertex_buffer_obj::ptr> vertex_buffer_obj_list_;

		// rendering TODO クラス化
		boost::intrusive::multiset<mof::script::rendering_packet, rendering_queue_set_hook, constant_time_size<false>> rendering_queue_set_;
		boost::intrusive::list<mof::script::rendering_packet, rendering_dead_list_hook, constant_time_size<false>> rendering_dead_list_;
		std::vector<mof::script::rendering_packet> rendering_memory_pool_;

		std::vector<std::shared_ptr<mof::script::StaticSoundData>> static_sound_data_list_;
		std::vector<std::shared_ptr<mof::script::StreamSoundData>> music_data_list_;
		std::vector<std::shared_ptr<mof::script::SoundSourceData>> sound_source_data_list_;
		std::vector<mof::script::CameraData::ptr> camera_data_list_;// 現在、要素数は1つだけ
		std::vector<mof::script::light_obj::ptr> light_obj_list_;

		std::map<std::string, int> property_map_;
		ResourceManager<Texture>::ptr texture_manager_;
		ResourceManager<MeshBuilder>::ptr mesh_builder_manager_;
		
		Impl
		(
			std::shared_ptr<mof::InputReceiver> input,
			const ResourceManager<Texture>::ptr& texture_manager,
			const ResourceManager<MeshBuilder>::ptr& mesh_builder_manager
		)
			: input_(input), texture_manager_(texture_manager), mesh_builder_manager_(mesh_builder_manager)
		{
			rendering_memory_pool_.reserve(1000);
		}

		template <typename T, typename K>
		size_t add_obj
		(
			std::vector<T>& objs,
			T& obj,
			std::shared_ptr<K> T::element_type::*drawable,
			int priority,
			bool z_buffer
		)
		{
			if (!rendering_dead_list_.empty()) {
				// 使ってない要素があるなら、そこから回収
				int index = std::distance(&(rendering_memory_pool_.front()), &(rendering_dead_list_.front()));
				obj->r_packet_ = &rendering_memory_pool_[index];
				rendering_dead_list_.pop_front();
				rendering_memory_pool_[index] = rendering_packet((obj.get()->*drawable).get(), priority, z_buffer);
				rendering_queue_set_.insert(rendering_memory_pool_[index]);
			}
			else {
				// 新たにpoolに確保
				rendering_memory_pool_.push_back(rendering_packet((obj.get()->*drawable).get(), priority, z_buffer));
				obj->r_packet_ = &(rendering_memory_pool_.back());
				rendering_queue_set_.insert(rendering_memory_pool_.back());
			}

			// 再利用できる要素があれば、それを使う
			// TODO 線形探索してしまうのが気になる・・・
			std::vector<T>::iterator itr = std::find_if(objs.begin(), objs.end(),
					[drawable] (const T& obj)->bool {return !obj;});
			if (itr == objs.end()) {
				// 新規追加
				objs.push_back(obj);
				return objs.size() - 1;
			}
			else {
				// 使用していない要素が見つかったので、それを使う
				*itr = obj;
				return std::distance(objs.begin(), itr);
			}
		}

		void dispose_render_node(rendering_packet* r_packet)
		{
			r_packet->queue_hook_.unlink();
			rendering_dead_list_.push_back(*r_packet);
		}
		
		template <typename ListType>
		void erase_by_thread_id(ListType& list, unsigned int thread_id)
		{
			foreach (auto& data, list)
			{
				if(data.get() != NULL && thread_id == data->thread_id_)
				{
					dispose_render_node(data->r_packet_);
					data.reset();
				}
			}
		}


	};
//}}}
//{{{ constructor
	Environment::Environment
	(
		std::shared_ptr<mof::InputReceiver> input,
		const ResourceManager<Texture>::ptr& texture_manager,
		const ResourceManager<MeshBuilder>::ptr& mesh_builder_manager
	)
	: impl_(new Impl(input, texture_manager, mesh_builder_manager))
	{
		impl_->property_map_["system.menu.move_cursor_sound"] = sound_create(_T("sound/move_cursor.wav"));

		// カメラの初期化
		auto data = std::make_shared<CameraData>();
		data->camera_ = 
			std::make_shared<Camera>
			(
				mof::Vector3D(0, 0, -1),
				mof::Vector3D(0, 0, 0),
				mof::Vector3D(0, 0, 1)
			);
		impl_->camera_data_list_.push_back(data);
			
	}
//}}}
//{{{ destructor
	Environment::~Environment()
	{
	}
//}}}
//{{{ message_create
	int Environment::message_create(unsigned int thread_id, const mof::tstring& title, const GameData::entry_t& style, int priority)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("message_create(" << title << ")");
		// ***Dataは実体の配列で管理したほうがよい
		std::shared_ptr<MessageData> data = std::shared_ptr<MessageData>(create_message_data(title, style).release());
		data->view_ = data->frame_->getView();
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->add_obj(impl_->message_data_list_, data, &MessageData::view_, priority, false);
	}
//}}}	
//{{{ massage_next
	int Environment::message_next(int id, const tstring& text)
	{
		DEBUG_PRINT("message_next(" << id << "," << text << ")");
		std::shared_ptr<mof::script::MessageData>& data = impl_->message_data_list_.at(id);
		data->message_->addPage(text);
		return data->message_->nextPage();
	}
//}}}
//{{{ menu_create
	int Environment::menu_create
	(
	 	unsigned int thread_id,
		const mof::tstring& title,
		const std::vector<mof::tstring>& items,
		const GameData::entry_t& style,
		int priority
	)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("menu_create(" << title << ")");
		std::shared_ptr<MenuData> data = std::shared_ptr<MenuData>(create_menu_data(title, items, style).release());
		data->view_ = data->frame_->getView();
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->add_obj(impl_->menu_data_list_, data, &MenuData::view_, priority, false);
	}
//}}}	
//{{{ menu_move_cursor
	int Environment::menu_move_cursor(int id, MoveDirection direction)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT(format("menu_move_cursor(%d, %d)") % id % direction);
		auto& data = impl_->menu_data_list_.at(id);
		int prev_index = data->menu_->getSelectedIndex();
		mof::FrameNumber period;
		switch (direction) {
			case MOVE_UP : 
				period = data->menu_->up();
				break;
			case MOVE_DOWN : 
				period = data->menu_->down();
				break;
			case MOVE_LEFT : 
				period = data->menu_->left();
				break;
			case MOVE_RIGHT : 
				period = data->menu_->right();
				break;
			default:
				return 0;
		}
		if (data->menu_->getSelectedIndex() != prev_index) {
			sound_play(impl_->property_map_["system.menu.move_cursor_sound"]);
		}
		return period;
	}
//}}}	
//{{{ menu_select
	int Environment::menu_select(int id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT(format("menu_select(%d)") % id);
		auto& data = impl_->menu_data_list_.at(id);
		return data->menu_->performAction();
	}
//}}}	
//{{{ menu_get_current
	int Environment::menu_get_current(int id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT(format("menu_get_current(%d)") % id);
		auto& data = impl_->menu_data_list_.at(id);
		DEBUG_PRINT(data->menu_->getSelectedIndex());
		return data->menu_->getSelectedIndex();
	}
//}}}	
//{{{ wait_for_key
	void Environment::wait_for_key(size_t id, mof::InputReceiver::Key key)
	{
		DEBUG_PRINT("wait_for_key(" << id << ", " << key << ")");
		if (impl_->threads.end() == impl_->threads.find(id))
		{
			impl_->threads.insert(std::make_pair(id, thread_t()));// スレッドがなければつくる 
		}

		bool& waiting = impl_->threads[id].waiting_;
        impl_->threads[id].scheduler_.addEvent
        (
            new mof::OnceEventCondition(new mof::KeyPressedEventCondition(key, impl_->input_)), 
			[&waiting](){waiting = false;}
        );
		impl_->threads[id].waiting_ = true;
	}
//}}}
//{{{ wait_frame
	void Environment::wait_frame(size_t id, size_t frame)
	{
		DEBUG_PRINT("wait_frame(" << id << ", " << frame << ")");
		if (frame == 0)return;

		for (auto itr = impl_->threads.begin(); itr != impl_->threads.end(); ) {
			if (!itr->second.waiting_) {
				// 解放
				itr = impl_->threads.erase(itr);
			}
			else {
				++itr;
			}
		}
		impl_->threads.insert(std::make_pair(id, thread_t()));// TODO スレッドじゃなくて、waiting_taskと言い換えたほうがいいかも

		bool& waiting = impl_->threads[id].waiting_;
		impl_->threads[id].scheduler_.addEvent(frame, [&waiting](){waiting = false;});// set waiting_ false after 'frame' 
		impl_->threads[id].waiting_ = true;
	}
//}}}
//{{{ routine_stop
	void Environment::routine_stop(size_t thread_id)
	{
		DEBUG_PRINT("routine_stop(" << thread_id << ")");

		for (auto itr = impl_->threads.begin(); itr != impl_->threads.end(); )
		{
			if (itr->first == thread_id) 
			{
				// 解放
				itr = impl_->threads.erase(itr);
			}
			else 
			{
				++itr;
			}
		}

		
		// スレッド内で生成したオブジェクトを解放
		impl_->erase_by_thread_id(impl_->message_data_list_, thread_id);
		impl_->erase_by_thread_id(impl_->menu_data_list_, thread_id);
		impl_->erase_by_thread_id(impl_->picture_data_list_, thread_id);
		impl_->erase_by_thread_id(impl_->solid_data_list_, thread_id);
		impl_->erase_by_thread_id(impl_->board_data_list_, thread_id);
		impl_->erase_by_thread_id(impl_->vertex_buffer_obj_list_, thread_id);
		
		// lightは別処理
		foreach (auto& data, impl_->light_obj_list_)
		{
			if(data.get() != NULL && thread_id == data->thread_id_)
			{
				data.reset();
			}
		}

		// particlegenは別処理
		foreach (auto& data, impl_->particlegen_data_list_)
		{
			if(data.get() != NULL && thread_id == data->thread_id_)
			{
				data.reset();
			}
		}

	}
//}}}
//{{{ get_last_key
	mof::InputReceiver::Key Environment::get_last_key()
	{
		//TODO 複数同時をtimestampで識別
		DEBUG_PRINT("get_last_key()");
		for (int key = mof::InputReceiver::BEGIN + 1; key < mof::InputReceiver::ANY; ++key) {
			if (impl_->input_->testKeyState(static_cast<mof::InputReceiver::Key>(key))) {
				return static_cast<mof::InputReceiver::Key>(key);// 最初に見つかったものを選ぶ
			}
		}
		return mof::InputReceiver::END;// 何も入力されていないとき
	}
//}}}
//{{{ sound_create
	int Environment::sound_create(const mof::tstring& filepath)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("sound_create(" << filepath << ")");
		auto ptr = mof::sound::SoundDevice::create_static_sound(filepath);

		auto data = std::make_shared<SoundSourceData>();
		data->source_ = std::shared_ptr<mof::StaticSoundBuffer>(ptr.release());
		impl_->sound_source_data_list_.push_back(data);
		return impl_->sound_source_data_list_.size() -1;
	}
//}}}	
//{{{ sound_play
	void Environment::sound_play(int id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("sound_play(" << id << ")");
		auto data = std::make_shared<StaticSoundData>();
		auto src = impl_->sound_source_data_list_.at(id);
		data->sound_ = std::shared_ptr<mof::StaticSoundBuffer>(src->source_->duplicate());
		data->sound_->play(false);
		impl_->static_sound_data_list_.push_back(data);
	}
//}}}	
//{{{ music_create
	int Environment::music_create(const mof::tstring& filepath)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("music_create(" << filepath << ")");
		auto ptr = mof::sound::SoundDevice::create_streaming_sound(filepath);

		auto data = std::make_shared<StreamSoundData>();
		data->sound_ = std::shared_ptr<mof::StreamSoundBuffer>(ptr.release());
		impl_->music_data_list_.push_back(data);
		return impl_->music_data_list_.size() -1;
	}
//}}}	
//{{{ music_play
	void Environment::music_play(int id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("music_play(" << id << ")");
		impl_->music_data_list_.at(id)->sound_->play(true);
	}
//}}}	
//{{{ music_pause
	void Environment::music_pause(int id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("music_pause(" << id << ")");
		impl_->music_data_list_.at(id)->sound_->pause();
	}
//}}}	
//{{{ load_game_data
	GameData::ptr Environment::load_game_data(const mof::tstring& resource_path)
	{
		return get_game_data(resource_path);
	}
//}}}	
//{{{ save_game_data
	void Environment::save_game_data(const mof::tstring& resource_path, const GameData::ptr& game_data)
	{
		return set_game_data(resource_path, game_data);
	}
//}}}	
//{{{ print_debug
	void Environment::print_debug(const mof::tstring& message)
	{
		DEBUG_PRINT(message);
	}
//}}}
//{{{ picture_create
	int Environment::picture_create(unsigned int thread_id, const mof::tstring& class_path, int priority)
	{
		using namespace mof::script;
		using namespace boost;
		using namespace boost::algorithm;

		DEBUG_PRINT("picture_create(" << class_path << ")");
		std::list<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		auto data = std::make_shared<PictureData>();
		if (splited_list.front() == "file") {
			// ファイルから読み込む
			splited_list.pop_front();
			mof::tstring filepath = join(splited_list, ".");
			data->sprite_ = 
				std::make_shared<Sprite>
				(
					impl_->texture_manager_->getResource(filepath),// TODO share
					mof::Rectangle<mof::real>(0, 0, 1, 1)
				);
		} 
		data->sprite_->getRectangleCoordinatesStream() <<
			data->texcoord_ref_.makeRef(Rectangle<float>(0, 0, 1, 1));
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->add_obj(impl_->picture_data_list_, data, &PictureData::sprite_, priority, false);

	}
//}}}	
//{{{ particlegen_create
	int Environment::particlegen_create(unsigned int thread_id)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("particlegen_create()");
		//DEBUG_PRINT("picture_create(" << filepath << ")");
		auto data = std::make_shared<ParticlegenData>();
		data->particlegen_ = std::make_shared<mof::particlegen>();
		data->particlegen_->world_transform() << data->position_ref_.makeRef(Matrix3D::createIdentity());
		impl_->particlegen_data_list_.push_back(data);// TODO 再利用
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->particlegen_data_list_.size() -1;

	}
//}}}	
//{{{ solid_create
	int Environment::solid_create(unsigned int thread_id, const mof::tstring& class_path, int priority)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("solid_create(" << class_path << ")");
		std::list<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));

		auto data = std::make_shared<SolidData>();
		if (splited_list.front() == "file") {
			// ファイルから読み込む
			splited_list.pop_front();
			mof::tstring filepath = join(splited_list, ".");
			auto builder = impl_->mesh_builder_manager_->getResource(filepath);
			data->solid_ = std::shared_ptr<Graphics3D>(builder->construct());
		}
		else {
			data->solid_ = create_solid(class_path);
		}
		
		data->solid_->world_transform() 
			<< data->size_ref_.makeRef(Matrix3D::createIdentity())
			<< data->rotate_ref_.makeRef(Matrix3D::createIdentity())
			<< data->position_ref_.makeRef(Matrix3D::createIdentity());
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->add_obj(impl_->solid_data_list_, data, &SolidData::solid_, priority, true);
	}
//}}}	
//{{{ board_create
	int Environment::board_create(unsigned int thread_id, const mof::tstring& class_path, const GameData::entry_t& style, int priority)
	{
		using namespace mof::widget;
		using namespace mof::script;
		using namespace boost;
		using namespace boost::algorithm;

		DEBUG_PRINT("board_create(" << class_path << ")");
		std::list<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		auto data = std::make_shared<BoardData>();
		bool z_buffer = true;

		// スタイルの解析
		foreach (const auto& p, style) {
			if (p.first == "z-buffer") {
				if (p.second == "enable") z_buffer = true;
				else if (p.second == "disable") z_buffer = false;
				else throw std::invalid_argument("invalid z-buffer parameter:" + p.second);
			}
			else throw std::invalid_argument("invalid key:" + p.first);
		}
		
		if (splited_list.front() == "file") {
			// ファイルから読み込む
			splited_list.pop_front();
			mof::tstring filepath = join(splited_list, ".");
			data->board_ = std::make_shared<Board>();
			data->board_->setTexture(impl_->texture_manager_->getResource(filepath));

			// テクスチャ座標の設定
			data->board_->getRectangleCoordinatesStream() 
				<< data->texcoord_ref_.makeRef(Rectangle<float>(0, 0, 1, 1));
		}
		else
		{
			TextCompiler compiler(mof::Font(mof::Font::MS_P_GOTHIC, 24));// TODO てきとーな設定だが大丈夫か？
			data->board_ = compiler.compile_to_board( class_path );
			// テクスチャ座標の設定は行わない
		}

		data->board_->world_transform() 
			<< data->size_ref_.makeRef(Matrix3D::createIdentity())
			<< data->rotate_ref_.makeRef(Matrix3D::createIdentity())
			<< data->position_ref_.makeRef(Matrix3D::createIdentity());
		data->board_->getColorStream() 
			<< data->color_ref_.makeRef(Color4f(1, 1, 1, 1));
		data->is_enable_ = true;
		data->thread_id_ = thread_id;
		return impl_->add_obj(impl_->board_data_list_, data, &BoardData::board_, priority, z_buffer);
	}
//}}}	
//{{{ camera_create
	int Environment::camera_create()
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("camera_create()");
		return impl_->camera_data_list_.size() -1;
	}
//}}}	
//{{{ light_create
	int Environment::light_create(unsigned int thread_id, const mof::tstring& class_path)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("light_create(" << class_path << ")");

		if (class_path == "directional") {
			auto p = std::make_shared<light_obj>();	
			p->light_ = std::make_shared<DirectionalLight>();
			p->type_ = class_path;
			p->thread_id_ = thread_id;
			impl_->light_obj_list_.push_back(p);
			return impl_->light_obj_list_.size() -1;
		}
		else throw std::invalid_argument("unknown class path:" + class_path);
	}
//}}}	
//{{{ vertex_buffer_create
	int Environment::vertex_buffer_create
	(
	 	unsigned int thread_id,
	 	const mof::VertexXYZRHWCUV& front,
	 	const mof::VertexXYZRHWCUV& back,
		const mof::Texture::ptr& texture,
		int priority
	)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("vertex_buffer_create()");
		std::shared_ptr<vertex_buffer_obj> obj = std::make_shared<vertex_buffer_obj>();
		obj->vb_ = std::make_shared<vertex_buffer_with_texture>();
		obj->vb_->buffer_ = std::make_shared<mof::VertexBuffer<mof::VertexXYZRHWCUV>>(front, back, mof::PRIMITIVE_TYPE_TRIANGLELIST);
		obj->vb_->texture_ = texture;
		obj->is_enable_ = true;
		obj->thread_id_ = thread_id;
		return impl_->add_obj(impl_->vertex_buffer_obj_list_, obj, &vertex_buffer_obj::vb_, priority, false);
	}
//}}}	
// methods for common objects 
//{{{ show
int Environment::show(int id, const mof::tstring& class_path)
{
		using namespace mof::script;
		using namespace boost;

		if (class_path == "menu") {
			DEBUG_PRINT("menu_show(" << id << ")");
			std::shared_ptr<MenuData>& data = impl_->menu_data_list_.at(id);
			return data->frame_->show();
		}
		else if (class_path == "message") {
			DEBUG_PRINT("message_show(" << id << ")");
			std::shared_ptr<mof::script::MessageData>& data = impl_->message_data_list_.at(id);
			return data->frame_->show();
		}
		else throw std::invalid_argument("unknown class path:" + class_path);
}
//}}}
//{{{ hide
int Environment::hide(int id, const mof::tstring& class_path)
{
		using namespace mof::script;
		using namespace boost;

		if (class_path == "menu") {
			DEBUG_PRINT("menu_hide(" << id << ")");
			std::shared_ptr<MenuData>& data = impl_->menu_data_list_.at(id);
			return data->frame_->hide();
		}
		else if (class_path == "message") {
			DEBUG_PRINT("message_hide(" << id << ")");
			std::shared_ptr<mof::script::MessageData>& data = impl_->message_data_list_.at(id);
			return data->frame_->hide();
		}
		else throw std::invalid_argument("unknown class path:" + class_path);
}
//}}}
//{{{ dispose
void Environment::dispose(int id, const mof::tstring& class_path)
{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("----- obj使用状況 ------");
		DEBUG_PRINT("message  = " << impl_->message_data_list_.size() );
		DEBUG_PRINT("menu     = " << impl_->menu_data_list_.size() );
		DEBUG_PRINT("picture  = " << impl_->picture_data_list_.size() );
		DEBUG_PRINT("solid    = " << impl_->solid_data_list_.size() );
		DEBUG_PRINT("board    = " << impl_->board_data_list_.size() );
		DEBUG_PRINT("------------------------");
		DEBUG_PRINT("rendring = " << impl_->rendering_memory_pool_.size() );

		if (class_path == "menu") {
			DEBUG_PRINT("menu_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->menu_data_list_.at(id)->r_packet_);
			impl_->menu_data_list_.at(id).reset();
		} else if (class_path == "message") {
			DEBUG_PRINT("message_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->message_data_list_.at(id)->r_packet_);
			impl_->message_data_list_.at(id).reset();
		} else if (class_path == "picture") {
			DEBUG_PRINT("picture_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->picture_data_list_.at(id)->r_packet_);
			impl_->picture_data_list_.at(id).reset();
		} else if (class_path == "particlegen") {
			DEBUG_PRINT("particlegen_dispose(" << id << ")");
			impl_->particlegen_data_list_.at(id).reset();
		} else if (class_path == "solid") {
			DEBUG_PRINT("solid_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->solid_data_list_.at(id)->r_packet_);
			impl_->solid_data_list_.at(id).reset();
		} else if (class_path == "board") {
			DEBUG_PRINT("board_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->board_data_list_.at(id)->r_packet_);
			impl_->board_data_list_.at(id).reset();
		} else if (class_path == "sound") {
			DEBUG_PRINT("sound_dispose(" << id << ")");
			impl_->sound_source_data_list_.at(id)->source_->stop();
			impl_->sound_source_data_list_.at(id).reset();
		} else if (class_path == "music") {
			DEBUG_PRINT("music_dispose(" << id << ")");
			impl_->music_data_list_.at(id)->sound_->stop();
			impl_->music_data_list_.at(id).reset();
		} else if (class_path == "vertex_buffer") {
			DEBUG_PRINT("vertex_buffer_dispose(" << id << ")");
			impl_->dispose_render_node(impl_->vertex_buffer_obj_list_.at(id)->r_packet_);
			impl_->vertex_buffer_obj_list_.at(id).reset();
		} else if (class_path == "light") {
			DEBUG_PRINT("light_dispose(" << id << ")");
			impl_->light_obj_list_.at(id).reset();
		}
		else throw std::invalid_argument("unknown class path:" + class_path);
}
//}}}
//{{{ set_enable
void Environment::set_enable(int id, bool is_enable, const mof::tstring& class_path)
{
		using namespace mof::script;
		using namespace boost;

		if (class_path == "menu") {
			DEBUG_PRINT("menu_set_enable(" << id << ")");
			impl_->menu_data_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->menu_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "message") {
			DEBUG_PRINT("message_set_enable(" << id << ")");
			impl_->message_data_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->message_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "picture") {
			DEBUG_PRINT("picture_set_enable(" << id << ")");
			impl_->picture_data_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->picture_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "particlegen") {
			DEBUG_PRINT("particlegen_set_enable(" << id << ")");
			impl_->particlegen_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "solid") {
			DEBUG_PRINT("solid_set_enable(" << id << ")");
			impl_->solid_data_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->solid_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "board") {
			DEBUG_PRINT("board_set_enable(" << id << ")");
			impl_->board_data_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->board_data_list_.at(id)->is_enable_ = is_enable;
		} else if (class_path == "vertex_buffer") {
			DEBUG_PRINT("vertex_buffer_set_enable(" << id << ")");
			impl_->vertex_buffer_obj_list_.at(id)->r_packet_->enable() = is_enable;
			impl_->vertex_buffer_obj_list_.at(id)->is_enable_ = is_enable;
		}
		else throw std::invalid_argument("unknown class path:" + class_path);
}
//}}}
//{{{ get_properties
	GameData::ptr Environment::get_properties(int id, const mof::tstring& class_path)
	{
		using namespace mof::script;
		using namespace boost;
		GameData::ptr game_data = std::make_shared<GameData>();
		game_data->data_.resize(1);
		GameData::entry_t& e = game_data->data_.front();

		if (class_path == "menu") {
			DEBUG_PRINT("get_property(" << id << class_path << ")");
			std::shared_ptr<MenuData>& data = impl_->menu_data_list_.at(id);
			Vector2D v = data->frame_->getView()->getPreferredSize();
			e["preferred_size"] = (format("%d,%d") % v.x % v.y).str();
			Vector2D pos = data->frame_->getView()->getPositionStream().value();
			e["position2"] = (format("%d,%d") % pos.x % pos.y).str();
			Vector2D size = data->frame_->getView()->getSizeStream().value();
			e["size2"] = (format("%d,%d") % size.x % size.y).str();
		}
		else if (class_path == "message") {
			DEBUG_PRINT("get_property(" << id << class_path << ")");
			std::shared_ptr<MessageData>& data = impl_->message_data_list_.at(id);
			Vector2D v = data->frame_->getView()->getPreferredSize();
			e["preferred_size"] = (format("%d,%d") % v.x % v.y).str();
			Vector2D pos = data->frame_->getView()->getPositionStream().value();
			e["position2"] = (format("%d,%d") % pos.x % pos.y).str();
			Vector2D size = data->frame_->getView()->getSizeStream().value();
			e["size2"] = (format("%d,%d") % size.x % size.y).str();
		}
		else if (class_path == "picture") {
			DEBUG_PRINT("get_property(" << id << class_path << ")");
			std::shared_ptr<PictureData>& data = impl_->picture_data_list_.at(id);
			Vector2D v = data->sprite_->getPreferredSize();
			e["preferred_size"] = (format("%d,%d") % v.x % v.y).str();
			Vector2D pos = data->sprite_->getPositionStream().value();
			e["position2"] = (format("%d,%d") % pos.x % pos.y).str();
			Vector2D size = data->sprite_->getSizeStream().value();
			e["size2"] = (format("%d,%d") % size.x % size.y).str();
		}
		else throw std::invalid_argument("unknown class path:" + class_path);

		return game_data;
	}
//}}}
//{{{ set_color_behavior
	void Environment::set_color_behavior(int id, const mof::tstring& class_path, const Manipulator<Color4f>::ptr& seq, mof::FrameNumber period)
	{
		using namespace boost::algorithm;
		DEBUG_PRINT("set_color_behavior(" << id << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "picture") {
			auto picture = impl_->picture_data_list_.at(id);
			picture->sprite_->setColorStream(ColorStream(seq));
			return;
		}
		else if (splited_list.front() == "menu") {
			if (splited_list.at(1) == "open") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnColor(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnColor(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}

		}
		else if (splited_list.front() == "message") {
			if (splited_list.at(1) == "open") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnColor(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnColor(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}

		}
		else if (splited_list.front() == "board") {
			auto p = impl_->board_data_list_.at(id);
			p->color_ref_.replace(0, p->board_->getColorStream(), seq);
			// TODO
			return;
		} else if (splited_list.front() == "light") {
			auto p = impl_->light_obj_list_.at(id);
			mof::tstring type = p->type_;
			mof::tstring target = splited_list[1];
			if (type == "directional") {
				auto light = reinterpret_cast<mof::DirectionalLight*>(p->light_.get());
				// TODO tweenを設定できるように
				if (target == "ambient") light->setAmbientColor(seq->value(0).toColorCode());
				else if (target == "diffuse") light->setDiffuseColor(seq->value(0).toColorCode());
				else throw std::logic_error("unknown target:" + target);
				return;
			}
			throw std::logic_error("unknown light type:" + type);
			return;
		}






		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_position_behavior
	void Environment::set_position_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector2D>::ptr& seq, mof::FrameNumber period)
	{
		using namespace boost::algorithm;
		DEBUG_PRINT("set_position_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "picture") {
			auto picture = impl_->picture_data_list_.at(id);
			picture->sprite_->setPositionStream(Vector2DStream(seq));
			return;
		}
		else if (splited_list.front() == "menu") {
			if (splited_list.at(1) == "open") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnPosition(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnPosition(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}

		}
		else if (splited_list.front() == "message") {
			if (splited_list.at(1) == "open") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnPosition(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnPosition(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}
		}


		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_position_behavior
	void Environment::set_position_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector3D>::ptr& seq)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_position_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "particlegen") {
			auto p = impl_->particlegen_data_list_.at(id);
			p->position_ref_.replace(0, p->particlegen_->world_transform(), std::make_shared<Translation3D>(seq));
			return;
		}
		else if (splited_list.front() == "solid") {
			auto p = impl_->solid_data_list_.at(id);
			p->position_ref_.replace(0, p->solid_->world_transform(), std::make_shared<Translation3D>(seq));
			return;
		}
		else if (splited_list.front() == "board") {
			auto p = impl_->board_data_list_.at(id);
			p->position_ref_.replace(0, p->board_->world_transform(), std::make_shared<Translation3D>(seq));
			/*
			Manipulator<Matrix3D>::ptr tweens[] = 
				{
					mof::makeConstantHandler<Matrix3D>(Matrix3D::createScaling(Vector3D(0.2, 0.2, 0.2))),
					std::make_shared<Translation3D>(seq)
				};
			p->board_->setWorldMatrix(mof::makeCascadeHandler<Matrix3D>(tweens[0], lastOf(tweens)));
			p->board_->setFrameNumber(0);
			*/
			// TODO
			return;
		}
		else if (splited_list.front() == "light") {
			auto p = impl_->light_obj_list_.at(id);
			DEBUG_PRINT(p->type_.size());
			mof::tstring type = p->type_;
			DEBUG_PRINT(p->type_.size());
			if (type == "directional") {
				auto light = reinterpret_cast<mof::DirectionalLight*>(p->light_.get());
				// TODO tweenを設定できるように
				light->setDirection(seq);
				return;
			}
			throw std::logic_error("unknown light type:" + type);
		}


		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_texture_coord_tween
	void Environment::set_texture_coord_tween
	(
		int id,
		const mof::tstring& class_path, 
		const Manipulator<mof::Rectangle<float>>::ptr& seq
	)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_texture_coord_tween(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "picture") {
			auto picture = impl_->picture_data_list_.at(id);
			picture->texcoord_ref_.replace(0, picture->sprite_->getRectangleCoordinatesStream(), seq);
			return;
		}
		else if (splited_list.front() == "board") {
			auto board = impl_->board_data_list_.at(id);
			board->texcoord_ref_.replace(0, board->board_->getRectangleCoordinatesStream(), seq);
			return;
		}

		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_camera_behavior
	void Environment::set_camera_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector3D>::ptr& seq)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_camera_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "camera") {
			auto p = impl_->camera_data_list_.at(id);
			if (splited_list[1] == "eye") p->camera_->setEye(seq);
			else if (splited_list[1] == "lookat") p->camera_->setCenter(seq);
			else throw std::logic_error("unknown class_path:" + class_path);
			p->camera_->setFrameNumber(0);
			return;
		}

		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_size_behavior
	void Environment::set_size_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector2D>::ptr& seq, mof::FrameNumber period)
	{
		using namespace boost::algorithm;
		DEBUG_PRINT("set_position_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "picture") {
			auto picture = impl_->picture_data_list_.at(id);
			picture->sprite_->setSizeStream(Vector2DStream(seq));
			return;
		}
		else if (splited_list.front() == "menu") {
			if (splited_list.at(1) == "open") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnSize(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto menu = impl_->menu_data_list_.at(id);
				menu->frame_->setBehaviorOnSize(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}

		}
		else if (splited_list.front() == "message") {
			if (splited_list.at(1) == "open") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnSize(mof::widget::Frame::FRAME_OPEN, seq, period);
				return;
			}
			else if (splited_list.at(1) == "close") {
				auto message = impl_->message_data_list_.at(id);
				message->frame_->setBehaviorOnSize(mof::widget::Frame::FRAME_CLOSE, seq, period);
				return;
			}
		}

		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_size_behavior
	void Environment::set_size_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector3D>::ptr& seq)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_size3_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "solid") {
			auto p = impl_->solid_data_list_.at(id);
			p->size_ref_.replace(0, p->solid_->world_transform(), std::make_shared<Scaling3D>(seq));
			return;
		}
		else if (splited_list.front() == "board") {
			auto p = impl_->board_data_list_.at(id);
			p->size_ref_.replace(0, p->board_->world_transform(), std::make_shared<Scaling3D>(seq));
			return;
		}


		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_rotate_behavior
	void Environment::set_rotate_behavior(int id, const mof::tstring& class_path, const Manipulator<Vector3D>::ptr& seq)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_rotate_behavior(" << ")");	

		std::vector<mof::tstring> splited_list;
		split(splited_list, class_path, is_any_of("."));
		if (splited_list.front() == "solid") {
			auto p = impl_->solid_data_list_.at(id);
			p->rotate_ref_.replace(0, p->solid_->world_transform(), std::make_shared<Rotation3D>(seq));
			return;
		}

		throw std::logic_error("unknown class_path:" + class_path);
	}
//}}}
//{{{ set_light
	void Environment::set_light(int id, int light_id)
	{
		using namespace boost::algorithm;
		using namespace mof;
		DEBUG_PRINT("set_light(" << id << "," << light_id << ")");	

		auto p = impl_->solid_data_list_.at(id);
		p->r_packet_->queue_hook_.unlink();// 一端外して
		p->r_packet_->light() = impl_->light_obj_list_.at(light_id)->light_;// 書き換えて
		impl_->rendering_queue_set_.insert(*p->r_packet_);// また追加
	}
//}}}
//{{{ joint	
	void Environment::joint
	(
		int id1,
		const mof::tstring& class_name1,
		const mof::tstring& joint_type,
		int id2, 
		const mof::tstring& class_name2
	) 
	{
		if (class_name1 == "solid" && class_name2 == "solid") {
			auto& solid1 = impl_->solid_data_list_.at(id1);
			auto& solid2 = impl_->solid_data_list_.at(id2);
			solid1->solid_->world_transform() << solid2->solid_->world_transform();
		}
		else if (class_name1 == "board" && class_name2 == "solid") {
			auto& obj1 = impl_->board_data_list_.at(id1);
			auto& obj2 = impl_->solid_data_list_.at(id2);
			obj1->board_->world_transform() << obj2->solid_->world_transform();
		}
		else throw std::logic_error("unkown join");
	}
//}}}
//{{{ quit
	void Environment::quit()
	{
		DEBUG_PRINT("quit");	
		Application::quit();
	}
//}}}
//{{{ update
	void Environment::update()
	{
		using namespace mof::script;
		using namespace std;
		foreach (auto& th, impl_->threads) th.second.update();
		foreach (auto& data, impl_->message_data_list_) {
			if (data.get() && data->is_enable_) data->frame_->update();
		}
		foreach (auto& data, impl_->menu_data_list_) {
			if (data.get() && data->is_enable_) data->frame_->update(); 
		}

		foreach (auto& data, impl_->picture_data_list_) {
			if (data.get() && data->is_enable_) data->sprite_->update(); 
		}

		foreach (auto& data, impl_->particlegen_data_list_) {
			if (data && data->is_enable_) data->particlegen_->update(); 
		}
		
		foreach (auto& data, impl_->solid_data_list_) {
			if (data && data->is_enable_) data->solid_->update(); 
		}
		
		foreach (auto& data, impl_->board_data_list_) {
			if (data && data->is_enable_) data->board_->update(); 
		}
		
		foreach (auto& data, impl_->light_obj_list_) {
			if (data) data->light_->update(); 
		}
		
		foreach (auto& data, impl_->static_sound_data_list_) {
			if (data) {
				if (!data->sound_->is_playing()) {
					data.reset();// 停止したサウンドは回収する
					continue;
				}
				data->sound_->update(); 
			}
		}
		
		foreach (auto& data, impl_->music_data_list_) {
			if (data) {
				data->sound_->update(); 
			}
		}



		impl_->camera_data_list_.front()->camera_->update();

	}
//}}}
//{{{ draw
	void Environment::draw() const
	{
		using namespace mof::script;
		using namespace std;

		mof::GraphicsDevice::clearZBuffer();

		mof::GraphicsDevice::setViewTransform
		(
			impl_->camera_data_list_.front()->camera_->getViewMatrix()
		);
	
		foreach (auto r_packet, impl_->rendering_queue_set_) {
			r_packet.draw();
		}

		mof::GraphicsDevice::setAlphaBlendingMode(mof::GraphicsDevice::BLENDING_MODE_ADD);
		mof::GraphicsDevice::setZBuffer(false);
		/*
		foreach (auto& data, impl_->particlegen_data_list_) {
			if (data) {
				auto& list = data->particlegen_->drawables(); 
				foreach (auto p, list) p->draw();
			}

		}
		*/
		mof::GraphicsDevice::setZBuffer(true);
		mof::GraphicsDevice::setAlphaBlendingMode(mof::GraphicsDevice::BLENDING_MODE_ALPHA);

	}
//}}}
//{{{ is_waiting
	bool Environment::is_waiting(size_t id) const
	{
		auto found = impl_->threads.find(id);
		return found != impl_->threads.end() && found->second.waiting_;
	}
//}}}
}
}
