//
//  MapLayer.cpp
//  GlobalGameJam15
//
//  Created by Cesar Parent on 24/01/2015.
//  Copyright (c) 2015 Cesar Parent. All rights reserved.
//

#include "MapLayer.h"
#include "Tile.h"
#include "MainCharacter.h"
#include "DungeonGame.h"
#include <math.h>

MapLayer::MapLayer(Game& game, Entity* character) :
	Layer(game, kMapWidth*kTileSize, kMapHeight*kTileSize)
{
	_player = character;
	_createMap();
	_layTiles();
}

MapLayer::~MapLayer()
{
	for(auto tile: _tiles)
	{
		delete tile;
	}
	_tiles.clear();
}

bool MapLayer::moveAllowed(Point<int> targetTile)
{
	return (_map[targetTile.x][targetTile.y] != 0) &&
		   !((DungeonGame*)_parent)->enemyOnTile(targetTile.x, targetTile.y);
}

Point<float> MapLayer::playerPosition()
{
	return _player->position;
}

void MapLayer::update(int timeInterval)
{
	Layer::update(timeInterval);
	flicker = 0.9f + (float)(rand()%100)*0.1f/100.0f;
}

float MapLayer::distanceToPlayer(int x, int y)
{
	float dx = _player->position.x - x;
	float dy = _player->position.y - y;
	return sqrt(dx*dx + dy*dy);
}

Point<float> MapLayer::orthoDistance(int x, int y)
{
	float dx = _player->position.x - x;
	float dy = _player->position.y - y;
	return Point<float>(dx, dy);
}

#pragma mark map creation functions

void MapLayer::_createMap()
{
	do
	{
		fillRandomMap();
		for(int i = 0; i < kCellullarSteps; ++i)
		{
			cellullarStep();
		}
	} while(!checkMap());
}

void MapLayer::_layTiles()
{
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			if(_map[x][y] != 0)
			{
				if((x%2 == 1) == (y%2 == 0))
				{
					Tile* newTile = new Tile(kTileFloor, x, y);
					_tiles.push_back(newTile);
					addChild(newTile);
				}
				else
				{
					Tile* newTile = new Tile(kTileFloor+1, x, y);
					_tiles.push_back(newTile);
					addChild(newTile);
				}
				
			}
			else if(y < kMapHeight-1)
			{
				if(_map[x][y+1] != 0)
				{
					Tile* newTile = new Tile(kTileWall, x, y);
					_tiles.push_back(newTile);
					addChild(newTile);
				}
				else
				{
					Tile* newTile = new Tile(kTileDirt, x, y);
					_tiles.push_back(newTile);
					addChild(newTile);
				}
			}
			else
			{
				Tile* newTile = new Tile(kTileDirt, x, y);
				_tiles.push_back(newTile);
				addChild(newTile);
			}
			
		}
	}
}

#pragma mark Flood-fill check

int MapLayer::floodFillStep(int x, int y, Uint8 newUID, std::stack<Point<int>>& stack)
{
	Point<int> point = {x, y};
	if(_checkMap[x][y] == 1)
	{
		_checkMap[x][y] = newUID;
		stack.push(point);
		return 1;
	}
	return 0;
}

int MapLayer::floodFill(int x, int y, Uint8 newUID)
{
	std::stack<Point<int>> tileStack;
	Point<int> seed = {x, y};
	Point<int> point;
	int count = 0;
	
	tileStack.push(seed);
	
	while(!tileStack.empty())
	{
		point = tileStack.top();
		tileStack.pop();
		if(point.x > 0)
		{
			count += floodFillStep(point.x-1, point.y, newUID, tileStack);
		}
		if(point.x < kMapWidth-1)
		{
			count += floodFillStep(point.x+1, point.y, newUID, tileStack);
		}
		if(point.y > 0)
		{
			count += floodFillStep(point.x, point.y-1, newUID, tileStack);
		}
		if(point.y < kMapHeight-1)
		{
			count += floodFillStep(point.x, point.y+1, newUID, tileStack);
		}
	}
	return count;
}

bool MapLayer::checkMap()
{
	int biggestRoom = 0, currentCount = 0;
	Uint8 biggestUID = 2, currentUID = 2;
	
	//copy map to check map
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			if(y == 0 || y == kMapHeight-1 || x == 0 || x == kMapWidth-1)
			{
				_map[x][y] = 0;
			}
			_checkMap[x][y] = _map[x][y];
		}
	}
	
	// start flood fill
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			if(_checkMap[x][y] == 1)
			{
				currentCount = floodFill(x, y, currentUID);
				if(currentCount > biggestRoom)
				{
					biggestRoom = currentCount;
					biggestUID = currentUID;
				}
				++currentUID;
			}
		}
	}
	
	if(biggestRoom < kMapRatio*kMapWidth*kMapHeight) return false;
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			if(_checkMap[x][y] != biggestUID)
			{
				_map[x][y] = 0;
			}
		}
	}
	return true;
}

#pragma mark cellullar automaton helper functions

void MapLayer::fillRandomMap()
{
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			_map[x][y] = (rand() < kChanceAliveStart*RAND_MAX);
		}
	}
}

void MapLayer::cellullarStep()
{
	Uint8 newMap[kMapWidth][kMapHeight];
	int neighbours;
	
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			neighbours = aliveNeighbours(x, y);
			if(_map[x][y])
			{
				newMap[x][y] = (neighbours < kDeathTreshold) ? 0 : 1;
			}
			else
			{
				newMap[x][y] = (neighbours >= kBirthTreshold) ? 1 : 0;
			}
		}
	}
	for(int x = 0; x < kMapWidth; ++x)
	{
		for(int y = 0; y < kMapHeight; ++y)
		{
			_map[x][y] = newMap[x][y];
		}
	}
}

int MapLayer::aliveNeighbours(int tileX, int tileY)
{
	int count = 0, neighbourX = 0, neighbourY = 0;
	
	for (int i = -1; i <= 1; ++i)
	{
		for (int j = -1; j <= 1; ++j)
		{
			neighbourX = tileX + i;
			neighbourY = tileY + j;
			if (i != 0 || j != 0)
			{
				if (neighbourX < 0 || neighbourY < 0 ||
					neighbourX >= kMapWidth || neighbourY >= kMapHeight )
				{
					++count;
				}
				else if (_map[neighbourX][neighbourY])
				{
					++count;
				}
			}
		}
	}
	return count;
}


