#pragma once
#include <mof/script/Environment.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/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 <vector>
#include <memory>
	
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<std::shared_ptr<mof::script::StaticSoundData>> static_sound_data_list_;
		std::vector<std::shared_ptr<mof::script::SoundSourceData>> sound_source_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::CameraData::ptr> camera_data_list_;// 現在、要素数は1つだけ
		std::vector<mof::script::vertex_buffer_obj::ptr> vertex_buffer_obj_list_;
		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)
		{
		}

	};
//}}}
//{{{ 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(const mof::tstring& title, const GameData::entry_t& style)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("message_create(" << title << ")");
		std::shared_ptr<MessageData> data = std::shared_ptr<MessageData>(create_message_data(title, style).release());
		impl_->message_data_list_.push_back(data);// TODO 再利用
		return impl_->message_data_list_.size() -1;
	}
//}}}	
//{{{ 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
	(
		const mof::tstring& title,
		const std::vector<mof::tstring>& items,
		const GameData::entry_t& style
	)
	{
		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());
		impl_->menu_data_list_.push_back(data);
		return impl_->menu_data_list_.size() -1;
	}
//}}}	
//{{{ 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;
		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(frame, [&waiting](){waiting = false;});// set waiting_ false after 'frame' 
		impl_->threads[id].waiting_ = true;
	}
//}}}
//{{{ 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);
	}
//}}}	
//{{{ 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(const mof::tstring& class_path)
	{
		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));

		
		impl_->picture_data_list_.push_back(data);// TODO 再利用
		return impl_->picture_data_list_.size() -1;

	}
//}}}	
//{{{ particlegen_create
	int Environment::particlegen_create()
	{
		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 再利用
		return impl_->particlegen_data_list_.size() -1;

	}
//}}}	
//{{{ solid_create
	int Environment::solid_create(const mof::tstring& class_path)
	{
		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->position_ref_.makeRef(Matrix3D::createIdentity());
		impl_->solid_data_list_.push_back(data);
		return impl_->solid_data_list_.size() -1;
	}
//}}}	
//{{{ board_create
	int Environment::board_create(const mof::tstring& class_path)
	{
		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>();
		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));
		}
		
		impl_->board_data_list_.push_back(data);// TODO 再利用
		return impl_->board_data_list_.size() -1;

	}
//}}}	
//{{{ 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(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;
			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
	(
	 	const mof::VertexXYZRHWCUV& front,
	 	const mof::VertexXYZRHWCUV& back,
		const mof::Texture::ptr& texture
	)
	{
		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->buffer_ = std::make_shared<mof::VertexBuffer<mof::VertexXYZRHWCUV>>(front, back, mof::PRIMITIVE_TYPE_TRIANGLELIST);
		obj->texture_ = texture;
		impl_->vertex_buffer_obj_list_.push_back(obj);
		return impl_->vertex_buffer_obj_list_.size() -1;
	}
//}}}	
// 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;

		if (class_path == "menu") {
			DEBUG_PRINT("menu_dispose(" << id << ")");
			impl_->menu_data_list_.at(id).reset();
		} else if (class_path == "message") {
			DEBUG_PRINT("message_dispose(" << id << ")");
			impl_->message_data_list_.at(id).reset();
		} else if (class_path == "picture") {
			DEBUG_PRINT("picture_dispose(" << id << ")");
			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_->solid_data_list_.at(id).reset();
		} else if (class_path == "board") {
			DEBUG_PRINT("board_dispose(" << id << ")");
			impl_->board_data_list_.at(id).reset();
		} else if (class_path == "sound") {
			DEBUG_PRINT("sound_dispose(" << id << ")");
			impl_->sound_source_data_list_.at(id).reset();
		} else if (class_path == "vertex_buffer") {
			DEBUG_PRINT("vertex_buffer_dispose(" << id << ")");
			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);
}
//}}}
//{{{ 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->board_->setColor(seq);
			p->board_->setFrameNumber(0);
			// 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);
			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() == "board") {
			auto p = impl_->board_data_list_.at(id);
			p->board_->setTextureCoordinates(seq);
			p->board_->setFrameNumber(0);
			// TODO
			return;
		}
		else if (splited_list.front() == "picture") {
			auto picture = impl_->picture_data_list_.at(id);
			picture->texcoord_ref_.replace(0, picture->sprite_->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->board_->setWorldMatrix(std::make_shared<Scaling3D>(seq));
			p->board_->setFrameNumber(0);
			// TODO
			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->light_ = impl_->light_obj_list_.at(light_id)->light_;
	}
//}}}

//{{{ 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->frame_->update();
		}
		foreach (auto& data, impl_->menu_data_list_) {
			if (data.get()) data->frame_->update(); 
		}

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

		foreach (auto& data, impl_->particlegen_data_list_) {
			if (data) data->particlegen_->update(); 
		}
		
		foreach (auto& data, impl_->solid_data_list_) {
			if (data) data->solid_->update(); 
		}
		
		foreach (auto& data, impl_->board_data_list_) {
			if (data) 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(); 
			}
		}

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

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

		mof::GraphicsDevice::setViewTransform
		(
			impl_->camera_data_list_.front()->camera_->getViewMatrix()
		);
	
		foreach (auto data, impl_->solid_data_list_) {
			if (data) {
				// ライトが設定されていれば、それを使う
				if (data->light_) {
					mof::GraphicsDevice::lightEnable(true);
					data->light_->setEnable(true);
				}
				else mof::GraphicsDevice::lightEnable(false);
				data->solid_->draw();
			}

		}

		mof::GraphicsDevice::setWorldTransform(mof::Matrix3D());
		foreach (auto& obj, impl_->vertex_buffer_obj_list_) {
			if (obj) {
				mof::GraphicsDevice::setTexture(obj->texture_.get());
				obj->buffer_->draw(); 
			}

		}

		foreach (auto data, impl_->board_data_list_) {
			if (data.get()) data->board_->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);

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


		foreach (auto data,  impl_->message_data_list_) {
			if (data.get()) data->frame_->getView()->draw();
		}
		
		foreach (auto data, impl_->menu_data_list_) {
			if (data.get()) data->frame_->getView()->draw();
		}//TODO ソート
	
		
	}
//}}}
//{{{ is_waiting
	bool Environment::is_waiting(size_t id) const
	{
		return impl_->threads[id].waiting_;
	}
//}}}
}
}
