//!
/**
 **************************************************************************************************************************************************************
 * BotGB implementation
 **************************************************************************************************************************************************************
 * @file   BotGB.cpp
 * @author Guillaume Blanchard
 **************************************************************************************************************************************************************/
#pragma once

/**************************************************************************************************************************************************************
 * Includes
 **************************************************************************************************************************************************************/

// BotStrike
#include "BotGB.h"

/**************************************************************************************************************************************************************
 * Namespace
 **************************************************************************************************************************************************************/

namespace BS {


/**
 * Cosntruct the bot class
 * @param	msg		The first message
 */
CBotGB::CBotGB(BS::CMessage& msg0): msg(msg0)
{
    own_team          = ETeam::Blue;
    own.x             = 0;
    own.y             = 0;
    own.dx            = 0;
    own.dy            = 0;
	friend_num        = 0;
	ennemy_num        = 0;
	ennemy_team       = (own_team == BS::ETeam::Blue) ? BS::ETeam::Red : BS::ETeam::Blue;
    ennemy.x          = -1;
    ennemy.y          = -1;
    ennemy.dx         = 0;
    ennemy.dy         = 0;
	ennemy_stopped    = 0;
    grenades          = 0;
    grenades_area     = 0;
    grenades_per_bots = 0;
	state             = STATE_SCOUT;
	fire_counter      = 0;
}

/**
 * Tell if a given is valid (ie. into the grid)
 * @param	x0		Cell coordinate.
 * @param	x0		Cell coordinate.
 * @return			TRUE if the cell bound to thhe grid.
 */
BOOL CBotGB::IsValidCell(i32 x0, i32 y0) const
{
	return !(x0 < 0 || y0 < 0 || x0 >= msg.arena.width || y0 >= msg.arena.height);
}

/**
 * Tell if a given is passable
 * @param	x0		Cell coordinate.
 * @param	x0		Cell coordinate.
 * @return			TRUE if the cell is passable.
 */
BOOL CBotGB::IsPassable(i32 x0, i32 y0) const
{
	return ((msg.arena.cell(x0, y0) == BS::ECell::Empty) || (msg.arena.cell(x0, y0) == BS::ECell::Cadavre));
}


/**
 * Compute the free cells list (the cells where there is no wall).
 * @return			The number of free cells.
 */
i32 CBotGB::ComputeFreeCells()
{
	free_cells.clear();

	// Search on the all arena
	loop(y0, msg.arena.height)
	{
		loop(x0, msg.arena.width)
		{
			if(!(msg.arena.cell(x0, y0) & BS::ECell::Box))
			{
				free_cells.push_back(x0);
				free_cells.push_back(y0);
			}
		}
	}

	return scast<i32>(free_cells.size());
}

/**
 * Find an ennemy.
 * @return			TRUE if an ennemy is founded.
 */
BOOL CBotGB::FindEnemy()
{
    static int dir[8][2] = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} };

	// Search around previous position
	if(ennemy.x != -1)
	{
		if(msg.arena.cell(ennemy.x, ennemy.y) & ennemy_team) // search at previous position
		{
			msg.arena.orientation(ennemy.x, ennemy.y, ennemy.dx, ennemy.dy);
			ennemy_stopped++;
			ennemy_move = ennemy.dy * 3 + ennemy.dx;
			return TRUE;
		}

		loop(i, 8) // search around the previous position
		{
			i32 x0 = ennemy.x + dir[i][0];
			i32 y0 = ennemy.y + dir[i][1];

			if(IsValidCell(x0, y0) && (msg.arena.cell(x0, y0) & ennemy_team))
			{
				msg.arena.orientation(x0, y0, ennemy.dx, ennemy.dy);
				ennemy.x = x0;
				ennemy.y = y0;
				ennemy_stopped = 0;
				ennemy_move = ennemy.dy * 3 + ennemy.dx;
				return TRUE;
			}
		}
	}

	// Search on the all arena
	i32 x0, y0;
	for(size_t i=0; i<free_cells.size();)
	{
		x0 = free_cells[i++];
		y0 = free_cells[i++];

		if(msg.arena.cell(x0, y0) & ennemy_team)
		{
			msg.arena.orientation(x0, y0, ennemy.dx, ennemy.dy);
			ennemy.x = x0;
			ennemy.y = y0;
			ennemy_stopped = 0;
			ennemy_move = ennemy.dy * 3 + ennemy.dx;
			return TRUE;
		}
	}

	ennemy.x = -1;
	ennemy.y = -1;
	return FALSE;
}

/**
 * Compute the number of bot in each team.
 * @return			The difference between the number of friends and the number of ennmies.
 */
i32 CBotGB::ComputeTeamNumber()
{
	friend_num = 0;
	ennemy_num = 0;

	// Search on the all valid arena cells
	i32 x0, y0;
	for(size_t i=0; i<free_cells.size();)
	{
		x0 = free_cells[i++];
		y0 = free_cells[i++];
		if(msg.arena.cell(x0, y0) & own_team)
			friend_num++;
		else if(msg.arena.cell(x0, y0) & ennemy_team)
			ennemy_num++;
	}

	return friend_num - ennemy_num;
}

/**
 * Tell if the bot can move in a given direction
 * @param	dx0		Direction.
 * @param	dy0		Direction.
 * @return			TRUE if road is safe.
 */
BOOL CBotGB::CanMove(int dx0, int dy0) const
{
    int x0 = own.x + dx0;
    int y0 = own.y + dy0;

    int distance = 1;

    if(!IsValidCell(x0, y0))
        return FALSE;

    if(msg.arena.cell(x0,y0) & BS::ECell::Blue)    { return FALSE; }
    if(msg.arena.cell(x0,y0) & BS::ECell::Red)     { return FALSE; }
    if(msg.arena.cell(x0,y0) & BS::ECell::Box)     { return FALSE; }
    if(msg.arena.cell(x0,y0) & BS::ECell::Grenade) { return FALSE; }

    for(;;)
    {
        ++distance;

        x0 += own.dx;
        y0 += own.dy;

        if(distance > grenades_area)
            return TRUE;

        if(!IsValidCell(x0, y0))
            return TRUE;

        if(msg.arena.cell(x0,y0) & BS::ECell::Grenade)
            return FALSE;
    }
}

/**
 * Tell if the bot can move the adjacent cell
 * @param	dx0		Direction.
 * @param	dy0		Direction.
 * @return			TRUE if the bot can move the adjacent cell.
 */
BOOL CBotGB::CanLook(int dx0, int dy0) const
{
    int x0 = own.x + dx0;
    int y0 = own.y + dy0;

    if(!IsValidCell(x0, y0))
        return FALSE;

    if(msg.arena.cell(x0,y0) & BS::ECell::Box)
		return FALSE;

    return TRUE;
}

/**
 * Check for ennemy in a given direction and shoot him if possible
 * @param	dx0		Direction.
 * @param	dy0		Direction.
 * @return			TRUE the bot shoot an ennemy.
 */
BOOL CBotGB::RotateAndShoot(int dx0, int dy0)
{
	int distance;
	if(CanSee(dx0, dy0, distance))
	{
		if((own.dx == dx0) && (own.dy == dy0))
		{
			if((grenades > 0) && (distance > grenades_area))
			{
				msg.type = BS::EMessage::Bot::Grenade;
				grenades--;
			}
			else
				msg.type = BS::EMessage::Bot::Fire;
			state = STATE_BERSERK;
			fire_counter = 20;
			return TRUE;
		}
		else
		{
			msg.type = BS::EMessage::Bot::Move;
			msg.dx   = dx0;
			msg.dy   = dy0;
			return TRUE;
		}
	}
	return FALSE;
}

/**
 * Check for ennemy in a given direction
 * @param	dx0			Direction.
 * @param	dy0			Direction.
 * @param	distance	Distance from player to target.
 * @return				TRUE if an ennemy is visible in the direction.
 */
BOOL CBotGB::CanSee(int dx0, int dy0, int& distance) const
{
	return CanSeeFrom(own.x, own.y, dx0, dy0, distance);
}

/**
 * Check for ennemy in a given direction
 * @param	x0			Position.
 * @param	y0			Position.
 * @param	dx0			Direction.
 * @param	dy0			Direction.
 * @param	distance	Distance from position to target.
 * @return				TRUE if an ennemy is visible in the direction.
 */
BOOL CBotGB::CanSeeFrom(int x0, int y0, int dx0, int dy0, int& distance) const
{
    distance = 0;

	for(;;)
    {
        x0 += dx0;
        y0 += dy0;

		distance++;

        if(!IsValidCell(x0, y0))
            return FALSE;

        if(msg.arena.cell(x0, y0) & own_team)
            return FALSE;

        if(msg.arena.cell(x0, y0) & BS::ECell::Box)
            return FALSE;

        if(msg.arena.cell(x0, y0) & ennemy_team)
            return TRUE;
    }
}

/**
 * Check if bot can shoot an ennemy.
 * @param	distance	Distance to founded target.
 * @return				TRUE if the bot can shoot an ennemy.
 */
BOOL CBotGB::CanShoot(int& distance) const
{
    if((own.dx==0) && (own.dy==0))
        return FALSE;

	return CanSee(own.dx, own.dy, distance);
}

/**
 * Check if bot can trough a grenade.
 * @param	distance	Distance to founded target.
 * @return				TRUE if the bot can trough a grenade.
 */
BOOL CBotGB::CanGrenade() const
{
    int maxgrenades = 0;

    if(grenades <= 0)
        return FALSE;

    if((own.dx==0) && (own.dy==0))
        return FALSE;

    int x0 = own.x;
    int y0 = own.y;

    int distance = 0;

    for(;;)
    {
        x0 += own.dx;
        y0 += own.dy;

        ++distance;

        if(!IsValidCell(x0, y0))
            return FALSE;

        if(msg.arena.cell(x0, y0) & own_team)
            return FALSE;

        if(msg.arena.cell(x0, y0) & BS::ECell::Box)
            return FALSE;

        if(msg.arena.cell(x0, y0) & BS::ECell::Grenade)
        {
            ++maxgrenades;

            if(maxgrenades >= grenades_per_bots)
                return FALSE;

            if(distance <= grenades_area + 1)
                return FALSE;
        }

        if(msg.arena.cell(x0, y0) & ennemy_team)
		{
			if(distance > grenades_area)
				return TRUE;
		}
    }
}

/**
 * Check if 2 bots are aligned.
 * @param	b1		First bot to check.
 * @param	b2		Second bot to check.
 * @param	dx0		Direction of the alignment (if any).
 * @param	dy0		Direction of the alignment (if any).
 * @return			TRUE if the 2 bots are aligned.
 */
BOOL CBotGB::AreAligned(const CBotInfo& b1, const CBotInfo& b2, i32& dx0, i32& dy0) const
{
	dx0 = b2.x - b1.x;
	dy0 = b2.y - b1.y;

	if(dx0 == 0)
	{
		noe::Bound((int&)dy0, -1, 1);
		return TRUE;
	}
	else if(dy0 == 0)
	{
		noe::Bound((int&)dx0, -1, 1);
		return TRUE;
	}
	else if(abs(dx0) == abs(dy0))
	{
		noe::Bound((int&)dx0, -1, 1);
		noe::Bound((int&)dy0, -1, 1);
		return TRUE;
	}
	return FALSE;
}

/**
 * Check bot can attack an ennemy.
 * @return			TRUE if the bot can attack an ennemy.
 */
BOOL CBotGB::CheckAttack()
{
	/*if(CanGrenade())
	{
		--grenades;

		msg.x    = 999;
		msg.type = BS::EMessage::Bot::Grenade;
		return TRUE;
	}

	if(CanShoot())
	{
		msg.type = BS::EMessage::Bot::Fire;
		return TRUE;
	}*/

	int distance;
	if(CanShoot(distance))
	{
		if((grenades > 0) && (distance > grenades_area))
		{
			--grenades;
			msg.x    = 999;
			msg.type = BS::EMessage::Bot::Grenade;
			return TRUE;
		}
		else
		{
			msg.type = BS::EMessage::Bot::Fire;
			return TRUE;
		}
	}

	return FALSE;
}

/**
 * Convert dx/dy to orientation index (0~8)
 * @param	dx0		Direction.
 * @param	dy0		Direction.
 * @return			Rotation index.
 */
i32 CBotGB::DeltaToRotation(i32 dx, i32 dy) const
{
	static i32 convert[] = { 0, 1, 2, 7, -1, 3, 6, 5, 4 };

	i32 index = (3 * dy) + dx + 4;

	return convert[index];
}

/**
 *
 */
/*void CBotGB::RotationToDelta(i32 rot, i32& dx, i32& dy) const
{
    static int dir[8][2] = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} };
	
	dx = dir[rot][0];
	dx = dir[rot][1];
}*/


/**************************************************************************************************************************************************************
 * Namespace
 **************************************************************************************************************************************************************/

}