//!
/**
 **************************************************************************************************************************************************************
 * Bot-Strike GB-bot
 **************************************************************************************************************************************************************
 * @file   Main.cpp
 * @author Cyanide
 * @author Guillaume Blanchard
 **************************************************************************************************************************************************************/

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

// std
#include <stdlib.h>

// BotStrike
#include "BotStrike.h"
#include "BotGB.h"
#include "AStar.h"

/**
 *
 */
DLL_EXPORT void update(BS::CMessage& msg)
{
    static BS::CBotGB bot(msg);
    bot.msg = msg;

    static int dir[8][2] = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} };

    switch(msg.type)
    {
		case BS::EMessage::Game::Rules:	
        {
            bot.grenades          = msg.bot_grenades;
            bot.grenades_area     = msg.grenade_area;
			bot.grenades_per_bots = msg.bot_life / msg.grenade_damage;

            return;
        }

		case BS::EMessage::Game::Init:
        {
            srand(msg.random);

            msg.type = BS::EMessage::Bot::Init;
            msg.team = bot.own_team;

			msg.print("ninja-bot");

			bot.ComputeFreeCells();

            return;
        }

		case BS::EMessage::Game::Pos:
        {
            bot.own.x  = msg.x;
            bot.own.y  = msg.y;
            bot.own.dx = msg.dx;
            bot.own.dy = msg.dy;
            return;
        }

		case BS::EMessage::Game::Damage:
        {
            /*msg.type = BS::EMessage::Bot::Move;
            msg.x    = bot.own.x;
            msg.y    = bot.own.y;            
            msg.dx   = -msg.dx;
            msg.dy   = -msg.dy;*/
            return;
        }

		case BS::EMessage::Game::Death:
        {
            return;
        }

		case BS::EMessage::Game::ChatTeam:
		{
			bot.state = BS::CBotGB::STATE_NINJA; // "Henshin!" received
			return;
		}

		case BS::EMessage::Game::Play:
        {
			//---------------------------------------- BERSERK ----------------------------------------
			if(bot.state == BS::CBotGB::STATE_BERSERK)
			{
				msg.type = BS::EMessage::Bot::Fire;
				if(--bot.fire_counter <= 0)
					bot.state = BS::CBotGB::STATE_NINJA;

				return;
			}
			//---------------------------------------- NINJA ----------------------------------------
			else if(bot.state == BS::CBotGB::STATE_NINJA)
			{
				// MAJ
				bot.own.x  = msg.x;
				bot.own.y  = msg.y;
				bot.own.dx = msg.dx;
				bot.own.dy = msg.dy;

				bot.FindEnemy();

				if(bot.ennemy_stopped > 0)//20) // ennemy stopped for long time
				{
					bot.state = BS::CBotGB::STATE_SEARCH; // search & destroy
				}

				i32 dx0, dy0;
				if(bot.AreAligned(bot.own, bot.ennemy, dx0, dy0))
					if(bot.RotateAndShoot(dx0, dy0))
						return;

				// Chat sometimes
				if((rand() % 100) == 0)
				{
					msg.type = BS::EMessage::Bot::ChatAll;
					msg.print("Your are dead but you don't know it yet!");
					return;
				}

				// MOVES
				msg.type = BS::EMessage::Bot::Move;

				// Path finding
				if(bot.ennemy_stopped == 0)
				{
					BS::CAStar::Instance().Initialize(&msg.arena);
					BS::CAStar::Instance().FindPath(bot.path, bot.own.x, bot.own.y, bot.ennemy.x, bot.ennemy.y, bot.ennemy.dx, bot.ennemy.dy, 
						3 /*bot.grenades_area + 1*/, BS::CAStar::DistanceToBehindGoal);
				}

				if(bot.path.size() > 0)
				{
					i32 x0 = bot.path.back()->X;
					i32 y0 = bot.path.back()->Y;

					i32 dx0, dy0;
					int distance;
					if(bot.AreAligned(BS::CBotInfo(x0, y0), bot.ennemy, dx0, dy0) && bot.CanSeeFrom(x0, y0, dx0, dy0, distance))
					{
						msg.dx = noe::Bounded(scast<int>(bot.ennemy.x + bot.ennemy.dx - bot.own.x), -1, 1);
						msg.dy = noe::Bounded(scast<int>(bot.ennemy.y + bot.ennemy.dy - bot.own.y), -1, 1);
					}
					else
					{
						msg.dx = x0 - bot.own.x;
						msg.dy = y0 - bot.own.y;
						msg.x  = x0;
						msg.y  = y0;					
						bot.path.pop_back();
					}
				}
				else
				{
					msg.dx = noe::Bounded(scast<int>(bot.ennemy.x + bot.ennemy.dx - bot.own.x), -1, 1);
					msg.dy = noe::Bounded(scast<int>(bot.ennemy.y + bot.ennemy.dy - bot.own.y), -1, 1);
				}
			}
			//---------------------------------------- SEARCH ----------------------------------------
			else if(bot.state == BS::CBotGB::STATE_SEARCH)
			{
				// MAJ
				bot.own.x  = msg.x;
				bot.own.y  = msg.y;
				bot.own.dx = msg.dx;
				bot.own.dy = msg.dy;

				// Shoot is the priority
				if(bot.CheckAttack())
					return;

				// MOVES
				bot.FindEnemy();

				msg.type = BS::EMessage::Bot::Move;

				// Path finding
				BS::CAStar::Instance().Initialize(&msg.arena);
				BS::CAStar::Instance().FindPath(bot.path, bot.own.x, bot.own.y, bot.ennemy.x, bot.ennemy.y, bot.ennemy.dx, bot.ennemy.dy, 
					2, BS::CAStar::DistanceToGoal);

				if(bot.path.size() > 0)
				{
					msg.dx = bot.path.back()->X - bot.own.x;
					msg.dy = bot.path.back()->Y - bot.own.y;
					msg.x  = bot.path.back()->X;
					msg.y  = bot.path.back()->Y;
					bot.path.pop_back();
				}
				else
				{
					msg.dx = noe::Bounded(scast<int>(bot.ennemy.x - bot.own.x), -1, 1);
					msg.dy = noe::Bounded(scast<int>(bot.ennemy.y - bot.own.y), -1, 1);
				}
			}
			//---------------------------------------- SCOUT ----------------------------------------
			else if(bot.state == BS::CBotGB::STATE_SCOUT)
			{
				// MAJ
				bot.own.x  = msg.x;
				bot.own.y  = msg.y;
				bot.own.dx = msg.dx;
				bot.own.dy = msg.dy;

				// Shoot is the priority
				if(bot.CheckAttack())
					return;

				// 
				static int iCounter = rand() % 1000;
				if(--iCounter <= 0)
				{
					iCounter = 1000;
					i32 diff = bot.ComputeTeamNumber();
					if((diff > 1) || (bot.ennemy_num <= 2))
					{
						bot.state = BS::CBotGB::STATE_NINJA;
						msg.type = BS::EMessage::Bot::ChatTeam;
						msg.print("Henshin!"); // transformation
					}
				}

				// MOVES
				msg.type = BS::EMessage::Bot::Move;

				// Orientation
				static int iShoot        = 0;
				static int iMovePeriod   = 0;
				static int iMoveStraight = 0;

				if(msg.dx!=0 || msg.dy!=0)
				{
					int iGuard = 0;
					do
					{
						++iGuard;
						++iMovePeriod;

						iShoot = (iShoot + 1) & 0x00000007;
					}
					while((!bot.CanLook(dir[iShoot][0], dir[iShoot][1])) && ((iGuard < 8)));
				}

				msg.dx = dir[iShoot][0];
				msg.dy = dir[iShoot][1];

				// Translation
				if(iMovePeriod >= 8)
				{
					iMovePeriod = 0;

					static int iMove = 0;
					static int dx0   = dir[iMove][0];
					static int dy0   = dir[iMove][1];

					i32 rot = bot.DeltaToRotation(dx0, dy0);
					i32 offset = (rand() & 0x00000001) * 6 + 1; // 1 or 7

					/*for(int i=0; i<8; i++)
					{
						if((msg.arena.cell(bot.own.x + dir[i][0], bot.own.y + dir[i][1]) & BS::ECell::Grenade)
							|| (msg.arena.cell(bot.own.x + dir[i][0] * 2, bot.own.y + dir[i][1] * 2) & BS::ECell::Grenade))
						{
							rot = i + 4;
							offset = 0;
							break;
						}
					}*/

					i32 move = (rot + offset) & 0x00000007;
					if((iMoveStraight > 7) && bot.CanMove(dir[move][0], dir[move][1]))
					{
						iMove = move;
						iMoveStraight = 0;

						dx0 = dir[iMove][0];
						dy0 = dir[iMove][1];
					}
					else
					{
						int k=0;

						while(k<8 && !bot.CanMove(dx0, dy0))
						{
							iMove = (iMove + offset + k++) & 0x00000007;
							iMoveStraight = 0;

							dx0 = dir[iMove][0];
							dy0 = dir[iMove][1];
						}
					}

					iMoveStraight++;
					
					int distance;
					if(!bot.CanSeeFrom(bot.own.x + dx0, bot.own.y + dy0, dx0, dy0, distance))
					{
						msg.x = bot.own.x + dx0;
						msg.y = bot.own.y + dy0; 
					}
				}
			}
			//---------------------------------------- DEFAULT ----------------------------------------
			else if(bot.state == BS::CBotGB::STATE_DEFAULT)
			{
				// MAJ
				bot.own.x  = msg.x;
				bot.own.y  = msg.y;
				bot.own.dx = msg.dx;
				bot.own.dy = msg.dy;

				// Chat sometimes
				if((rand() % 100) < 10)
				{
					msg.type = BS::EMessage::Bot::ChatAll;
					msg.print("Fire in the hole !");
					return;
				}

				// Shoot is the priority
				if(bot.CheckAttack())
					return;

				// MOVES
				msg.type = BS::EMessage::Bot::Move;

				// Orientation
				static int iShoot       = 0;
				static int iMovePeriod  = 0;

				if(msg.dx!=0 || msg.dy!=0)
				{
					int iGuard = 0;

					do
					{
						++iGuard;
						++iMovePeriod;

						iShoot = (iShoot+1) % 8;
					}
					while((!bot.CanLook(dir[iShoot][0], dir[iShoot][1])) && ((iGuard < 8)));
				}

				msg.dx = dir[iShoot][0];
				msg.dy = dir[iShoot][1];

				// Translation
				if(iMovePeriod >= 8)
				{
					iMovePeriod = 0;

					static int iMove = 0;
					static int dx0   = dir[iMove][0];
					static int dy0   = dir[iMove][1];

					int k=0;

					while(k<8 && !bot.CanMove(dx0,dy0))
					{
						++k;
						iMove = rand() % 8;

						dx0 = dir[iMove][0];
						dy0 = dir[iMove][1];
					}

					msg.x = bot.own.x + dx0;
					msg.y = bot.own.y + dy0;    
				}
				else
				{
					msg.x = bot.own.x;
					msg.y = bot.own.y;
				}
			}

            return; 
        }
    }
}

/**************************************************************************************************************************************************************/

/**************************************************************************************************************************************************************/


