#include "gamedata.h"
#include "mathutils.h"
#include "debug.h"
#include <list>
using std::list;

int GameData::PLAYER1_COLOUR = 0x4a483f;
int GameData::PLAYER2_COLOUR = 0x090c7a;

int GameData::BASE_BUILD_RADIUS = 75;
int GameData::NODE_RADIUS = 10;


GameData::GameData()
    : Graph(true)
{
    current = NULL;
    mode = MODE_BUILD;
    player1 = Player("player 1", PLAYER1_COLOUR);
    player2 = Player("player 2", PLAYER2_COLOUR);
    turn = &player1;
    build_type = VERTEX_PRODUCER; // first vertex is always a producer
}

GameData::~GameData() { }


GameVertex* GameData::get_current_vertex(bool only_mine) const
{
    if (only_mine)
    {
	if (current != NULL)
	{
	    GameVertex* ret = dynamic_cast<GameVertex*>(current);
	    if (ret->player == turn) return ret;
	}
	
	return NULL;
    }
    
    return dynamic_cast<GameVertex*>(current);
}


void GameData::clear_current_vertex()
{
    mode = MODE_SELECT;
    current = NULL;
}


void GameData::toggle_turn()
{
    if (!turn->has_played()) turn->set_played();

    if (!endgame())
    {
	if (turn == &player1) turn = &player2;
	else if (turn == &player2) turn = &player1;

	if (!turn->has_played())
	{
	    mode = MODE_BUILD;
	    build_type = VERTEX_PRODUCER;
	}
	else
	{
	    mode = MODE_SELECT;
	    build_type = VERTEX_NONE;
	    produce_energy();
	}
    }

    for (list<Vertex*>::iterator cursor = vertices.begin();
	 cursor != vertices.end(); cursor++)
    {
	GameVertex* v = dynamic_cast<GameVertex*>(*cursor);
	v->attacked = false;
    }

    current = NULL;
}


void GameData::handle_click(int x, int y)
{
    int r = 10;

    int colour;
    colour = turn->get_colour();

    GameVertex* v = dynamic_cast<GameVertex*>(vertex_at(x, y, 0));

    switch (mode)
    {
    case MODE_SELECT:
	// select_vertex handles making sure a point exists at (x,y)
	select_vertex(x, y);
	break;
    case MODE_BUILD:
	add_vertex(x, y, 0, r, colour);
	break;
    case MODE_ATTACK:
	if (v == NULL || v->player == turn) return;
	if (v->colour != colour) attack_vertex(v);
	break;
    case MODE_MOVE:
	if (current == NULL) return;
	int dist = MathUtils::distance(current->x, current->y, current->z,
				       x, y, 0);

	Vertex* temp = new Vertex(x, y, 0, NODE_RADIUS);
	if (crosses_edge(current, temp) ||
	    vertex_would_overlap(x, y, 0, NODE_RADIUS))
	{
	    delete temp;
	    return;
	}
	delete temp;

	if (dist <= get_range())
	{
	    if (turn->spend_energy(dist))
	    {
		current->x = x;
		current->y = y;
	    }
	}
	break;
    }
}


bool GameData::select_vertex(int x, int y)
{
    GameVertex * v = dynamic_cast<GameVertex*>(vertex_at(x, y, 0));
    if (v == NULL) return false;

    current = v;
    return true;
}


bool GameData::add_vertex(int x, int y, int z, int r, int colour)
{
    int energy_cost = get_energy_cost(build_type);
    if (!can_build(build_type)) return false;

    GameVertex* v = new GameVertex(x, y, z, r, colour, 10, build_type, turn);

    if (current == NULL)
    {
	// this is the special case for adding the first vertex for each player
	if (!turn->has_played())
	{
	    if (Graph::add_vertex(v))
	    {
		turn->spend_energy(energy_cost);
		toggle_turn();
		return true;
	    }
	}

	delete v;
	return false;
    }

    // same here - just a logic check
    if (current->player != turn)
    {
	delete v;
	return false;
    }

    // This is the range check...
    if (MathUtils::distance(current->x, current->y, 0, v->x, v->y, 0) >
	get_range())
    {
	delete v;
	return false;
    }

    if (Graph::add_vertex(v, current))
    {
	turn->spend_energy(energy_cost);
	mode = MODE_SELECT;
	return true;
    }

    delete v;
    return false;
}


// This class contains logic checks to keep the mode aligned with
// what is reasonable. Special cases inside the GameData class should just
// do mode = MODE_<whatever>
void GameData::set_mode(Mode m)
{
    // Stay in MODE_SELECT (or maybe MODE_BUILD) when current is null
    if (current == NULL) return;

    // If we're leaving MODE_BUILD, we should set the build_type back to none
    if (mode == MODE_ATTACK && m != mode) build_type = VERTEX_NONE;

    // The other modes all require current to match the player
    if (current->player == turn) mode = m;
}


int GameData::get_range(GameVertex* node)
{
    if (node == NULL) node = current;

    if (node == NULL) return 0;
    else if (mode == MODE_MOVE) return turn->get_energy();
    else if (mode == MODE_BUILD) return BASE_BUILD_RADIUS;
    else if (mode == MODE_ATTACK)
    {
	int range = BASE_BUILD_RADIUS;
	list<Vertex*> neighbors = node->neighbors;

	for(list<Vertex*>::iterator cursor = neighbors.begin();
	    cursor != neighbors.end(); cursor++)
	{
	    Vertex* v = *cursor;
	    range -= (100 - MathUtils::distance(v->x, v->y, v->z,
						node->x, node->y, node->z))
		/ 2;
	}
	if (range < 0) range = 0;
	return range;
    }
}


void GameData::attack_vertex(GameVertex* target)
{
    if (!current->attacked)
    {
	float atk = current->calculate_attack();
	float armor = target->calculate_armor();
	int damage = (int)(atk / armor);
	target->score -= damage;
	if (target->score <= 0) remove_vertex(target);
	current->attacked = true;
	
#ifdef DEBUG
	fprintf(stderr, "debug: GameData::attack_vertex(): atk=%.2f, armor=%.2f, damage=%d\n", atk, armor, damage);
#endif

    }

    mode = MODE_SELECT;
}


bool GameData::endgame()
{
    if (!(player1.has_played() && player2.has_played())) return false;

    if (get_colour(player1.get_colour()).empty())
    {
	debug("Gamedata::endgame(): player 2 wins\n");
	return true;
    }
    if (get_colour(player2.get_colour()).empty())
    {
	debug("Gamedata::endgame(): player 1 wins\n");
	return true;
    }

    return false;
}


int GameData::num_vertices_by_type(VertexType type, Player* player)
{
    int num = 0;

    for (list<Vertex*>::iterator cursor = vertices.begin();
	 cursor != vertices.end(); cursor++)
    {
	GameVertex* v = dynamic_cast<GameVertex*>(*cursor);

	if (player != NULL && v->player != player) continue;

	if (v->type == type) num++;
    }

    return num;
}


void GameData::produce_energy()
{
    int amount = 25 * num_vertices_by_type(VERTEX_PRODUCER, turn);
    turn->add_energy(amount);
}


// Checks the energy cost, and returns true only if
// it is currently possible to build the selected
bool GameData::can_build(VertexType type)
{
    int cost = get_energy_cost(type);
    if (cost > turn->get_energy() || cost == -1) return false;
    return true;
}


int GameData::get_energy_cost(VertexType type)
{
    int energy_cost = -1;

    switch (type)
    {
    case VERTEX_ATTACKER:
	energy_cost = 50;
	break;
    case VERTEX_DEFENDER:
	energy_cost = 25;
	break;
    case VERTEX_PRODUCER:
	if (num_vertices_by_type(type, turn) == 0) energy_cost = 0;
	else energy_cost = 25 << (num_vertices_by_type(type, turn) - 1);
	break;
    }

    return energy_cost;
}


void GameData::set_build_type(VertexType type)
{
    if (can_build(type)) build_type = type;
}