#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "lauxlib.h"
#include "lualib.h"

#include <math.h>

#include "lephisto.h"
#include "log.h"
#include "pilot.h"
#include "player.h"
#include "physics.h"
#include "pack.h"
#include "rng.h"
#include "space.h"
#include "faction.h"
#include "llua.h"
#include "lluadef.h"
#include "ai.h"

/**
 * @file ai.c
 *
 * @brief Controls the Pilot AI.
 *
 * == AI ======================================================
 * 
 * -- Goal (Task) based AI with additional optimization.
 * AI uses the goal (task) based AI approach with tasks scripted
 * in lua. Additionally there is a task that is hardcoded and
 * obligatory in any AI script. The 'control' task, whose only
 * purpose is to assign tasks if there is none, and optimize
 * or change tasks if there are.
 *
 * Eg.. Pilot A is attacking Pilot B. Pilot C then comes along
 * the same system and is of the same faction as Pilot B. and
 * therefor attacks Pilot A. Pilot A would keep fighting pilot
 * B and until the control task comes in. Then the pilot could
 * run if it deems fit that Pilot C and Pilot B together are
 * both too strong for A. Or.. Attack C as it is an easy target
 * to finish.
 * Basically, there is many possibilities, and it's down to the
 * Lua fanatics to decide what to do.
 *
 * -- AI will follow basic tasks defined from Lua AI scripts.
 *  -- If task is NULL, AI will run "control" task.
 *  -- Task is continued every frame.
 *  -- "control" task is a special task that *must* exist in
 *     any given Pilot AI (missiles, and suck will use "seek".
 *    -- "control" task is not permanent, but transitory.
 *    -- "control" task sets another task.
 *  -- "control" task is also run at a set rate (depending on
 *      Lua global "control_rate") to choose optimal behaviour
 *      (task).
 */

/**
 * @def lua_regnumber(l, s, n)
 *
 * @brief Register a number constant n to name s (syntax is just like lua_regfunc). 
 */
#define lua_regnumber(l,s,n)  (lua_pushnumber(l,n), lua_setglobal(l,s))

/* Ai flags. */
#define ai_setFlag(f) (pilot_flags |= f)  /**< Set pilot flag f */
#define ai_isFlag(f)  (pilot_flags  & f)  /**< Check pilot flag f */
/* Flags. */
#define AI_PRIMARY    (1<<0)              /**< Firing primary weapon. */
#define AI_SECONDARY  (1<<1)              /**< Firing secondary weapon. */

/* file info. */
#define AI_PREFIX   "../scripts/ai/"      /**< AI file prefix. */
#define AI_SUFFIX   ".lua"                /**< AI file suffix. */
#define AI_INCLUDE  "include/"            /**< Where to search for includes. */

/* AI profiles. */
static AI_Profile* profiles = NULL;       /**< Array of AI_Profiles loaded. */
static int nprofiles = 0;                 /**< Number of AI_Profiles loaded. */

/* Extern pilot hacks. */
extern Pilot** pilot_stack;
extern int pilot_nstack;

static int ai_minbrakedist(lua_State* L); /* Minimal breaking distance. */
static int ai_accel(lua_State* L); /* Accelerate. */

/* Internal C routines. */
static void ai_run(lua_State* L, const char* funcname);
static int ai_loadProfile(char* filename);
static void ai_freetask(Task* t);
/* External C routines. */
void ai_attacked(Pilot* attacked, const unsigned int attacker); /* weapon.c */
void ai_create(Pilot* pilot);
/* C routines made external. */
void ai_destroy(Pilot* p);
void ai_think(Pilot* pilot);

/* Ai routines for Lua. */
/* Tasks. */
static int ai_pushtask(lua_State* L);           /* pushtask(string, number/pointer, number) */
static int ai_poptask(lua_State* L);            /* poptask() */
static int ai_taskname(lua_State* L);           /* Number taskname. */
/* Consult values. */
static int ai_gettarget(lua_State* L);          /* Pointer gettarget() */
static int ai_gettargetid(lua_State* L);        /* Number gettargetis() */
static int ai_getrndpilot(lua_State* L);        /* Number getrndpilot() */
static int ai_armour(lua_State* L);             /* armour() */
static int ai_shield(lua_State* L);             /* shield() */
static int ai_parmour(lua_State* L);            /* parmour() */
static int ai_pshield(lua_State* L);            /* pshield() */
static int ai_getdistance(lua_State* L);        /* Number getdist(Vec2) */
static int ai_getpos(lua_State* L);             /* getpos(number) */
static int ai_minbrakedist(lua_State* L);       /* Number minbrakedist() */
static int ai_cargofree(lua_State* L);          /* Number cargofree(). */
/* Boolean expressions. */
static int ai_exists(lua_State* L);             /* boolean exists */
static int ai_ismaxvel(lua_State* L);           /* Boolean ismaxvel() */
static int ai_isstopped(lua_State* L);          /* Boolean isstopped() */
static int ai_isenemy(lua_State* L);            /* boolean isenemy(number). */
static int ai_isally(lua_State* L);             /* boolean isally(number). */
static int ai_incombat(lua_State* L);           /* boolean incombat([number]) */
static int ai_haslockon(lua_State* L);          /* boolean haslockon() */

/* Movement. */
static int ai_accel(lua_State* L);              /* accel(number); nuimber <= 1. */
static int ai_turn(lua_State* L);               /* turn(number); abs(number) <= 1. */
static int ai_face(lua_State* L);               /* face(number/pointer) */
static int ai_aim(lua_State* L);                /* aim(number). */
static int ai_brake(lua_State* L);              /* Brake() */
static int ai_getnearestplanet(lua_State* L);   /* pointer getnearestplanet() */
static int ai_getrndplanet(lua_State* L);       /* pointer getrndplanet() */
static int ai_getlandplanet(lua_State* L);      /* pointer getlandplanet() */
static int ai_hyperspace(lua_State* L);         /* [number] hyperspace() */
static int ai_stop(lua_State* L);               /* stop() */
/* Combat. */
static int ai_combat(lua_State* L);             /* combat(number) */
static int ai_settarget(lua_State* L);          /* settarget(number) */
static int ai_secondary(lua_State* L);          /* string secondary() */
static int ai_hasturrets(lua_State* L);         /* bool hasturrets() */
static int ai_shoot(lua_State* L);              /* shoot(number) number = 1,2,3. */
static int ai_getenemy(lua_State* L);           /* number getenemy(). */
static int ai_hostile(lua_State* L);            /* hostile(number). */
static int ai_getweaprange(lua_State* L);       /* number getweaprange(). */
/* Timers. */
static int ai_settimer(lua_State* L);           /* settimer(number, number) */
static int ai_timeup(lua_State* L);             /* boolean timeup(number) */
/* Messages. */
static int ai_comm(lua_State* L);               /* comm(string) */
static int ai_broadcast(lua_State* L);          /* broadcast(string) */
/* Loot. */
static int ai_credits(lua_State* L);            /* credits(number). */
static int ai_cargo(lua_State* L);              /* cargo(name, quantity). */
static int ai_shipprice(lua_State* L);          /* shipprice(). */

static const luaL_Reg ai_methods[] = {
  /* Tasks. */
  { "pushtask",             ai_pushtask         },
  { "poptask",              ai_poptask          },
  { "taskname",             ai_taskname         },
  /* Sanity checks. */
  { "exists",               ai_exists           },
  { "ismaxvel",             ai_ismaxvel         },
  { "isstopped",            ai_isstopped        },
  { "isenemy",              ai_isenemy          },
  { "isally",               ai_isally           },
  /* Get's. */
  { "incombat",             ai_incombat         },
  { "haslockon",            ai_haslockon        },
  { "target",               ai_gettarget        },
  { "targetid",             ai_gettargetid      },
  { "rndpilot",             ai_getrndpilot      },
  { "armour",               ai_armour           },
  { "shield",               ai_shield           },
  { "parmour",              ai_parmour          },
  { "pshield",              ai_pshield          },
  { "dist",                 ai_getdistance      },
  { "pos",                  ai_getpos           },
  { "minbrakedist",         ai_minbrakedist     },
  { "cargofree",            ai_cargofree        },
  { "nearestplanet",        ai_getnearestplanet },
  { "rndplanet",            ai_getrndplanet     },
  { "landplanet",           ai_getlandplanet    },
  /* Movement. */
  { "accel",                ai_accel            },
  { "turn",                 ai_turn             },
  { "face",                 ai_face             },
  { "brake",                ai_brake            },
  { "stop",                 ai_stop             },
  { "hyperspace",           ai_hyperspace       },
  /* Combat. */
  { "aim",                  ai_aim              },
  { "combat",               ai_combat           },
  { "settarget",            ai_settarget        },
  { "secondary",            ai_secondary        },
  { "hasturrets",           ai_hasturrets       },
  { "shoot",                ai_shoot            },
  { "getenemy",             ai_getenemy         },
  { "hostile",              ai_hostile          },
  { "getweaprange",         ai_getweaprange     },
  /* Timers. */
  { "settime",              ai_settimer         },
  { "timeup",               ai_timeup           },
  /* Messages. */
  { "comm",                 ai_comm             },
  { "broadcast",            ai_broadcast        },
  /* Loot. */
  { "setcredits",           ai_credits          },
  { "setcargo",             ai_cargo            },
  { "shipprice",            ai_shipprice        },
  { 0, 0 } /* End. */
};

/* Current pilot "thinking" and assorted variables. */
static Pilot* cur_pilot     = NULL;
static double pilot_acc     = 0.;
static double pilot_turn    = 0.;
static int pilot_flags      = 0;
static int pilot_target     = 0;

/* Ai status: 'Create' functions that can't be used elsewhere. */
#define AI_STATUS_NORMAL  1   /**< Normal AI function behaviour. */
#define AI_STATUS_CREATE  2   /**< AI is running create function. */
static int ai_status = AI_STATUS_NORMAL;

/**
 * @fn static void ai_run(lua_State* L, const char* funcname)
 *
 * @brief Attemps to run a function.
 *
 *    @param[in] L Lua state to run function on.
 *    @param[in] funcname Function to run.
 */
static void ai_run(lua_State* L, const char* funcname) {
  lua_getglobal(L, funcname);
  if(lua_pcall(L, 0, 0, 0))
    /* Errors accured. */
    WARN("Pilot '%s' ai -> '%s' : %s",
         cur_pilot->name, funcname, lua_tostring(L,-1));
}

/**
 * @fn void ai_destroy(Pilot* p)
 *
 * @brief Destroys the ai part of the pilot.
 *    
 *    @param[in] p Pilot to destroy it's AI part.
 */
void ai_destroy(Pilot* p) {
  if(p->task)
    ai_freetask(p->task);
}

/**
 * @fn int ai_init(void)
 *
 * @brief Initializes the AI stuff which is basically Lua.
 *
 *    @return 0 on no errors.
 */
int ai_init(void) {
  char** files;
  uint32_t nfiles, i;

  /* Get the file list. */
  files = pack_listfiles(data, &nfiles);

  /* Load the profiles. */
  for(i = 0; i < nfiles; i++)
    if((strncmp(files[i], AI_PREFIX, strlen(AI_PREFIX))==0) &&  /* Prefixed. */
          (strncmp(files[i] + strlen(AI_PREFIX), AI_INCLUDE, /* Not an include. */
                   strlen(AI_INCLUDE)) != 0) &&
          (strncmp(files[i] + strlen(files[i]) - strlen(AI_SUFFIX), /* Suffixed. */
                   AI_SUFFIX, strlen(AI_SUFFIX))==0))
      if(ai_loadProfile(files[i])) /* Load the profiles. */
        WARN("Error loading AI profile '%s'", files[i]);

  /* Free the char allocated by pack. */
  for(i = 0; i < nfiles; i++)
    free(files[i]);
  free(files);

  DEBUG("Loaded %d AI profile%c", nprofiles, (nprofiles==1)?' ':'s');

  return 0;
}

/**
 * @fn static int ai_loadProfile(char* filename)
 *
 * @brief Initializes an AI_Profile and adds it to the stack.
 * 
 *    @param[in] filename File to create the profile from.
 *    @return 0 on no error.
 */
static int ai_loadProfile(char* filename) {
  char* buf = NULL;
  uint32_t bufsize = 0;
  lua_State* L;

  profiles = realloc(profiles, sizeof(AI_Profile)*(++nprofiles));

  profiles[nprofiles-1].name =
      malloc(sizeof(char)*
             (strlen(filename)-strlen(AI_PREFIX)-strlen(AI_SUFFIX))+1);

  snprintf(profiles[nprofiles-1].name,
           strlen(filename)-strlen(AI_PREFIX)-strlen(AI_SUFFIX)+1,
           "%s", filename+strlen(AI_PREFIX));

  profiles[nprofiles-1].L = llua_newState();

  if(profiles[nprofiles-1].L == NULL) {
    ERR("Unable to create a new Lua state");
    return -1;
  }

  L = profiles[nprofiles-1].L;

  /* Open basic Lua stuff. */
  llua_loadBasic(L);

  /* Constants. */
  lua_regnumber(L, "player", PLAYER_ID); /* Player id. */

  /* Register C funstions in Lua. */
  luaL_register(L, "ai", ai_methods);
  lua_loadRnd(L);

  /* Now load the file, since all the functions have been previously loaded. */
  buf = pack_readfile(DATA, filename, &bufsize);
  if(luaL_dobuffer(L, buf, bufsize, filename) != 0) {
    ERR("Error loading AI file: %s\n"
        "%s\n"
        "Most likely Lua file has improper syntax, please check.",
        filename, lua_tostring(L, -1));
    return -1;
  }

  free(buf);

  return 0;
}

/* @fn AI_Profile* ai_getProfile(char* name)
 *
 * @brief Get the AI_Profile by name.
 *
 *    @param[in] name Name of the profile to get.
 *    @return The profile or NULL on error.
 */
AI_Profile* ai_getProfile(char* name) {
  if(profiles == NULL) return NULL;

  int i;
  for(i = 0; i < nprofiles; i++)
    if(strcmp(name, profiles[i].name)==0)
      return &profiles[i];

  WARN("AI Profile '%s' not found in AI stack", name);
  return NULL;
}

/**
 * @fn void ai_exit(void)
 *
 * @brief Clean up the gloabl AI.
 */
void ai_exit(void) {
  int i;
  for(i = 0; i < nprofiles; i++) {
    free(profiles[i].name);
    lua_close(profiles[i].L);
  }
  free(profiles);
}

/**
 * @fn void ai_think(Pilot* pilot)
 *
 * @brief Heart of the AI, brains of the pilot.
 *
 *    @param pilot Pilot that needs to think.
 */
void ai_think(Pilot* pilot) {
  lua_State* L;

  cur_pilot = pilot; /* Set current pilot being processed. */
  L = cur_pilot->ai->L; /* Set the AI profile to the current pilot's. */

  /* Clean up some variables. */
  pilot_acc     = 0.;
  pilot_turn    = 0.;
  pilot_flags   = 0;
  pilot_target  = 0;

  /* Control function if pilot is idle or tick is up. */
  if((cur_pilot->tcontrol < SDL_GetTicks()) || (cur_pilot->task == NULL)) {
    ai_run(L, "control"); /* Run control. */
    lua_getglobal(L, "control_rate");
    cur_pilot->tcontrol = SDL_GetTicks() + 1000*(int)lua_tonumber(L, -1);
  }
  if(cur_pilot->task)
    /* Pilot has a currently running task. */
    ai_run(L, cur_pilot->task->name);

  /* Make sure pilot_acc and pilot_turn are legal moves. */
  if(pilot_acc > 1.) pilot_acc = 1.;    /* Value must be <= 1. */
  if(pilot_turn > 1.) pilot_turn = 1.;  /* Value must be between -1 and 1. */
  else if(pilot_turn < -1.) pilot_turn = -1.;

  cur_pilot->solid->dir_vel = 0.;
  if(pilot_turn) /* Set the turning velocity. */
    cur_pilot->solid->dir_vel += cur_pilot->turn * pilot_turn;
  vect_pset(&cur_pilot->solid->force, cur_pilot->thrust * pilot_acc,
            cur_pilot->solid->dir);

  /* Fire weapons if needs be. */
  if(ai_isFlag(AI_PRIMARY)) pilot_shoot(pilot, pilot_target, 0); /* Primary. */
  if(ai_isFlag(AI_SECONDARY)) pilot_shoot(pilot, pilot_target, 1); /* Secondary. */
}

/**
 * @fn void ai_attacked(Pilot* attacked, const unsigned int attacker)
 *
 * @brief Trigger the attacked() function in the Pilots AI.
 *
 *    @param attacked Pilot that is attacked.
 *    @param[in] attacker ID of the attacker.
 */
void ai_attacked(Pilot* attacked, const unsigned int attacker) {
  lua_State* L;

  cur_pilot = attacked;
  L = cur_pilot->ai->L;
  lua_getglobal(L, "attacked");
  lua_pushnumber(L, attacker);
  if(lua_pcall(L, 1, 0, 0))
    WARN("Pilot '%s' ai -> 'attacked' : %s", cur_pilot->name, lua_tostring(L, -1));
}

/**
 * @fn void ai_create(Pilot* pilot)
 *
 * @brief Run the create() function in the pilot.
 *
 * Should create all the gear and such the pilot has.
 *
 *    @param pilot Pilot to "create".
 */
void ai_create(Pilot* pilot) {
  lua_State* L;

  cur_pilot = pilot;
  L = cur_pilot->ai->L;
  ai_status = AI_STATUS_CREATE;
  ai_run(L, "create");
  ai_status = AI_STATUS_NORMAL;
}

/* ===================== */
/* INTERNAL C FUNCTIONS. */
/* ===================== */

/* Free the task. */
static void ai_freetask(Task* t) {
  if(t->next) ai_freetask(t->next); /* Woot, recursive freeing! */

  if(t->name)               free(t->name);
  if(t->dtype == TYPE_PTR)  free(t->dat.target);
  free(t);
}

/* ======================================================== */
/* C functions to call from Lua. */
/* ======================================================== */

/* Push the current stack. */
static int ai_pushtask(lua_State* L) {
  int pos;
  if(lua_isnumber(L, 1)) pos = (int) lua_tonumber(L, 1);
  else return 0; /* Invalid param. */

  Task* t = MALLOC_L(Task);
  t->name = (lua_isstring(L, 2)) ? strdup((char*) lua_tostring(L, 2)) : NULL;
  t->next = NULL;
  t->dat.target = NULL;

  if(lua_gettop(L) > 2) {
    if(lua_isnumber(L, 3)) {
      t->dtype = TYPE_INT;
      t->dat.ID = (unsigned int) lua_tonumber(L, 3);
    }
    else if(lua_islightuserdata(L, 3)) {
      /* Only pointer valid is Vec2* in Lua. */
      t->dtype = TYPE_PTR;
      t->dat.target = MALLOC_L(Vec2);
      /* No idea why vectcpy doesn't work here.. */
      ((Vec2*)t->dat.target)->x     = ((Vec2*)lua_topointer(L,3))->x;
      ((Vec2*)t->dat.target)->y     = ((Vec2*)lua_topointer(L,3))->y;
      ((Vec2*)t->dat.target)->mod   = ((Vec2*)lua_topointer(L,3))->mod;
      ((Vec2*)t->dat.target)->angle = ((Vec2*)lua_topointer(L,3))->angle;
    } else
      t->dtype = TYPE_NULL;
  }

  if(cur_pilot->task == NULL) /* No other tasks. */
    cur_pilot->task = t;
  else if(pos == 1) {
    /* Put at the end. */
    Task* pointer;
    for(pointer = cur_pilot->task; pointer->next; pointer = pointer->next);
    pointer->next = t;
  } else {
    /* Default put at the beginning. */
    t->next = cur_pilot->task;
    cur_pilot->task = t;
  }
  return 0;
}

/* Pop the current task. */
static int ai_poptask(lua_State* L) {
  (void)L; /* Just a hack to avoid -W -Wall warnings. */
  Task* t = cur_pilot->task;
  if(t != NULL) {
    cur_pilot->task = t->next;
    t->next = NULL;
  } else {
    LLUA_DEBUG("Trying to pop task when there are no tasks in stack");
    return 0;
  }
  ai_freetask(t);
  return 0;
}

/* Grab the current tasks name. */
static int ai_taskname(lua_State* L) {
  if(cur_pilot->task) lua_pushstring(L, cur_pilot->task->name);
  else lua_pushstring(L, "none");
  return 1;
}

/* Grab the target pointer. */
static int ai_gettarget(lua_State* L) {
  if(cur_pilot->task->dtype == TYPE_PTR) {
    lua_pushlightuserdata(L, cur_pilot->task->dat.target);
    return 1;
  }
  return 0;
}

/* Get the ID. */
static int ai_gettargetid(lua_State* L) {
  if(cur_pilot->task->dtype == TYPE_INT) {
    lua_pushnumber(L, cur_pilot->task->dat.ID);
    return 1;
  }
  return 0;
}

static int ai_getrndpilot(lua_State* L) {
  lua_pushnumber(L, pilot_stack[RNG(0, pilot_nstack-1)]->id);
  return 1;
}

/* Get the pilots armour. */
static int ai_armour(lua_State* L) {
  double d;

  if(lua_isnumber(L,1)) d = pilot_get((unsigned int)lua_tonumber(L,1))->armour;
  else d = cur_pilot->armour;

  lua_pushnumber(L, d);
  return 1;
}

/* Get pilots shield. */
static int ai_shield(lua_State* L) {
  double d;

  if(lua_isnumber(L,1)) d = pilot_get((unsigned int)lua_tonumber(L,1))->shield;
  else d = cur_pilot->shield;

  lua_pushnumber(L, d);
  return 1;
}

/* Get the pilot's armour in percentage. */
static int ai_parmour(lua_State* L) {
  double d;
  Pilot* p;

  if(lua_isnumber(L,1)) {
    p = pilot_get((unsigned int)lua_tonumber(L,1));
    d = p->armour / p->armour_max * 100.;
  }
  else d = cur_pilot->armour / cur_pilot->armour_max * 100.;

  lua_pushnumber(L, d);
  return 1;
}

/* Get the pilot's shield in percentage. */
static int ai_pshield(lua_State* L) {
  double d;
  Pilot* p;

  if(lua_isnumber(L,1)) {
    p = pilot_get((unsigned int)lua_tonumber(L,1));
    d = p->shield / p->shield_max * 100.;
  }
  else d = cur_pilot->shield / cur_pilot->shield_max * 100.;

  lua_pushnumber(L, d);

  return 1;
}

/* Get the distance from the pointer. */
static int ai_getdistance(lua_State* L) {
  Vec2* vect;
  Pilot* pilot;
  unsigned int n;

  LLUA_MIN_ARGS(1);

  /* Vector as a parameter. */
  if(lua_islightuserdata(L, 1))
    vect = (Vec2*)lua_topointer(L, 1);

  /* Pilot id as parameter. */
  else if(lua_isnumber(L, 1)) {
    n = (unsigned int)lua_tonumber(L,1);
    pilot = pilot_get((unsigned int) lua_tonumber(L, 1));
    if(pilot == NULL) {
      LLUA_DEBUG("Pilot '%d' not found in stack", n);
      return 0;
    }
    vect = &pilot->solid->pos;
  } else
    /* Wrong parameter. */
    LLUA_INVALID_PARAMETER();

  lua_pushnumber(L, vect_dist(vect, &cur_pilot->solid->pos));
  return 1;
}

/* Get the pilots position. */
static int ai_getpos(lua_State* L) {
  Pilot* p;
  if(lua_isnumber(L, 1)) {
    p = pilot_get((int)lua_tonumber(L,1)); /* Pilot ID. */
    if(p == NULL) return 0;
  }
  else p = cur_pilot; /* Default to ones self. */

  lua_pushlightuserdata(L, &p->solid->pos);

  return 1;
}

/*
 * Get the minimum braking distance.
 *
 * Braking vel ==> 0 = v - a*dt
 * Add turn around time (to initial velocity) :
 * ==> 180.*360./cur_pilot->turn
 * Add it to general euler equation x = v*t + 0.5 * a * t^2
 * Have fun.
 *
 * I really hate this function. Why isn't it depricated yet?
 */
static int ai_minbrakedist(lua_State* L) {
  double time, dist, vel;
  time = VMOD(cur_pilot->solid->vel) /
      (cur_pilot->thrust / cur_pilot->solid->mass);

  vel = MIN(cur_pilot->speed, VMOD(cur_pilot->solid->vel));
  dist = vel*(time+1.1*180./cur_pilot->turn) -
      0.5 * (cur_pilot->thrust / cur_pilot->solid->mass)*time*time;

  lua_pushnumber(L, dist); /* return */
  return 1;
}

static int ai_cargofree(lua_State* L) {
  lua_pushnumber(L, pilot_cargoFree(cur_pilot));
  return 1;
}

static int ai_exists(lua_State* L) {
  Pilot* p;
  int i;

  if(lua_isnumber(L,1)) {
    i = 1;
    p = pilot_get((unsigned int)lua_tonumber(L,1));
    if(p == NULL) i = 0;
    else if(pilot_isFlag(p, PILOT_DEAD)) i = 0;
    lua_pushboolean(L, i);
    return 1;
  }

  /* Default to false for everything that isn't a pilot. */
  lua_pushboolean(L, 0);
  return 0;
}

/* Are we at max velocity? */
static int ai_ismaxvel(lua_State* L) {
  lua_pushboolean(L,(VMOD(cur_pilot->solid->vel)>cur_pilot->speed-MIN_VEL_ERR));
  return 1;
}

/* Have we stopped? */
static int ai_isstopped(lua_State* L) {
  lua_pushboolean(L, VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR);
  return 1;
}

/* Check if the pilot is an enemy. */
static int ai_isenemy(lua_State* L) {
  if(lua_isnumber(L,1))
    lua_pushboolean(L, areEnemies(cur_pilot->faction,
                                  pilot_get(lua_tonumber(L,1))->faction));
  return 1;
}

/* Check if the pilot is an ally. */
static int ai_isally(lua_State* L) {
  lua_pushboolean(L, areAllies(cur_pilot->faction,
                               pilot_get(lua_tonumber(L,1))->faction));
  return 1;
}

/* Check to see if the pilot is in combat. Defaults to self. */
static int ai_incombat(lua_State* L) {
  Pilot* p;

  if(lua_isnumber(L, 1)) p = pilot_get((unsigned int)lua_tonumber(L,1));
  else p = cur_pilot;

  lua_pushboolean(L,pilot_isFlag(p, PILOT_COMBAT));
  return 1;
}

static int ai_haslockon(lua_State* L) {
  lua_pushboolean(L, cur_pilot->lockons > 0);
  return 1;
}

/* Accelerate the pilot based on a param. */
static int ai_accel(lua_State* L) {
  double n;

  if(lua_gettop(L) > 1 && lua_isnumber(L, 1)) {
    n = (double)lua_tonumber(L,1);

    if(n > 1.) n = 1.;
    else if(n < 0.) n = 0.;

    if(VMOD(cur_pilot->solid->vel) > (n * cur_pilot->speed))
      pilot_acc = 0.;
  } else
    pilot_acc = 1.;

  return 0;
}

/* Turn the pilot based on a param. */
static int ai_turn(lua_State* L) {
  LLUA_MIN_ARGS(1);
  pilot_turn = (lua_isnumber(L, 1)) ? (double)lua_tonumber(L, 1) : 0.;
  return 0;
}

/* Face the target. */
static int ai_face(lua_State* L) {
  LLUA_MIN_ARGS(1);
  Vec2* v, sv, tv; /* Grab the position to face. */
  Pilot* p;
  double mod, diff;
  int invert = 0;
  int n = -2;

  if(lua_isnumber(L, 1))
    n = (int)lua_tonumber(L, 1);

  if(n >= 0) {
    p = pilot_get(n);
    if(p == NULL) return 0; /* Make sure pilot is valid. */
    vect_cset(&tv, VX(p->solid->pos), VY(p->solid->pos));
    v = NULL;
  }
  else if(lua_islightuserdata(L,1)) v = (Vec2*)lua_topointer(L,1);

  mod = 10;
  if(lua_gettop(L) > 1 && lua_isnumber(L,2)) invert = (int)lua_tonumber(L,2);
  if(invert) mod *= -1;

  vect_cset(&sv, VX(cur_pilot->solid->pos), VY(cur_pilot->solid->pos));

  if(v == NULL)
    /* Target is dynamic. */
    diff = angle_diff(cur_pilot->solid->dir,
                      (n==-1) ? VANGLE(sv) :
                                vect_angle(&sv, &tv));
  else
    /* Target is static. */
    diff = angle_diff(cur_pilot->solid->dir,
                      (n==-1) ? VANGLE(cur_pilot->solid->pos) :
                                vect_angle(&cur_pilot->solid->pos, v));

  pilot_turn = mod*diff;

  lua_pushnumber(L, ABS(diff*180./M_PI));

  return 1;
}

/* This is generally good for coming to a halt. */
static int ai_brake(lua_State* L) {
  (void)L; /* Just a hack to avoid -W -Wall warnings. */
  double diff, d;

  d = cur_pilot->solid->dir+M_PI;
  if(d >= 2*M_PI) d = fmodf(d, 2*M_PI);

  diff = angle_diff(d, VANGLE(cur_pilot->solid->vel));
  pilot_turn = 10*diff;

  if(ABS(diff) < MAX_DIR_ERR && VMOD(cur_pilot->solid->vel) > MIN_VEL_ERR)
    pilot_acc = 1.;
  return 0;
}

/* Return the nearest friendly planet's position to the pilot. */
static int ai_getnearestplanet(lua_State* L) {
  if(cur_system->nplanets == 0) return 0; /* No planets. */

  double dist, d;
  int i, j;

  /* Cycle through planets. */
  for(dist = 0., j = -1, i = 0; i < cur_system->nplanets; i++) {
    d = vect_dist(&cur_system->planets[i].pos, &cur_pilot->solid->pos);
    if((!areEnemies(cur_pilot->faction, cur_system->planets[i].faction)) &&
       (d < dist)) {
      /* Closer friendly planet. */
      j = i;
      dist = d;
    }
  }

  /* No friendly planet found. */
  if(j == -1) return 0;

  lua_pushlightuserdata(L, &cur_system->planets[j].pos);
  return 1;
}

/* Return a random planet's position to the pilot. */
static int ai_getrndplanet(lua_State* L) {
  Vec2 v;
  int p;

  if(cur_system->nplanets == 0) return 0; /* No planets. */

  /* Get a random planet. */
  p = RNG(0, cur_system->nplanets-1);

  /* Copy the data into a vector. */
  vectcpy(&v, &cur_system->planets[p].pos);
  lua_pushlightuserdata(L, &v);

  return 1;
}

/* Return a random friendly planet's position to the pilot. */
static int ai_getlandplanet(lua_State* L) {
  Planet** planets;
  int nplanets, i;
  Vec2 v;
  planets = malloc(sizeof(Planet*) * cur_system->nplanets);

  if(cur_system->nplanets == 0) return 0; /* No planets. */

  for(nplanets = 0, i = 0; i < cur_system->nplanets; i++)
    if(planet_hasService(&cur_system->planets[i], PLANET_SERVICE_BASIC) &&
       !areEnemies(cur_pilot->faction, cur_system->planets[i].faction))
      planets[nplanets++] = &cur_system->planets[i];

  /* No planet to land on found. */
  if(nplanets == 0) {
    free(planets);
    return 0;
  }

  /* We can actually get a random planet now. */
  i = RNG(0,nplanets-1);
  vectcpy(&v, &planets[i]->pos);
  vect_cadd(&v, RNG(0, planets[i]->gfx_space->sw)-planets[i]->gfx_space->sw/2.,
            RNG(0, planets[i]->gfx_space->sh)-planets[i]->gfx_space->sh/2.);
  lua_pushlightuserdata(L, &v);
  free(planets);
  return 1;
}

/* Attempt to enter the pilot hyperspace. Return the distance if too far away. */
static int ai_hyperspace(lua_State* L) {
  int dist;

  dist = space_hyperspace(cur_pilot);
  if(dist == 0.) return 0;

  lua_pushnumber(L,dist);
  return 1;
}

/* Completely stop the pilot if it is below minimum vel error. (No instant stops.) */
static int ai_stop(lua_State* L) {
  (void)L; /* Just avoid a gcc warning. */

  if(VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR)
    vect_pset(&cur_pilot->solid->vel, 0., 0.);

  return 0;
}

/* Aim at the pilot, trying to hit it. */
static int ai_aim(lua_State* L) {
  int id;
  double x, y;
  double t;
  Pilot* p;
  Vec2 tv;
  double dist, diff;
  double mod;
  LLUA_MIN_ARGS(1);

  /* Only acceptable parameter is pilot id. */
  if(lua_isnumber(L,1))
    id = lua_tonumber(L,1);
  else
    LLUA_INVALID_PARAMETER();

  /* Get the pilot. */
  p = pilot_get(id);
  if(p == NULL) {
    WARN("Pilot is invalid");
    return 0;
  }

  /* Get the  distance. */
  dist = vect_dist(&cur_pilot->solid->pos, &p->solid->pos);

  /* Time for shots to reach distance. */
  t = dist / cur_pilot->weap_speed;

  /* Position is calculated on where it should be. */
  x = p->solid->pos.x + p->solid->vel.x*t
    - (cur_pilot->solid->pos.x + cur_pilot->solid->vel.x*t);

  y = p->solid->pos.y + p->solid->vel.y*t
    - (cur_pilot->solid->pos.y + cur_pilot->solid->vel.y*t);
  vect_cset(&tv, x, y);

  /* Calculate what we need to turn. */
  mod = 10.;
  diff = angle_diff(cur_pilot->solid->dir, VANGLE(tv));
  pilot_turn = mod * diff;

  /* Return distance to target (in grad). */
  lua_pushnumber(L, ABS(diff*180./M_PI));
  return 1;
}

/* Toggle combat flag. Default is on. */
static int ai_combat(lua_State* L) {
  int i;

  if(lua_isnumber(L, 1)) {
    i = (int)lua_tonumber(L,1);
    if(i == 1) pilot_setFlag(cur_pilot, PILOT_COMBAT);
    else if(i == 0) pilot_rmFlag(cur_pilot, PILOT_COMBAT);
  } else pilot_setFlag(cur_pilot, PILOT_COMBAT);

  return 0;
}

/* Set the pilots target. */
static int ai_settarget(lua_State* L) {
  LLUA_MIN_ARGS(1);

  if(lua_isnumber(L,1)) {
    pilot_target = (int)lua_tonumber(L,1);
    return 1;
  }

  LLUA_INVALID_PARAMETER();
}

/* Set the secondary weapon. Biassed towards launchers.. */
static int ai_secondary(lua_State* L) {
  PilotOutfit* co, *po;
  int i;
  char* type;
  const char* otype;

  po = NULL;

  /* Search for a type. */
  type = NULL;
  if(lua_isstring(L, 1))
    type = (char*) lua_tostring(L, 1);

  /* Pilot has secondary selected - use that. */
  if(cur_pilot->secondary != NULL) {
    co = cur_pilot->secondary;
    otype = outfit_getTypeBroad(co->outfit);

    /* If we aren't looking for a type or if it matches what we want. */
    if((type == NULL) || (strcmp(otype, type) == 0))
      po = co;
  }

  /* Need to get new secondary. */
  if(po == NULL) {
    /* Iterate over the list. */
    po = NULL;
    for(i = 0; i < cur_pilot->noutfits; i++) {
      co = &cur_pilot->outfits[i];

      /* Not a secondary weapon. */
      if(!outfit_isProp(co->outfit, OUTFIT_PROP_WEAP_SECONDARY) ||
          outfit_isAmmo(co->outfit))
        continue;

      /* Searching for type. */
      if(type != NULL) {
        otype = outfit_getTypeBroad(co->outfit);
        if(strcmp(otype, type)==0) {
          po = co;
          break;
        }

        /* We'll grab the first weapon in case we don't find what we want. */
        if((po == NULL) && (outfit_isWeapon(co->outfit) ||
              outfit_isLauncher(co->outfit)))
          po = co;
      }

      /* Just grabbing best weapon. */
      else {
        /* Grab first weapon or launcher it finds. */
        if((po == NULL) && (outfit_isWeapon(co->outfit) ||
              outfit_isLauncher(co->outfit)))
          po = co;

        /* Grab launcher over weapon by default. */
        else if((po != NULL) && outfit_isWeapon(po->outfit) &&
            outfit_isLauncher(co->outfit))
          po = co;
      }
    }
  }

  if(po != NULL) {
    cur_pilot->secondary = po;
    pilot_setAmmo(cur_pilot);
    otype = outfit_getTypeBroad(po->outfit);
    lua_pushstring(L, otype);

    /* Set special flags. */
    if((strcmp(otype, "Launcher")==0) &&
        (po->outfit->type != OUTFIT_TYPE_MISSILE_DUMB)) {
      lua_pushstring(L, "Smart");
      return 2;
    }
    return 1;
  }
  /* Nothing found. */
  return 0;
}

static int ai_hasturrets(lua_State* L) {
  lua_pushboolean(L, pilot_isFlag(cur_pilot, PILOT_HASTURRET));
  return 1;
}

/* Pew pew.. Says the pilot. */
static int ai_shoot(lua_State* L) {
  int n = 1;
  if(lua_isnumber(L, 1)) n = (int)lua_tonumber(L,1);

  if(n == 1) ai_setFlag(AI_PRIMARY);
  else if(n == 2) ai_setFlag(AI_SECONDARY);
  else if(n == 3) ai_setFlag(AI_PRIMARY | AI_SECONDARY);

  return 0;
}

/* Get the nearest enemy. */
static int ai_getenemy(lua_State* L) {
  unsigned int p;

  p = pilot_getNearestEnemy(cur_pilot);

  if(p == 0) /* No enemy found. */
    return 0;

  lua_pushnumber(L,p);
  return 1;
}

/* Set the enemy hostile. (Simply notifies of an impending attack). */
static int ai_hostile(lua_State* L) {
  LLUA_MIN_ARGS(1);

  if(lua_isnumber(L,1) && ((unsigned int)lua_tonumber(L,1) == PLAYER_ID))
    pilot_setFlag(cur_pilot, PILOT_HOSTILE);

  return 0;
}

/* Return the maximum range of weapons if parameter is 1, then do secondary. */
static int ai_getweaprange(lua_State* L) {
  double range;

  /* If 1 is passed as parameter, secondary weapon is checked. */
  if(lua_isnumber(L, 1) && ((int)lua_tonumber(L, 1) == 1))
    if(cur_pilot->secondary != NULL) {
      /* Get range, launchers use ammo's range. */
      if(outfit_isLauncher(cur_pilot->secondary->outfit) && (cur_pilot->ammo != NULL))
        range = outfit_range(cur_pilot->ammo->outfit);
      else
        range = outfit_range(cur_pilot->secondary->outfit);

      if(range < 0.) return 0; /* Secondary doesn't have range. */

      /* Secondary does have range. */
      lua_pushnumber(L, range);
      return 1;
    }

  lua_pushnumber(L, cur_pilot->weap_range);
  return 1;
}

/* Set the timer. */
static int ai_settimer(lua_State* L) {
  LLUA_MIN_ARGS(2);

  int n; /* Get the timer. */
  if(lua_isnumber(L, 1)) n = lua_tonumber(L,1);

  cur_pilot->timer[n] = (lua_isnumber(L,2)) ?
        lua_tonumber(L,2) + SDL_GetTicks() : 0;
  return 0;
}

/* Check the timer. */
static int ai_timeup(lua_State* L) {
  LLUA_MIN_ARGS(1);

  int n; /* Get the timer. */
  if(lua_isnumber(L,1)) n = lua_tonumber(L,1);

  lua_pushboolean(L, cur_pilot->timer[n] < SDL_GetTicks());
  return 1;
}

/* Have the pilot say something to player. */
static int ai_comm(lua_State* L) {
  LLUA_MIN_ARGS(2);

  if(lua_isnumber(L,1) && (lua_tonumber(L,1)==PLAYER_ID) && lua_isstring(L,2))
    player_message("Comm: %s> \"%s\"", cur_pilot->name, lua_tostring(L,2));

  return 0;
}

/* Broadcasts to the entire area. */
static int ai_broadcast(lua_State* L) {
  LLUA_MIN_ARGS(1);

  if(lua_isstring(L, 1))
    player_message("Broadcast: %s> \"%s\"", cur_pilot->name, lua_tostring(L,1));

  return 0;
}

/* Set the pilots credits. */
static int ai_credits(lua_State* L) {
  LLUA_MIN_ARGS(1);
  if(ai_status != AI_STATUS_CREATE) return 0;

  if(lua_isnumber(L,1))
    cur_pilot->credits = (int)lua_tonumber(L,1);

  return 0;
}

/* Set the pilots cargo. */
static int ai_cargo(lua_State* L) {
  LLUA_MIN_ARGS(2);
  if(ai_status != AI_STATUS_CREATE) return 0;

  if(lua_isstring(L,1) && lua_isnumber(L,2))
    pilot_addCargo(cur_pilot, commodity_get(lua_tostring(L,1)),
                   (int)lua_tonumber(L,2));

  return 0;
}

/* Get the pilot's ship value. */
static int ai_shipprice(lua_State* L) {
  lua_pushnumber(L, cur_pilot->ship->price);
  return 1;
}

