#pragma once
#include <mof/script/LuaInterpreter.hpp>
#include <mof/script/lua_state.hpp>
#include <mof/script/CommandSet.hpp>
#include <lua.hpp>
#include "luabind/luabind.hpp"
#include <mof/ConsoleIO.hpp>
#include <mof/utilities.hpp>
#include <boost/bind.hpp>
#include <list>

namespace mof
{
namespace script
{
//{{{ Impl
	struct LuaInterpreter::Impl
	{
		std::map<size_t, lua_State*> threads_;
		
		Impl()
		{
		}

		~Impl()
		{
		}

	};
//}}}
//{{{ constructor
	LuaInterpreter::LuaInterpreter(const std::shared_ptr<CommandSet>& commands, const mof::tstring& filename)
	: impl_(new Impl())
	{
		
		int error = luaL_dofile(lua_state::instance().raw_lua(), filename.c_str());
		if(error)
		{
			throw std::runtime_error(lua_tostring(lua_state::instance().raw_lua(), -1));
		}
		auto& threads = impl_->threads_;
		lua_state::instance().bind(commands,
				[&threads](lua_State* th) -> size_t
					{
						size_t id = threads.size();
						threads.insert(std::make_pair(id, th));
						return id;
					}
				);// TODO LuaInterpreterとlua_stateの責任分割を再考
	}
//}}}
//{{{ destructor
	LuaInterpreter::~LuaInterpreter()
	{
	}
//}}}
//{{{ update
	void LuaInterpreter::update(const std::shared_ptr<CommandSet>& commands)
	{
		// TODO luaスレッドが死んだときにそれをCommandSetに通知するように

		foreach (auto& co, impl_->threads_) {
			if (!co.second) continue;// スレッドは既に終了している TODO 削除
			if (commands->is_waiting(co.first)) continue;// スレッドは休止中

			lua_state::instance().set_target_thread_id(co.first);
			int error = lua_resume(co.second, 0);
			if (!error) co.second = NULL;
			else if (error != LUA_YIELD) throw std::logic_error(std::string("lua error:") + lua_tostring(co.second, -1));
		}
	}
//}}}
//{{{ start
	void LuaInterpreter::start(const mof::tstring& entry_function)
	{
		lua_State* co = lua_newthread(lua_state::instance().raw_lua());
		if(!co) {
			DEBUG_PRINT(_T("ERROR-Failed lua_newthread") );
			throw std::runtime_error("Failed --- start lua script");
		}
		lua_getglobal(co, entry_function.c_str());
		impl_->threads_.insert(std::make_pair(impl_->threads_.size(), co));
	}
//}}}

}// namespace script
}// namespace mof
