#include <iostream>
#include <Ogre.h>
#include <OgreCodec.h>
#include <OgreFileSystem.h>
#include <OgreFileSystemLayer.h>
#include <OgreMaterialManager.h>
#include <OgreShaderGenerator.h>
int main()
{
	Ogre::LogManager logMgr;
	Ogre::ConfigFile pluginsCfg;
	Ogre::FileSystemLayer fsLayer("Ogre3D");
	pluginsCfg.load(fsLayer.getConfigFilePath("plugins.cfg"));
	logMgr.createLog("messages.log", true, true, true);
	Ogre::DefaultHardwareBufferManager
		bufferManager; // needed because we don't have a rendersystem
	Ogre::Root *ogre = new Ogre::Root("", "", "");
	auto pluginDir = Ogre::FileSystemLayer::resolveBundlePath(
		pluginsCfg.getSetting("PluginFolder") + "/");
	ogre->loadPlugin(pluginDir + "/Codec_STBI");
	auto codec = Ogre::Codec::getCodec("png");
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./resources/terrain", "FileSystem",
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true,
		true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../resources/terrain", "FileSystem",
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true,
		true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../../resources/terrain", "FileSystem",
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true,
		true);
	struct harbour {
		Ogre::Vector2i position;
		std::vector<int> neighbors;
	};
	std::vector<struct harbour> harbours;
	Ogre::Image map_image;
	map_image.load("world_map.png",
		       Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	Ogre::Image output_image(map_image);
	int pos_x = map_image.getWidth() / 2;
	int pos_y = map_image.getHeight() / 2 - 1;
	int seed = 1345;
	long long int step_count = 1000000000;
	int max_stuck = 10000000;
	long long int step = 0;
	int directions[18] = {};
	int i, j;
	for (i = 0; i < map_image.getHeight(); i++)
		for (j = 0; j < map_image.getWidth(); j++) {
			Ogre::ColourValue c = map_image.getColourAt(j, i, 0);
			c.g = 0;
			c.b = 0;
			output_image.setColourAt(c, j, i, 0);
		}
	for (i = -1; i < 2; i++)
		for (j = -1; j < 2; j++) {
			int index = (i + 1) * 3 + (j + 1);
			directions[index * 2 + 0] = i;
			directions[index * 2 + 1] = j;
			std::cout << index << " " << i << " " << j << std::endl;
		}
	Ogre::ColourValue mc = map_image.getColourAt(pos_x, pos_y, 0);
	std::cout << "base h: " << mc.r << std::endl;
	mc.g = 1.0;
	mc.b = 0.0;
	output_image.setColourAt(mc, pos_x, pos_y, 0);
	float l = 0.1f;
	int dir = seed % 8;
	if (dir >= 4)
		dir++;
	int state = 0;
	int stuck = 0;
	int backtrack = 0;
	int dir_x, dir_y;
	Ogre::ColourValue c, rc;
	std::list<std::pair<int, int> > queue;
	queue.push_back({ pos_x, pos_y });
	std::set<uint64_t> harbour_keys;
	while (!queue.empty()) {
		int i;
		std::pair<int, int> item = queue.front();
		queue.pop_front();
		c = map_image.getColourAt(item.first, item.second, 0);
		rc = output_image.getColourAt(item.first, item.second, 0);
		//		std::cout << "item: " << item.first << " " << item.second
		//			  << " color: " << c.r << " " << rc.b << std::endl;
		if (c.r < 0.5f && rc.b < 0.04f) {
			//			std::cout << "using " << item.first << " "
			//				  << item.second << std::endl;
			Ogre::ColourValue mc(c.r, 0, 1, 1);
			output_image.setColourAt(mc, item.first, item.second,
						 0);
			for (dir = 0; dir < 9; dir++) {
				bool bad_position = false;
				dir_x = directions[dir * 2];
				dir_y = directions[dir * 2 + 1];
				if (dir_x == 0 && dir_y == 0)
					continue;
				OgreAssert(dir_x > -2 && dir_x < 2,
					   "bad dir_x");
				OgreAssert(dir_y > -2 && dir_y < 2,
					   "bad dir_x");
				if (item.first + dir_x < 0 ||
				    item.first + dir_x >= map_image.getWidth())
					bad_position = true;
				if (item.second + dir_y < 0 ||
				    item.second + dir_y >=
					    map_image.getHeight())
					bad_position = true;
				if (bad_position)
					continue;
				mc = map_image.getColourAt(item.first + dir_x,
							   item.second + dir_y,
							   0);
				if (mc.r > 0.5f) {
					mc.g = 1.0f;
					mc.b = 1.0f;
					uint64_t harbour_key =
						((item.first >> 3) & 0xffff) |
						(((item.second >> 3) & 0xffff)
						 << 16);
					seed = seed * 1103515245 + 1234513;
					seed = seed / 65536 % 32768;
					int have_harbour = seed % 18 == 0;
					if (have_harbour &&
					    harbour_keys.find(harbour_key) ==
						    harbour_keys.end()) {
						output_image.setColourAt(
							mc, item.first + dir_x,
							item.second + dir_y, 0);
						harbour_keys.insert(
							harbour_key);
						harbours.push_back(
							{ { item.first + dir_x,
							    item.second +
								    dir_y },
							  {} });
					}
				} else
					queue.push_back(
						{ item.first + dir_x,
						  item.second + dir_y });
			}
		}
	}
	for (i = 0; i < map_image.getHeight(); i++)
		for (j = 0; j < map_image.getWidth(); j++) {
			Ogre::ColourValue c = map_image.getColourAt(j, i, 0);
			Ogre::ColourValue mc =
				output_image.getColourAt(j, i, 0);
			mc.r = c.r;
			mc.b = 0;
			output_image.setColourAt(mc, j, i, 0);
		}
	std::cout << "harbours count: " << harbours.size() << std::endl;
	for (i = 0; i < harbours.size(); i++) {
		int x = harbours[i].position[0];
		int y = harbours[i].position[1];
		queue.clear();
		queue.push_back({ x, y });
		std::cout << " looking for neighbors... " << i << std::endl;
		while (!queue.empty()) {
			std::pair<int, int> item = queue.front();
			queue.pop_front();
			c = map_image.getColourAt(item.first, item.second, 0);
			rc = output_image.getColourAt(item.first, item.second,
						      0);
			// std::cout << "item: " << c.r << " " << c.b << std::endl;
			if (c.r > 0.5f && rc.b < 0.04f) {
				Ogre::ColourValue mc(rc.r, rc.g, 1, 1);
				bool neighbor_found = false;
				if (rc.g >= 0.9f) {
					if (item.first != x ||
					    item.second != y) {
						std::cout << "neighbor found\n";
						std::cout << "\titem: " << rc.r
							  << " " << rc.b << " "
							  << item.first << " "
							  << item.second
							  << std::endl;
						//						output_image.setColourAt(
						//							mc, item.first,
						//							item.second, 0);
						neighbor_found = true;
					}
					if (neighbor_found) {
						int neighbor_id = -1;
						for (j = 0; j < harbours.size();
						     j++)
							if (harbours[j].position
									    [0] ==
								    item.first &&
							    harbours[j].position
									    [1] ==
								    item.second) {
								neighbor_id = j;
								break;
							}
						OgreAssert(
							neighbor_id >= 0,
							"neighbor not found");
						std::cout << "neighbor_id: "
							  << neighbor_id
							  << std::endl;
						if (std::find(harbours[i]
								      .neighbors
								      .begin(),
							      harbours[i]
								      .neighbors
								      .end(),
							      neighbor_id) ==
						    harbours[i].neighbors.end())
							harbours[i].neighbors.push_back(
								neighbor_id);
					}
				}
				output_image.setColourAt(mc, item.first,
							 item.second, 0);
				for (dir = 0; dir < 9; dir++) {
					bool bad_position = false;
					dir_x = directions[dir * 2];
					dir_y = directions[dir * 2 + 1];
					if (dir_x == 0 && dir_y == 0)
						continue;
					OgreAssert(dir_x > -2 && dir_x < 2,
						   "bad dir_x");
					OgreAssert(dir_y > -2 && dir_y < 2,
						   "bad dir_x");
					if (item.first + dir_x < 0 ||
					    item.first + dir_x >=
						    map_image.getWidth())
						bad_position = true;
					if (item.second + dir_y < 0 ||
					    item.second + dir_y >=
						    map_image.getHeight())
						bad_position = true;
					if (bad_position)
						continue;
					mc = output_image.getColourAt(
						item.first + dir_x,
						item.second + dir_y, 0);
					if (mc.r > 0.5f && mc.b < 0.04f) {
						queue.push_back(
							{ item.first + dir_x,
							  item.second +
								  dir_y });
					}
				}
			}
			// std::cout << "size: " << queue.size() << std::endl;
		}
		std::cout << "neighbors: " << i << " "
			  << harbours[i].neighbors.size() << std::endl;
		int k;
		for (k = 0; k < map_image.getHeight(); k++)
			for (j = 0; j < map_image.getWidth(); j++) {
				Ogre::ColourValue c =
					map_image.getColourAt(j, k, 0);
				Ogre::ColourValue mc =
					output_image.getColourAt(j, k, 0);
				mc.r = c.r;
				mc.b = 0;
				output_image.setColourAt(mc, j, k, 0);
			}
	}
#if 0
	for (i = 0; i < map_image.getHeight(); i++)
		for (j = 0; j < map_image.getWidth(); j++) {
			Ogre::ColourValue c = map_image.getColourAt(j, i, 0);
			Ogre::ColourValue mc =
				output_image.getColourAt(j, i, 0);
			mc.r = c.r;
			mc.b = c.b;
			output_image.setColourAt(mc, j, i, 0);
		}
#endif
	output_image.save("world_map_marked.png");

	return 0;
}
