#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
{
//{{{ routine
	struct routine_t
	{
		enum state
		{
			RUNNING,     //!< 起動中
			WAIT_DESTROY,//!< 破棄待ち
		};
		lua_State* thread_;     //!< Luaスレッド
		state state_;           //!< 現在のルーチンの状態
		unsigned int id_;       //!< スレッドID
		unsigned int parent_id_;//!< 親スレッドID 親がいなければid_と等しい

		routine_t(unsigned int id, unsigned int parent_id, lua_State* threads)
			: id_(id), parent_id_(parent_id), thread_(threads), state_(RUNNING)
		{
			// do nothing
		}
	};
//}}}
//{{{ Impl
	struct LuaInterpreter::Impl
	{
		std::list<routine_t> threads_;
		unsigned int next_id_;//!< 次のスレッドに割り当てるID
		
		Impl()
			: next_id_(0)
		{
		}

		~Impl()
		{
		}

		//! thread_idのスレッドと、その子スレッドに削除待ちフラグを立てる
		static void set_wait_destroy_recursive(std::list<routine_t>& threads, unsigned int thread_id)
		{
			for (auto itr = threads.begin(); itr != threads.end(); ++itr ) 
			{
				if (itr->id_ == thread_id)
				{
					itr->state_ = routine_t::WAIT_DESTROY;
				}
				else if (itr->parent_id_ == thread_id && itr->parent_id_ != itr->id_ )
				{
					// parent_id_ と id_ が一致するなら、それは親なし
					set_wait_destroy_recursive(threads, itr->id_);
				}
			}
		}

	};
//}}}
//{{{ 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));
		}

		// impl_にアクセスできないので
		auto& threads = impl_->threads_;
		auto& next_id_ = impl_->next_id_;
		lua_state::instance().bind(commands,
				[&threads, &next_id_](lua_State* th, unsigned int parent_id) -> size_t
					{
						// 末尾に追加
						threads.push_back(routine_t(next_id_++, parent_id, th));
						return next_id_ - 1;
					},
				[&threads](size_t thread_id) -> void
					{
						// このタイミングでは削除せず、フラグを設定するだけ
						LuaInterpreter::Impl::set_wait_destroy_recursive(threads, thread_id);
					}
				);// TODO LuaInterpreterとlua_stateの責任分割を再考
	}
//}}}
//{{{ destructor
	LuaInterpreter::~LuaInterpreter()
	{
	}
//}}}
//{{{ update
	void LuaInterpreter::update(const std::shared_ptr<CommandSet>& commands)
	{
		// 破棄待ちのスレッドの解放
		for (auto itr = impl_->threads_.begin(); itr != impl_->threads_.end(); ) 
		{
			if(itr->state_ == routine_t::WAIT_DESTROY)
			{
				// スレッドは削除待ち
				// 以降も別の場所からstopが呼ばれる可能性があるので、idは再利用しない

				commands->routine_stop(itr->id_);
				itr = impl_->threads_.erase(itr);
			}
			else 
			{
				++itr;
			}
		}

		for (auto itr = impl_->threads_.begin(); itr != impl_->threads_.end(); ++itr) 
		{
			if (itr->thread_ == NULL || commands->is_waiting(itr->id_))
			{
				continue;// スレッドは休止中
			}
			
			lua_state::instance().set_target_thread_id(itr->id_);
			int result = lua_resume(itr->thread_, 0);
			if (result == 0)
			{
				// 終了
				itr->thread_ = NULL;
			}
			else if (result != LUA_YIELD) 
			{
				// エラー
				throw std::logic_error(std::string("lua error:") + lua_tostring(itr->thread_, -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());
		unsigned int id = impl_->next_id_++;
		impl_->threads_.push_back(routine_t(id, id, co));

	}
//}}}

}// namespace script
}// namespace mof
