#include "world/World.hpp"
#include "world/WorldMapBuilder.hpp"
#include <boost/multi_array.hpp>
#include <boost/lexical_cast.hpp>
#include <mof/Sprite.hpp>
#include <mof/streams.hpp>
#include <mof/script/ObjectData.hpp>
#include <mof/utilities.hpp>

// TODO もっと関数わけしましょー

//{{{ assign_land_id
void assign_land_id
(
	::AreaMap& world_map,
	int next_land_id,
	int x, 
	int y
)
{
	int height = world_map.size();	
	int width = world_map[0].size();	

	if (!world_map[y][x].land) return;
	if (world_map[y][x].land_id != 0) return;// 既に探査されている
	world_map[y][x].land_id = next_land_id;

	// 四隅に再帰的に探査
	if (y != 0) assign_land_id(world_map, next_land_id, x, y - 1);
	if (x != 0) assign_land_id(world_map, next_land_id, x - 1, y);
	if (y < height - 1) assign_land_id(world_map, next_land_id, x, y + 1);
	if (x < width - 1) assign_land_id(world_map, next_land_id, x + 1, y);
}
//}}}
//{{{ append_region_border
void append_region_border(std::vector<mof::VertexXYZRHWCUV>& vertices, float x, float y)
{
	const int size = 4;
	static const mof::Rectangle<float> trect(0, 0, 1.0 / 16, 1.0 / 16);
	mof::Vector2D pos = mof::GraphicsDevice::to2DPosition(mof::Vector3D(0.197 * x, -0.05, -0.19 * y));
	mof::Rectangle<float> region(pos.x , pos.y - 2 * size, pos.x + size, pos.y - 1 * size);//TODO  計算が合わないのは何故？
	mof::Sprite::append(vertices, region, mof::createColor(255, 255, 255), trect);
}
//}}}
//{{{ impl
struct World::impl
{
	::WorldMapBuilder::world_data_t world_data_;
	::AreaMap world_map_;
	int world_regen_;
	int world_term_;

	impl(size_t width, size_t height)
		: world_map_(boost::extents[height][width])
	{}
	~impl(){}
};
//}}}
//{{{ constructor
World::World
(
 	size_t width,
	size_t height,
	::sqlite_wrapper& sqlite	
)
	: pimpl_(new impl(width, height))
{
	using namespace mof;

	const size_t w = width;
	const size_t h = height;

	auto histories = sqlite.query_history_profile();
	mof::script::GameData stack;
			
	// ワールドを初期化
	::AreaMap& map = pimpl_->world_map_;
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			map[y][x] = ::Area();
		}
	}
	pimpl_->world_term_ = 1;
	pimpl_->world_regen_ = 0;

	foreach (auto& history, histories->data_) {
		if (history["name"] == "sleep") {
		pimpl_->world_term_++;
			foreach (auto& element, stack.data_) {
				mof::tstring name = element["name"];
				int x = boost::lexical_cast<int>(element["x"]);
				int y = boost::lexical_cast<int>(element["y"]);
				::AreaPowerLevel level1, level2, level3;
				if (name == "forest") {
					level1 = ::AreaPowerLevel(7, 0);
					level2 = ::AreaPowerLevel(5, 0);
					level3 = ::AreaPowerLevel(2, 0);
				}
				else if (name == "earth") {
					level1 = ::AreaPowerLevel(0, 7);
					level2 = ::AreaPowerLevel(0, 5);
					level3 = ::AreaPowerLevel(0, 2);
				}
				else if (name == "water") {
					level1 = ::AreaPowerLevel(4, 4);
					level2 = ::AreaPowerLevel(2, 2);
					level3 = ::AreaPowerLevel(1, 1);
				}

				map[y][x].powerLevel += level1;		
				if (x >= 1) map[y][x - 1].powerLevel += level2;		
				if (y >= 1) map[y - 1][x].powerLevel += level2;		
				if (x < w - 1) map[y][x + 1].powerLevel += level2;		
				if (y < h - 1) map[y + 1][x].powerLevel += level2;		
				if (x >= 1 && y >= 1) map[y - 1][x - 1].powerLevel += level3;		
				if (x >= 1 && y < h - 1) map[y + 1][x - 1].powerLevel += level3;		
				if (x < w - 1 && y >= 1) map[y - 1][x + 1].powerLevel += level3;		
				if (x < w - 1 && y < h - 1) map[y + 1][x + 1].powerLevel += level3;		
			}
			stack.data_.clear();
		}
		else stack.data_.push_back(history);
	}

	::WorldMapBuilder builder(map, 0.2f);
	auto p = builder.create();
	pimpl_->world_data_ = *p;

	// エレメントの配置されたエリアにcolor tween設定
	foreach (auto& element, stack.data_) {
		mof::tstring name = element["name"];
		int x = boost::lexical_cast<int>(element["x"]);
		int y = boost::lexical_cast<int>(element["y"]);
		if (name == "forest") {
			KeyFrameAnimation<mof::Color4f>::KeyFrame keyFrames[] = 
			{
				makeKeyFrame(0, mof::Color4f(0.7f, 1, 0.7f)),
				makeKeyFrame(30, mof::Color4f(1, 1, 1)),
				makeKeyFrame(60, mof::Color4f(0.7f, 1, 0.7f)),
			};
			auto tw1 = makeKeyFrameAnimationHandler(keyFrames[0], lastOf(keyFrames));
			auto tw2 = makeLoopHandler<Color4f>(tw1, 0, 90);
			int index = y * width + x;
			auto child = pimpl_->world_data_.world_->child(index)->getColorStream();
			pimpl_->world_data_.color_refs_[index].replace(0, child, tw2);
			pimpl_->world_map_[y][x].forest_element = true;
		}
		else if (name == "earth") {
			KeyFrameAnimation<mof::Color4f>::KeyFrame keyFrames[] = 
			{
				makeKeyFrame(0, mof::Color4f(1, 0.7f, 0.7f)),
				makeKeyFrame(30, mof::Color4f(1, 1, 1)),
				makeKeyFrame(60, mof::Color4f(1, 0.7f, 0.7f)),
			};
			auto tw1 = makeKeyFrameAnimationHandler(keyFrames[0], lastOf(keyFrames));
			auto tw2 = makeLoopHandler<Color4f>(tw1, 0, 90);
			int index = y * width + x;
			auto child = pimpl_->world_data_.world_->child(index)->getColorStream();
			pimpl_->world_data_.color_refs_[index].replace(0, child, tw2);
			pimpl_->world_map_[y][x].earth_element = true;
		}
		else if (name == "water") {
			KeyFrameAnimation<mof::Color4f>::KeyFrame keyFrames[] = 
			{
				makeKeyFrame(0, mof::Color4f(0.7f, 0.7f, 1)),
				makeKeyFrame(30, mof::Color4f(1, 1, 1)),
				makeKeyFrame(60, mof::Color4f(0.7f, 0.7f, 1)),
			};
			auto tw1 = makeKeyFrameAnimationHandler(keyFrames[0], lastOf(keyFrames));
			auto tw2 = makeLoopHandler<Color4f>(tw1, 0, 90);
			int index = y * width + x;
			auto child =pimpl_->world_data_.world_->child(index)->getColorStream();
			pimpl_->world_data_.color_refs_[index].replace(0, child, tw2);
			pimpl_->world_map_[y][x].water_element = true;
		}
	}

	// 種族値の計算
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			int forest = pimpl_->world_map_[y][x].powerLevel.forest;
			int earth = pimpl_->world_map_[y][x].powerLevel.earth;
			auto p = sqlite.query_area_profile(x, y);
			float coef_ehves  = boost::lexical_cast<float>(p->data_.front()["ehves"]) / 100;
			float coef_bolony = boost::lexical_cast<float>(p->data_.front()["bolony"]) / 100;
			float coef_stra   = boost::lexical_cast<float>(p->data_.front()["stra"]) / 100;
			::LandPowerLevel level = make_land_power(forest, earth, coef_ehves, coef_bolony, coef_stra);
			pimpl_->world_map_[y][x].land_powerLevel = level;
		}
	}

	// ランド生成
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			::LandPowerLevel level = pimpl_->world_map_[y][x].land_powerLevel;
			auto p = sqlite.query_area_service(level.ehves, level.bolony, level.stra);
			int n_services = boost::lexical_cast<int>(p->data_.size());
			if (n_services != 0) pimpl_->world_map_[y][x].land = true;
		}
	}

	// ランドの結合
	int next_land_id = 0;
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			auto& area = pimpl_->world_map_[y][x];
			if (!area.land || area.land_id != 0) continue;
			assign_land_id(pimpl_->world_map_, ++next_land_id, x, y);
		}
	}
	std::vector<LandPowerLevel> sum_levels(next_land_id + 1);// 最初の要素は使わない
	std::map<int, std::pair<int, int>> represents;// 代表エリア
	// ランドパワー計算
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			int land_id = pimpl_->world_map_[y][x].land_id;
			sum_levels[land_id] += pimpl_->world_map_[y][x].land_powerLevel;
			if (represents.find(land_id) == represents.end()) represents[land_id] = std::make_pair(x, y);
		}
	}

	// 代表エリアにランドパワー代入
	for (int land_id = 1; land_id <= next_land_id; ++land_id) {
		// TODO 代表エリア選出アルゴリズム
		auto coord = represents[land_id];
		pimpl_->world_map_[coord.second][coord.first].land_powerLevel = sum_levels[land_id];
		pimpl_->world_map_[coord.second][coord.first].capital = true;
	}
	
			
	// 再生度の計算
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			pimpl_->world_regen_ += map[y][x].powerLevel.forest;
			pimpl_->world_regen_ += map[y][x].powerLevel.earth;
		}
	}
	pimpl_->world_regen_ /= 10;
}
//}}}
//{{{ destructor
World::~World(){}
//}}}
//{{{ regen
int World::regen() const {return pimpl_->world_regen_;}
//}}}
//{{{ term
int World::term() const {return pimpl_->world_term_;}
//}}}
//{{{ world_model
std::shared_ptr<mof::Graphics3D> World::world_model() const
{
	return pimpl_->world_data_.world_;
}
//}}}
//{{{ area
::Area World::area(size_t x, size_t y) const
{
	return pimpl_->world_map_[y][x];
}
//}}}
//{{{ make_area_picture_name
mof::tstring World::make_area_picture_name(size_t x, size_t y) const
{	
	// ピクチャの決定
	int forest_number;
	::Area area = this->area(x, y);
	::AreaPowerLevel areapowerLevel = area.powerLevel;
	if (area.powerLevel.forest < 3) forest_number = 0;
	else if (area.powerLevel.forest < 6) forest_number = 1;
	else if (areapowerLevel.forest < 9) forest_number = 2;
	else forest_number = 3;

	int earth_number;
	if (area.powerLevel.earth < 3) earth_number = 0;
	else if (area.powerLevel.earth < 6) earth_number = 1;
	else if (area.powerLevel.earth < 9) earth_number = 2;
	else earth_number = 3;

	std::basic_ostringstream<TCHAR> os;
	os << _T("image/area/area") << earth_number << forest_number << _T(".jpg");
	return os.str();
}
//}}}
//{{{ make_land_region_vertices	
std::unique_ptr<World::vertices_t> World::make_land_region_vertices(size_t x, size_t y) const
{
	const int width = pimpl_->world_data_.width_;
	const int height = pimpl_->world_data_.height_;
	boost::multi_array<bool, 2> land_region(boost::extents[height][width]);
	int land_id = pimpl_->world_map_[y][x].land_id;
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			if (pimpl_->world_map_[y][x].land_id == land_id) land_region[y][x] = true;
			else land_region[y][x] = false;
		}
	}

	auto result = std::unique_ptr<::World::vertices_t>(new ::World::vertices_t());
	::World::vertices_t& vertices = *result;
	// TODO 同じ位置に点が表示されないようにする
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			if (!land_region[y][x])	continue;
			if (y == 0 || !land_region[y - 1][x]) {
				// land regionの上側の境界に点を置く
				append_region_border(vertices, x, y);
				append_region_border(vertices, x + 0.5, y);
				append_region_border(vertices, x + 1, y);
			}
			if (x == 0 || !land_region[y][x - 1]) {
				// land regionの左側の境界に点を置く
				append_region_border(vertices, x, y);
				append_region_border(vertices, x, y + 0.5);
				append_region_border(vertices, x, y + 1);
			}
			if (y == height - 1 || !land_region[y + 1][x]) {
				// land regionの下側の境界に点を置く
				append_region_border(vertices, x, y + 1);
				append_region_border(vertices, x + 0.5, y + 1);
				append_region_border(vertices, x + 1, y + 1);
			}
			if (x == width - 1 || !land_region[y][x + 1]) {
				// land regionの下側の境界に点を置く
				append_region_border(vertices, x + 1, y);
				append_region_border(vertices, x + 1, y + 0.5);
				append_region_border(vertices, x + 1, y + 1);
			}

		}
	}
	
	return result;
}
//}}}
//{{{ make_land_power
::LandPowerLevel World::make_land_power
(
	int forest,
	int earth,
	float coef_ehves,
	float coef_bolony,
	float coef_stra
) const
{
	LandPowerLevel level;
	level.ehves  = coef_ehves * 0.9 * forest + coef_ehves * 0.1 * earth;
	level.bolony = coef_bolony * 0.1 * forest + coef_bolony * 0.9 * earth;
	level.stra   = coef_stra * 0.5 * forest + coef_stra * 0.5 * earth;

	return level;
}
//}}}
//{{{ put_element	
void World::put_element(size_t x, size_t y, const mof::tstring& power_type)
{
	using namespace mof;
	using namespace mof::script;
	using namespace boost;

	Color4f col;
	if (power_type == "forest") {
		col = Color4f(0.2, 1, 0.2);
		pimpl_->world_map_[y][x].forest_element = true;
	} else if (power_type == "earth") {
		col = Color4f(1, 0.2, 0.2);
		pimpl_->world_map_[y][x].earth_element = true;
	} else if (power_type == "water") {
		col = Color4f(0.2, 0.2, 1);
		pimpl_->world_map_[y][x].water_element = true;
	}

	KeyFrameAnimation<mof::Color4f>::KeyFrame keyFrames[] = 
		{
			makeKeyFrame(0, mof::Color4f(1, 1, 1)),
			makeKeyFrame(90, mof::Color4f(1, 1, 1)),
			makeKeyFrame(120, col),
			makeKeyFrame(150, mof::Color4f(1, 1, 1)),
			makeKeyFrame(180, col),
		};
	auto tw1 = makeKeyFrameAnimationHandler(keyFrames[0], lastOf(keyFrames));
	auto tw2 = makeLoopHandler<Color4f>(tw1, 120, 210);
	int index = y * pimpl_->world_data_.width_ + x;
	auto child =pimpl_->world_data_.world_->child(index)->getColorStream();
	pimpl_->world_data_.color_refs_[index].replace(0, child, tw2);
}
//}}}
