#include "ai.h"

#include "SDL_net.h"
#include "globals.h"
#include "action.h"
#include "misc.h"

using namespace std;

long abs_distance(long from_x, long from_y, long to_x, long to_y)
{
	return 	(from_x - to_x)*(from_x - to_x) + (from_y - to_y)*(from_y - to_y);
}

// compute path by moving around the edge of the walls/water and not repeating ourself
long path_distance(long from_x, long from_y, long to_x, long to_y, char* first_dir)
{
	vector< vector<bool> > path;
	path.resize(board.size());
	for (long x = 0; x < board.size(); ++x)
		path[x].resize(board[0].size());
	
	long steps = 0;
	bool allowed[4]; // NESW
	long store_from_x=from_x, store_from_y=from_y;
	
	while (!(from_x == to_x && from_y == to_y) && steps < 2000)
	{
		++steps;
		allowed[0] = true;
		allowed[1] = true;
		allowed[2] = true;
		allowed[3] = true;
		path[from_x][from_y] = true;

		//d("step",steps,from_x, from_y);
		
		if (board[from_x][from_y+1] == '~' || board[from_x][from_y+1] == '#' || path[from_x][from_y+1] == true)
			allowed[0] = false;
		if (board[from_x+1][from_y] == '~' || board[from_x+1][from_y] == '#' || path[from_x+1][from_y] == true)
			allowed[1] = false;
		if (board[from_x][from_y-1] == '~' || board[from_x][from_y-1] == '#' || path[from_x][from_y-1] == true)
			allowed[2] = false;
		if (board[from_x-1][from_y] == '~' || board[from_x-1][from_y] == '#' || path[from_x-1][from_y] == true)
			allowed[3] = false;
	
		
		if (from_y < to_y)
			if (allowed[0])
				from_y++;
			else if (allowed[1])
				from_x++;
			else if (allowed[2])
				from_y--;
			else
				from_x--;
		else if (from_y > to_y)
			if (allowed[2])
				from_y--;
			else if (allowed[3])
				from_x--;
			else if (allowed[0])
				from_y++;
			else
				from_x++;
		else if (from_x < to_x)
			if (allowed[1])
				from_x++;
			else if (allowed[2])
				from_y--;
			else if (allowed[3])
				from_x--;
			else
				from_y++;
		else if (from_x > to_x)
			if (allowed[3])
				from_x--;
			else if (allowed[0])
				from_y++;
			else if (allowed[1])
				from_x++;
			else
				from_y--;

		// if this is the first time, store the direction we went
		if (steps == 1)
			if (store_from_x > from_x)
				*first_dir = 'W';
			else if (store_from_x < from_x)
				*first_dir = 'E';
			else if (store_from_y > from_y)
				*first_dir = 'S';
			else if (store_from_y < from_y)
				*first_dir = 'N';
			
		if (allowed[0] == false && allowed[1]  == false && allowed[2] == false && allowed[3] == false)
		{
			return 999999999;
		}
	}
	if (from_x != to_x || from_y != to_y)
	{
		d("too many steps");
		return 99999999;
	}
	else
		return steps;
}

// pathfinding logic
char step_towards(long from_x, long from_y, long to_x, long to_y)
{
	char dir = 0;
	bool allowed[4] = {true, true, true, true}; // NESW

	if (board[from_x][from_y+1] != '~' && board[from_x][from_y+1] != '#')
		allowed[0] = true;
	if (board[from_x+1][from_y] == '~' || board[from_x+1][from_y] == '#')
		allowed[1] = false;
	if (board[from_x][from_y-1] == '~' || board[from_x][from_y-1] == '#')
		allowed[2] = false;
	if (board[from_x-1][from_y] == '~' || board[from_x-1][from_y] == '#')
		allowed[3] = false;

	
	if (from_y < to_y)
		if (allowed[0])
			dir = 'N';
		else if (allowed[1])
			dir = 'E';
		else if (allowed[2])
			dir = 'S';
		else
			dir = 'W';
		
	if (from_y > to_y)
		if (allowed[2])
			dir = 'S';
		else if (allowed[3])
			dir = 'W';
		else if (allowed[0])
			dir = 'N';
		else
			dir = 'E';

	if (from_x < to_x)
		if (allowed[1])
			dir = 'E';
		else if (allowed[2])
			dir = 'S';
		else if (allowed[3])
			dir = 'W';
		else
			dir = 'N';
		
	if (from_x > to_x)
		if (allowed[3])
			dir = 'W';
		else if (allowed[0])
			dir = 'N';
		else if (allowed[1])
			dir = 'E';
		else
			dir = 'S';
	
	
	if (dir == 0)
		e("i'm blocked");
	return dir;
}

void do_ai()
{
	// drop if on a spot
	vector<package> packages;
	for(map<long,package>::iterator i = me.packages.begin(); i != me.packages.end(); ++i)
		if (i->second.to_x == me.x && i->second.to_y == me.y)
			packages.push_back(i->second);
	if (packages.size() > 0)
	{
		issue_drop(100, packages);
		return;
	}
	
	// pick pkg up if possible
	packages.clear();
	for(map<long,package>::iterator i = available_packages.begin(); i != available_packages.end(); ++i)
		if (i->second.x == me.x && i->second.y == me.y)
			if (i->second.weight + me.burden <= capacity)
				packages.push_back(i->second);
	if (packages.size() > 0)
	{
		issue_pick(100, packages);
		return;
	}
	
	// move towards nearest package, drop or base
	long nearest_x=0, nearest_y=0;
	long nearest_distance=1000001, this_distance, max_abs_distance=2000;
	char this_dir, nearest_dir;
	for(map<long,package>::iterator i = me.packages.begin(); i != me.packages.end(); ++i)
	{
		d("x");
		this_distance = path_distance(me.x, me.y, i->second.to_x, i->second.to_y, &this_dir);
		d("dist", this_distance);
		if (this_distance < nearest_distance && this_distance != 0)
		{
			nearest_distance = this_distance;
			nearest_x = i->second.to_x;
			nearest_y = i->second.to_y;
			nearest_dir = this_dir;
		}
	}
	
	// if i have a package, don't go for a base
	if (me.packages.size() == 0)
	{
		for(map<long,package>::iterator i = available_packages.begin(); i != available_packages.end(); ++i)
		{
			// make sure we can handle it
			if (i->second.weight + me.burden <= capacity)
			{
				// limit the absolute distance in case of a large map
				if(abs_distance(me.x, me.y, i->second.x, i->second.y) < max_abs_distance)
				{
					this_distance = path_distance(me.x, me.y, i->second.x, i->second.y, &this_dir);
					if (this_distance < nearest_distance && this_distance != 0)
					{
						nearest_distance = this_distance;
						nearest_x = i->second.x;
						nearest_y = i->second.y;
						nearest_dir = this_dir;
					}
				}
			}
		}
		for(vector< pair<long, long> >::iterator i = bases.begin(); i != bases.end(); ++i)
		{
			if(abs_distance(me.x, me.y, i->first, i->second) < max_abs_distance)
			{
				this_distance = path_distance(me.x, me.y, i->first, i->second, &this_dir);
				if (this_distance < nearest_distance && this_distance != 0)
				{
					nearest_distance = this_distance;
					nearest_x = i->first;
					nearest_y = i->second;
					nearest_dir = this_dir;
				}
			}
		}
	}
	if (nearest_x == 0 && nearest_y == 0)
		e("no bases or pkgs known; I am lost");
	else
	{
		d("ai towards", nearest_x, nearest_y, nearest_distance);
		issue_move(1, nearest_dir);
	}
}
