#include "script/MyEnvironment.hpp"
#include <boost/tuple/tuple.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <mof/script/ObjectData.hpp>
#include <mof/Sprite.hpp>
#include <memory>
#include <algorithm>
#include <mof/streams.hpp>
#include "widget/createMenuView.hpp"
#include "widget/createFrame.hpp"
#include <mof/Font.hpp>
#include <mof/widgets.hpp>
#include <mof/utilities.hpp>
#include <configure.hpp>
#include <mof/utilities.hpp>
#include <mof/widget/GridLayout.hpp>
#include "sqlite_wrapper.hpp"
#include "resource.hpp"
#include "world/WorldMapBuilder.hpp"
#include "world/World.hpp"

#define WORLD_WIDTH  6
#define WORLD_HEIGHT 6

namespace script
{
//{{{ Impl
	struct MyEnvironment::Impl
	{
		::sqlite_wrapper sqlite_;
		std::shared_ptr<::World> world_;
		
		Impl(){}
	};
//}}}
//{{{ constructor
	MyEnvironment::MyEnvironment(std::shared_ptr<mof::InputReceiver> input)
	: 
		mof::script::Environment
	  	(
		  	input,
			::getTextureResourceManager(::SYSTEM),
			::getMeshBuilderResourceManager(::SYSTEM)
		),
		impl_(new Impl())
	{
	}
//}}}
//{{{ destructor
	MyEnvironment::~MyEnvironment()
	{
	}
//}}}
//{{{ create_message_data
	std::unique_ptr<mof::script::MessageData> MyEnvironment::create_message_data
	(
		const mof::tstring& title, const mof::script::GameData::entry_t& style
	)
	{
		using namespace mof::widget;
		using namespace boost;
		
		mof::tstring title_bg("image/frame3.png");
		mof::tstring content_bg("image/frame0.png");

		// スタイルの解析
		foreach (const auto& p, style) {
			if (p.first == "view") {
				// 無効化する項目の解析
				std::vector<mof::tstring> splited_list;
				split(splited_list, p.second, is_any_of(","));
				foreach (auto& item, splited_list) { 
					if (item == "no-title-bg") title_bg = "";// タイトルの背景画像なし
					else if (item == "no-content-bg") content_bg = "";// コンテンツの背景画像なし
					else throw std::invalid_argument("invalid view parameter:" + item);
				}
			}
			else throw std::invalid_argument("invalid key:" + p.first);
		}


		auto compiler = std::make_shared<TextCompiler>
			(
				mof::Font(FONT_NAME_UME, FONT_SIZE_STANDARD),
				[] (const mof::tstring& path) { return ::getTextureResourceManager(::SYSTEM)->getResource(path);}
			);
 
		{
			// set behavior for page updated
			mof::KeyFrameAnimation<mof::Color4f>::KeyFrame keyFrames[] =
			{
				mof::makeKeyFrame(0,  mof::Color4f(0, 1, 1, 1)),
				mof::makeKeyFrame(15, mof::Color4f(0, 1, 1, 1)),
				mof::makeKeyFrame(45, mof::Color4f(1, 1, 1, 1)),
			};
			compiler->setBehaviorOnColor
			(
				TextCompiler::PAGE_OPEN,
				mof::makeKeyFrameAnimationHandler(keyFrames[0], mof::lastOf(keyFrames)),
				45
			);
		}

		{
			// set behavior for page updated
			compiler->setBehaviorOnColor
			(
				TextCompiler::PAGE_CLOSE,
				mof::makeKeyFrameAnimationHandler(0, mof::Color4f(1, 1, 1, 1), 15, mof::Color4f(0, 1, 1, 1)),
				15
			);
		}

		std::unique_ptr<mof::script::MessageData> result(new mof::script::MessageData);

		{
            result->message_ = std::make_shared<Pager>(mof::Vector2D(550, 125), compiler);

            result->frame_ = 
				std::shared_ptr<Frame>
                (
					createFrame
                	(
					 	title,
                    	content_bg,
						title_bg,
						result->message_->getView()
                	).release()
				);
        }
		
	
		return result;
	
	}
//}}}
//{{{ create_menu_data
	std::unique_ptr<mof::script::MenuData> MyEnvironment::create_menu_data
	(
		const mof::tstring& title,
		const std::vector<mof::tstring>& items,
		const mof::script::GameData::entry_t& style
	)
	{
		using namespace mof::widget;
		using namespace mof::script;
		using namespace boost;

		GridLayout::Direction direction = GridLayout::VERTICAL;
		std::vector<int> disable_items;
		mof::tstring title_bg("image/frame3.png");
		mof::tstring content_bg("image/frame0.png");
		bool enable_scrolling = false;
		
		// スタイルの解析
		foreach (const auto& p, style) {
			if (p.first == "direction") {
				if (p.second == "vertical") direction = GridLayout::VERTICAL;
				else if (p.second == "horizontal") direction = GridLayout::HORIZONTAL;
				else throw std::invalid_argument("invalid direction value:" + p.second);
			}
			else if (p.first == "disable") {
				// 無効化する項目の解析
				std::vector<mof::tstring> splited_list;
				split(splited_list, p.second, is_any_of(","));
				foreach (auto& item, splited_list) { 
					disable_items.push_back(lexical_cast<int>(item));
				}
			}
			else if (p.first == "view") {
				// 無効化する項目の解析
				std::vector<mof::tstring> splited_list;
				split(splited_list, p.second, is_any_of(","));
				foreach (auto& item, splited_list) { 
					if (item == "no-title-bg") title_bg = "";// タイトルの背景画像なし
					else if (item == "no-content-bg") content_bg = "";// コンテンツの背景画像なし
					else if (item == "scroll") enable_scrolling = true;
					else throw std::invalid_argument("invalid view parameter:" + item);
				}
			}
			else throw std::invalid_argument("invalid key:" + p.first);
		}

		std::unique_ptr<MenuData> result(new MenuData);

		{
			std::vector<MenuItem> menu_items;
			for (int i = 0; i < items.size(); ++i) {
				bool disable = find(disable_items.begin(), disable_items.end(), i) != disable_items.end();// disable_itemsより無効な項目か判断
				menu_items.push_back(MenuItem(createMenuView(items[i], disable)));
			}
            result->menu_ = std::make_shared<Menu>
				(
					menu_items.front(), menu_items.back(),
					mof::makeFactoryMethod<GridLayout>(direction, 0)
				);

			auto content_view = enable_scrolling
				? std::make_shared<scroll_view>(result->menu_)
				: result->menu_->getView();

            result->frame_ = 
				std::shared_ptr<Frame>
				(
                	createFrame
                	(
                    	title ,
                    	content_bg,
						title_bg,
						content_view
                	).release()
				);
        }

		return result;
	
	}
//}}}
//{{{ get_game_data
	mof::script::GameData::ptr MyEnvironment::get_game_data(const mof::tstring& resource_path)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("load_game_data(" << resource_path << ")");
		std::vector<mof::tstring> splited_list;
		split(splited_list, resource_path, is_any_of("."));

		if (splited_list[0] == "gamedata") {
			if (splited_list[1] == "item_profile") {
				return impl_->sqlite_.query_item_profile();
			}
			else if (splited_list[1] == "relic_profile") {
				return impl_->sqlite_.query_relic_profile();
			}
			else if (splited_list[1] == "ideal_profile") {
				return impl_->sqlite_.query_ideal_profile();
			}
			else if (splited_list[1] == "area_service") {
				int x = boost::lexical_cast<int>(splited_list[2]);
				int y = boost::lexical_cast<int>(splited_list[3]);
				::Area area = impl_->world_->area(x, y);
				::LandPowerLevel level = area.land_powerLevel;
				return impl_->sqlite_.query_area_service(level.ehves, level.bolony, level.stra);
			}
			else if (splited_list[1] == "service_info") {
				int service_id = boost::lexical_cast<int>(splited_list[2]);
				int grade = boost::lexical_cast<int>(splited_list[3]);
				return impl_->sqlite_.query_service_info(service_id, grade);
			}
			else if (splited_list[1] == "ideal_church_recipe") {
				int item_id = boost::lexical_cast<int>(splited_list[2]);
				return impl_->sqlite_.query_ideal_church_recipe(item_id);
			}
			else if (splited_list[1] == "shop_list") {
				int service_id = boost::lexical_cast<int>(splited_list[2]);
				int grade = boost::lexical_cast<int>(splited_list[3]);
				return impl_->sqlite_.query_shop_list(service_id, grade);
			}
			else if (splited_list[1] == "main_profile") {
				return impl_->sqlite_.query_main_profile();
			}
			else if (splited_list[1] == "area_profile") {
				int x = boost::lexical_cast<int>(splited_list[2]);
				int y = boost::lexical_cast<int>(splited_list[3]);
				auto p = impl_->sqlite_.query_area_profile(x, y);
				::Area area = impl_->world_->area(x, y);
				p->data_.front()["forest"] = boost::lexical_cast<mof::tstring>(area.powerLevel.forest);
				p->data_.front()["earth"] = boost::lexical_cast<mof::tstring>(area.powerLevel.earth);
				p->data_.front()["forest_flag"] = boost::lexical_cast<mof::tstring>(area.forest_element);
				p->data_.front()["earth_flag"] = boost::lexical_cast<mof::tstring>(area.earth_element);
				p->data_.front()["water_flag"] = boost::lexical_cast<mof::tstring>(area.water_element);
				p->data_.front()["land_flag"] = boost::lexical_cast<mof::tstring>(area.land);
				p->data_.front()["capital_flag"] = boost::lexical_cast<mof::tstring>(area.capital);
				p->data_.front()["land_scale"] = boost::lexical_cast<mof::tstring>(area.land_scale);
				p->data_.front()["picture"] = impl_->world_->make_area_picture_name(x, y);
				p->data_.front()["ehves_power"]  = "---";
				p->data_.front()["bolony_power"] = "---";
				p->data_.front()["stra_power"]   = "---";
				if (area.capital) 
				{
					::LandPowerLevel level = area.land_powerLevel;
					p->data_.front()["ehves_power"]  = boost::lexical_cast<mof::tstring>((int)level.ehves);
					p->data_.front()["bolony_power"] = boost::lexical_cast<mof::tstring>((int)level.bolony);
					p->data_.front()["stra_power"]   = boost::lexical_cast<mof::tstring>((int)level.stra);
				}
				return p;
			}
			else if (splited_list[1] == "world_profile") {
				auto p = std::make_shared<mof::script::GameData>();
				mof::script::GameData::entry_t entry;
				entry["term"] = boost::lexical_cast<mof::tstring>(impl_->world_->term());
				entry["regen"] = boost::lexical_cast<mof::tstring>(impl_->world_->regen());
				p->data_.push_back(entry);
				return p;
			}

		}
		else if (splited_list[0] == "system") {
			if (splited_list[1] == "client_region") {
				mof::Rectangle<int> rect = mof::GraphicsDevice::getClientRegion();
				mof::script::GameData::ptr p = std::make_shared<mof::script::GameData>();
				mof::script::GameData::entry_t entry;
				entry["width"] = boost::lexical_cast<mof::tstring>(rect.getWidth());
				entry["height"] = boost::lexical_cast<mof::tstring>(rect.getHeight());
				p->data_.push_back(entry);
				return p;
			}
		}

		DEBUG_PRINT("unknown resource path:" << resource_path);
		return std::make_shared<GameData>(); 
	}
//}}}	
//{{{ set_game_data
	void MyEnvironment::set_game_data(const mof::tstring& resource_path, const mof::script::GameData::ptr& game_data)
	{
		using namespace mof::script;
		using namespace boost;

		DEBUG_PRINT("save_game_data(" << resource_path << ")");
		std::vector<mof::tstring> splited_list;
		split(splited_list, resource_path, is_any_of("."));

		if (splited_list[0] == "gamedata") {
			if (splited_list[1] == "main_profile") {
				return impl_->sqlite_.update_main_profile(game_data);
			}
		}
		else if (splited_list[0] == "system") {
		}

		DEBUG_PRINT("unknown resource path:" << resource_path);
	}
//}}}	
//{{{ create_solid
	std::shared_ptr<mof::Graphics3D> MyEnvironment::create_solid(const mof::tstring& class_path)
	{
		using namespace mof::script;
		using namespace mof;
		using namespace boost;

		if (class_path == "world") {
			impl_->world_ = std::make_shared<::World>(WORLD_WIDTH, WORLD_HEIGHT, impl_->sqlite_);
			return impl_->world_->world_model();
		}
		else throw std::invalid_argument("unkown resource path:" + class_path);
	}
//}}}	
//{{{ put_element
	mof::frame_t MyEnvironment::put_element
	(
		mof::script::CommandSet::ElementType type,
		int x,
		int y
	)
	{
		using namespace mof;
		using namespace mof::script;
		using namespace boost;

		mof::tstring power_type;
		if (type == CommandSet::FOREST) power_type = "forest";
		else if (type == CommandSet::EARTH) power_type = "earth";
		else if (type == CommandSet::WATER) power_type = "water";
		impl_->world_->put_element(x, y, power_type);
		impl_->sqlite_.insert_history_profile(x, y, power_type);
		return 0;// TODO
	}
//}}}
//{{{ world_sleep
	void MyEnvironment::world_sleep()
	{
		impl_->sqlite_.insert_history_profile(0, 0, "sleep");
	}
//}}}
//{{{ land_region_create
	int MyEnvironment::land_region_create(unsigned int thread_id, int x, int y, int priority)
	{
		auto vertices = impl_->world_->make_land_region_vertices(x, y);
		auto texture = ::getTextureResourceManager(::SYSTEM)->getResource("image/element.png");
		return vertex_buffer_create(thread_id, vertices->front(), vertices->back(), texture, priority);
	}
//}}}
//{{{ save_profile
	void MyEnvironment::save_profile()
	{
		impl_->sqlite_.save();
	}
//}}}
}


