#include "allegro.h"

#ifdef _WIN32

#include "winalleg.h"
#include <winsock.h>

#endif

#include "defs.h"
#include "console.h"
#include "ai.h"
#include "ai_waypt.h"
#include "engine.h"
#include "gfx.h"
#include "math.h"
#include "maths.h"
#include "network.h"
#include "player.h"
#include "rules.h"
#include "server.h"

#include "ai_lua.h"
#include "ai_lua_wrap.h"

int traceX, traceY, trace, traceStep = 0;

void calcHit(int x, int y)
{
   traceX = x;
   traceY = y;
}

void tracePoint(BITMAP *bmp, int x, int y, int d)
{
   int i;
   long c;
   
   if (trace)
   if ((traceStep > 4 ? (traceStep=0) : (traceStep++)) == 0)
   {
   c = getpixel(material,x,y);
   if ((((c & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) && (((c & MAT_DESTROYABLE) == 0) || (!d))) ||
       (x<0) || (x>=mapSizeX) ||
       (y<0) || (y>=mapSizeY)
      )
   {
      calcHit(x, y);
      trace = 0;
   }
   }
}

void traceLine(int sX, int sY, int eX, int eY, int weap)
{
   calcHit(eX, eY);
   trace = 1;
   traceStep = 0;
   do_line(material, sX, sY, eX, eY, weap, tracePoint);
}


void lightPoint(BITMAP *bmp, int x, int y, int d)
{
   int i;
   long c;
   
   if (trace)
   {
   c = getpixel(material,x,y);
   if ((((c & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) && ((c & MAT_DESTROYABLE) == 0)) ||
       (x<0) || (x>=mapSizeX) ||
       (y<0) || (y>=mapSizeY)
      )
   {
      calcHit(x, y);
      trace = 0;
   }
   }
}

void traceLight(int sX, int sY, int eX, int eY)
{
   calcHit(eX, eY);
   trace = 1;
   traceStep = 0;
   do_line(material, sX, sY, eX, eY, 0, lightPoint);
}

void visPoint(BITMAP *bmp, int x, int y, int d)
{
   int i;
   long c;
   
   if (trace)
   if ((traceStep > 4 ? (traceStep=0) : (traceStep++)) == 0)
   {
      c = getpixel(material,x,y);
      if (((c & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) ||
         (x<0) || (x>=mapSizeX) ||
         (y<0) || (y>=mapSizeY)
         )
      {
         calcHit(x, y);
         trace = 0;
      }
   }
}

int visible(worm *src, int eX, int eY)
{
/*
   if (
       (eX + 10 < src->CurX) ||
       (eY + 10 < src->CurY) ||
       (eX - 10 > src->CurX + 320) ||
       (eY - 10 > src->CurY + 150)
      )
      return 0;
*/
   if (abs(eX - src->coords.x-4) + abs(eY - src->coords.y) < 32)   
      return 1;
   if (((src->dir == DIR_LEFT) && (eX - 10 > src->coords.x)) || ((src->dir == DIR_RIGHT) && (eX + 10 < src->coords.x)))
      return 0;
   calcHit(eX, eY);
   trace = 1;
   traceStep = 0;
   do_line(material, src->coords.x, src->coords.y + Engine.wormFireOffs, eX, eY, 0, visPoint);
   return trace;
}

void walkPoint(BITMAP *bmp, int x, int y, int d)
{
   int i;
   long c;
   
   if (trace)
   {
   c = getpixel(material,x,y);
   if ((((c & MAT_BLOCK_WORM) == MAT_BLOCK_WORM) && (((c & MAT_DESTROYABLE) == 0) || (!d))) ||
       (x<0) || (x>=mapSizeX) ||
       (y<0) || (y>=mapSizeY)
      )
   {
      calcHit(x, y);
      trace = 0;
   }
   }
}

void traceWalk(int sX, int sY, int eX, int eY, int weap)
{
   calcHit(eX, eY);
   trace = 1;
   do_line(material, sX, sY, eX, eY, weap, walkPoint);
}

void ropePoint(BITMAP *bmp, int x, int y, int d)
{
   int i;
   long c;
   
   if (trace)
   {
   c = getpixel(material,x,y);
   if (((c & MAT_BLOCK_ROPE) == MAT_BLOCK_ROPE) ||
       (x<0) || (x>=mapSizeX) ||
       (y<0) || (y>=mapSizeY)
      )
   {
      calcHit(x, y);
      trace = 0;
   }
   }
}

void traceRope(int sX, int sY, int eX, int eY)
{
   calcHit(eX, eY);
   trace = 1;
   do_line(material, sX, sY, eX, eY, 0, ropePoint);
}

static float shrapDmg(int i, int step)
{
   float dmg = 0;

   if (step > 7)
      return 0.0;

   dmg += (float)(wobjs[i].damage);

   if (wobjs[i].explosion > -1) // add explosion dmg if any
      dmg += specials[wobjs[i].explosion].damage;

   if (wobjs[i].shrapCount > 0) // add shrapnel
      dmg += (wobjs[i].shrapCount * shrapDmg(wobjs[i].shrapnel, step+1)) / 2.0;

}

// very simple way to predict actual damage of a weapon
float weapDmg(int i)
{
   float dmg = 0;

   dmg += weapons[i].damage * ((weapons[i].numObjects + 1)/2);
//   if (weapons[i].expOnWorm) // explodes on contact
   {
      if (weapons[i].explosion > -1) // add explosion dmg if any
         dmg += specials[weapons[i].explosion].damage * ((weapons[i].numObjects + 1)/2);
      if (weapons[i].shrapCount > 0) // add shrapnel
      {
         dmg += (weapons[i].shrapCount * shrapDmg(weapons[i].shrapnel, 1))/3;
      }
   }
      
   return dmg;
}

int getBestWeap(worm *tmpWorm, int reload)
{
   int best, bestscore, score, i;
   double dist;
   
   best = tmpWorm->curWeap;;
   bestscore = -99999;
   if (tmpWorm->enemy)
      dist = hypot(tmpWorm->pos.x - tmpWorm->enemy->pos.x, tmpWorm->pos.y - tmpWorm->enemy->pos.y);
   else
      dist = 1;
   if (dist == 0) dist = 1;
   for (i=0; i<5; i++)
   {
      score = 0;
      if (dist >= weapons[tmpWorm->weaps[i]].botRange)
         continue;
      if (i == tmpWorm->curWeap)
         score += AI_WEAP_CURRENT;
      if (!reload)
      {
         if (tmpWorm->ammo[i] > 0)
            score += AI_WEAP_HAS_AMMO;
         if (INT(weapDmg(tmpWorm->weaps[i])) >= tmpWorm->enemy->health)
            score += AI_WEAP_CAN_KILL;
         score -= (tmpWorm->next_fire[i] - gameTime);
         score -= INT(weapons[tmpWorm->weaps[i]].initSpeed / dist);
      }
      else
      {
         if (weapons[tmpWorm->weaps[i]].loadTime == -1)
         {
            score = -99999;
         }
         else
         {
            if (tmpWorm->ammo[i] == 0)
               score += AI_WEAP_HAS_AMMO;
            if (tmpWorm->reload[i] > gameTime)
               score -= (tmpWorm->reload[i] - gameTime);
         }
      }
      if (score > bestscore)
      {
         bestscore = score;
         best = i;
      }
   }
   return best;
}

int aiNeedReload(worm *tmpWorm)
{
   int i, cnt, canKill;
   cnt = 0;
   if (!tmpWorm->enemy)
   {
      for (i=0; i<5; i++)
      {
         if (tmpWorm->ammo[i] == 0)
         if (weapons[tmpWorm->weaps[i]].loadTime != -1)
            cnt++;
      }
      return (cnt > 1);
   }
   else
   {
      canKill = 0;
      for (i=0; i<5; i++)
      {
         if (tmpWorm->ammo[i] > 0)
         {
            canKill += tmpWorm->ammo[i] * INT(weapDmg(tmpWorm->weaps[i]));
         }
         else
         if (weapons[tmpWorm->weaps[i]].loadTime != -1)
            cnt++;
      }
      if ((canKill < tmpWorm->enemy->health) && (cnt > 2))
         return 1;
   }
   return 0;
}

void aiProcess(worm *tmpWorm)
{
   int spotX, spotY;

   bufor[tmpWorm->num] = aiGetMove(tmpWorm);
   worms[tmpWorm->num].sentKeys = 1;
/*
   if (tmpWorm->active)
   {
      if (tmpWorm->health > 0)
      {
         spotX = tmpWorm->x;
         if (spotX < 160)
            spotX = 160;
         if (spotX + 160 > mapSizeX)
            spotX = mapSizeX - 160;
         spotY = tmpWorm->y;
         if (spotY < 75)
            spotY = 75;
         if (spotY + 75 > mapSizeY)
            spotY = mapSizeY - 75;
         if (spotX < 160)
         {
            tmpWorm->aiCurX = 0;
         }
         else if (spotX + 160 > mapSizeX)
         {
            tmpWorm->aiCurX = mapSizeX - 320;
         }
         else {
            tmpWorm->aiCurX = spotX - 160;
         }

         if (spotY < 75)
         {
            tmpWorm->aiCurY = 0;
         }
         else if (spotY + 75 > mapSizeY)
         {
            tmpWorm->aiCurY = mapSizeY - 150;
         }
         else {
            tmpWorm->aiCurY = spotY - 75;
         }
      }
      else
      {
         tmpWorm->enemy = NULL;
         tmpWorm->aiState = AI_STATE_NONE;
      }
   }

   if (tmpWorm->aiState == AI_STATE_ENEMY)
   {
      ai_process_st_enemy(tmpWorm);
   }
   else
   if ((tmpWorm->aiState == AI_STATE_RUN)
       || (tmpWorm->aiState == AI_STATE_WANDER))
   {
      ai_process_st_run(tmpWorm);
   }
   else
   if (tmpWorm->aiState == AI_STATE_NONE)
   {
      ai_process_st_none(tmpWorm);
   }
   else
      Console.WriteF("Bot %d in undefined state.", tmpWorm->num);
*/
}

void initAI()
{
   int i, j;
   char tmp[255];
   int botCount;

   if ((isServer) && (!isDedicated))
   {
      override_config_file("Bots.cfg");

      botCount = get_config_int("wurmz!", "BotCount", 0);

      for (i=0; i<botCount; i++)
      {
         sprintf(buf, "Bot.%d", i);
//                    NEW BOT DATA READING
         sprintf(buf,"%s",get_config_string("wurmz!",buf,"player.cfg"));
         override_config_file(buf);
         sprintf(buf,"wurmz!");
//                    /NEW BOT DATA READING
         ClientConn[GP_NUM_PLAYERS] = -2;
         sprintf(worms[GP_NUM_PLAYERS].name, "%s", get_config_string(buf, "PlayerName", "Player"));
         worms[GP_NUM_PLAYERS].team = get_config_int(buf, "Team", 0);
         worms[GP_NUM_PLAYERS].skin = get_config_int(buf, "Skin", 0);
         addPlayer(GP_NUM_PLAYERS);
         renderSkin(GP_NUM_PLAYERS, worms[GP_NUM_PLAYERS].skin, worms[GP_NUM_PLAYERS].team);
         worms[GP_NUM_PLAYERS].active = 2;
         worms[GP_NUM_PLAYERS].isAi = 1;
         worms[GP_NUM_PLAYERS].aiPath = -1;
         for (j=0; j<5; j++)
         {
            sprintf(tmp, "Weapon.%d", j);
            worms[GP_NUM_PLAYERS].weaps[j] = get_config_int(buf, tmp, 0);
         }
         worms[GP_NUM_PLAYERS].sentKeys=1;

         override_config_file("Bots.cfg");
         GP_NUM_PLAYERS++;
      }   
   }
}

void freeAI()
{
   int i;
   
   for (i = 0; i< GP_NUM_PLAYERS; i++)
   {
      if (worms[i].isAi)
      {
         aiLuaFree(&worms[i]);
      }
   }
}
