// Woot, LUA!!!!!!
#include "lauxlib.h"
#include "lualib.h"

#include <math.h>

#include "main.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 "ai.h"

// == 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).
// ============================================================

// Call the AI function with name f.
#define AI_LCALL(f)           (lua_getglobal(L, f), lua_pcall(L, 0, 0, 0))
// 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))
// Registers a C function.
#define lua_regfunc(l,s,f)    (lua_pushcfunction(l,f), lua_setglobal(L,s))
// L state, void* buf, int n size, char* s identifier.
#define luaL_dobuffer(L,b,n,s) (luaL_loadbuffer(L,b,n,s) || lua_pcall(L, 0, LUA_MULTRET, 0))

// Don't run the function if (n) params aren't passed.
#define MIN_ARGS(n) if(lua_gettop(L) < n) return 0

#define MIN_DIR_ERR 1.0*M_PI/180.
#define MAX_DIR_ERR 0.1*M_PI/180.
#define MIN_VEL_ERR 0.5

// Ai flags.
#define ai_setFlag(f) (pilot_flags |= f)
#define ai_isFlag(f)  (pilot_flags  & 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/"
#define AI_SUFFIX   ".lua"

// AI profiles.
static AI_Profile* profiles = NULL;
static int nprofiles = 0;
// Current AI Lua interpreter.
static lua_State* L = NULL;

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

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

// Internal C routines.
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
// 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_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()
// 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])
// 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_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_hyperspace(lua_State* L);         // [number] hyperspace()
// 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_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).
// Timers.
static int ai_settimer(lua_State* L);           // settimer(number, number)
static int ai_timeup(lua_State* L);             // boolean timeup(number)
// Misc.
static int ai_createvect(lua_State* L);         // createvect(number, number)
static int ai_comm(lua_State* L);               // comm(string)
static int ai_broadcast(lua_State* L);          // broadcast(string)
static int ai_rng(lua_State* L);                // rng(number, number)

// 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;

// Destroy the AI part of the pilot.
void ai_destroy(Pilot* p) {
  if(p->task)
    ai_freetask(p->task);
}

// Init the AI stuff. Which is basically Lua.
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 &&
          strncmp(files[i] + strlen(files[i]) - strlen(AI_SUFFIX),
          AI_SUFFIX, strlen(AI_SUFFIX))==0))
      if(ai_loadProfile(files[i]))
        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;
}

// Init an IA_Profile and add it to the stack.
static int ai_loadProfile(char* filename) {
  char* buf = NULL;
  uint32_t bufsize = 0;

  profiles = realloc(profiles, sizeof(AI_Profile)*(++nprofiles));
  profiles[nprofiles-1].name = strndup(filename+strlen(AI_PREFIX),
        strlen(filename)-strlen(AI_PREFIX)-strlen(AI_SUFFIX));

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

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

  L = profiles[nprofiles-1].L;

  // Open the standard Lua libraries.
  //luaL_openlibs(L);

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

  // Register C funstions in Lua.
  // Tasks.
  lua_regfunc(L, "pushtask",           ai_pushtask);
  lua_regfunc(L, "poptask",            ai_poptask);
  lua_regfunc(L, "taskname",           ai_taskname);
  // Consult.
  lua_regfunc(L, "gettarget",          ai_gettarget);
  lua_regfunc(L, "gettargetid",        ai_gettargetid);
  lua_regfunc(L, "armour",             ai_armour);
  lua_regfunc(L, "shield",             ai_shield);
  lua_regfunc(L, "parmour",            ai_parmour);
  lua_regfunc(L, "pshield",            ai_pshield);
  lua_regfunc(L, "getdist",            ai_getdistance);
  lua_regfunc(L, "getpos",             ai_getpos);
  lua_regfunc(L, "minbrakedist",       ai_minbrakedist);
  // Boolean.
  lua_regfunc(L, "exists",             ai_exists);
  lua_regfunc(L, "ismaxvel",           ai_ismaxvel);
  lua_regfunc(L, "isstopped",          ai_isstopped);
  lua_regfunc(L, "isenemy",            ai_isenemy);
  lua_regfunc(L, "isally",             ai_isally);
  lua_regfunc(L, "incombat",           ai_incombat);
  // Movement.
  lua_regfunc(L, "accel",              ai_accel);
  lua_regfunc(L, "turn",               ai_turn);
  lua_regfunc(L, "face",               ai_face);
  lua_regfunc(L, "brake",              ai_brake);
  lua_regfunc(L, "getnearestplanet",   ai_getnearestplanet);
  lua_regfunc(L, "getrndplanet",       ai_getrndplanet);
  lua_regfunc(L, "hyperspace",         ai_hyperspace);
  // Combat.
  lua_regfunc(L, "combat",             ai_combat);
  lua_regfunc(L, "settarget",          ai_settarget);
  lua_regfunc(L, "secondary",          ai_secondary);
  lua_regfunc(L, "shoot",              ai_shoot);
  lua_regfunc(L, "getenemy",           ai_getenemy);
  lua_regfunc(L, "hostile",            ai_hostile);
  // Timers.
  lua_regfunc(L, "settimer",           ai_settimer);
  lua_regfunc(L, "timeup",             ai_timeup);
  // Misc.
  lua_regfunc(L, "createvect",         ai_createvect);
  lua_regfunc(L, "comm",               ai_comm);
  lua_regfunc(L, "broadcast",          ai_broadcast);
  lua_regfunc(L, "rng",                ai_rng);


  // 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("loading AI file: %s", filename);
    ERR("%s", lua_tostring(L, -1));
    WARN("Most likely Lua file has improper syntax, please check it.");
    return -1;
  }

  free(buf);

  return 0;
}

// Get the AI_Profile with name.
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;
}

// Clean up global AI
void ai_exit(void) {
  int i;
  for(i = 0; i < nprofiles; i++) {
    free(profiles[i].name);
    lua_close(profiles[i].L);
  }
  free(profiles);
}

// Heart of hearts of the ai!! Brains of the pilot.
void ai_think(Pilot* pilot) {
  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 = 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_LCALL("control"); // Run control.
    lua_getglobal(L, "control_rate");
    cur_pilot->tcontrol = SDL_GetTicks() + 1000*(int)lua_tonumber(L, -1);
  }
  if(cur_pilot->task != NULL)
    // Pilot has a currently running task.
    AI_LCALL(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->ship->turn * pilot_turn;
  vect_pset(&cur_pilot->solid->force, cur_pilot->ship->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.
}

// Pilot is attacked.
void ai_attacked(Pilot* attacked, const unsigned int attacker) {
  cur_pilot = attacked;
  L = cur_pilot->ai->L;
  lua_getglobal(L, "attacked");
  lua_pushnumber(L, attacker);
  lua_pcall(L, 1, 0, 0);
}

// =====================
// 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->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->target = NULL;

  if(lua_gettop(L) > 2) {
    if(lua_isnumber(L, 3)) {
      t->dtype = TYPE_INT;
      t->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->target = MALLOC_L(Vec2);
      vectcpy(t->target, (Vec2*)lua_topointer(L,3));
    } 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;
  cur_pilot->task = t->next;
  t->next = NULL;
  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->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->ID);
    return 1;
  }
  return 0;
}

// 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) {
  MIN_ARGS(1);
  Vec2* vect = (Vec2*)lua_topointer(L,1);
  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->ship->turn
// Add it to general euler equation x = v*t + 0.5 * a * t^2
// Have fun.
// ========================================================
static int ai_minbrakedist(lua_State* L) {
  double time = VMOD(cur_pilot->solid->vel) /
        (cur_pilot->ship->thrust / cur_pilot->solid->mass);

  double dist = VMOD(cur_pilot->solid->vel) * (time + 180./cur_pilot->ship->turn) -
        0.5 * (cur_pilot->ship->thrust / cur_pilot->solid->mass)*time*time;

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

static int ai_exists(lua_State* L) {
  MIN_ARGS(1);
  
  if(lua_isnumber(L,1)) {
    lua_pushboolean(L, (pilot_get((unsigned int)lua_tonumber(L,1)) != NULL)?1:0);
    return 1;
  }
  return 0;
}

// Are we at max velocity?
static int ai_ismaxvel(lua_State* L) {
  lua_pushboolean(L, VMOD(cur_pilot->solid->vel) == cur_pilot->ship->speed);
  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;
}

// Accelerate the pilot based on a param.
static int ai_accel(lua_State* L) {
  pilot_acc = (lua_gettop(L) > 1 && lua_isnumber(L, 1)) ? ABS((double)lua_tonumber(L, 1)) : 1.;
  return 0;
}

// Turn the pilot based on a param.
static int ai_turn(lua_State* L) {
  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) {
  MIN_ARGS(1);
  Vec2* v; // Grab the position to face.
  Pilot* p;
  
  if(lua_isnumber(L,1)) {
   p = pilot_get((unsigned int)lua_tonumber(L,1));
   if(p == NULL) return 0; // Make sure pilot is valid.
   v = &p->solid->pos;
  }
  else if(lua_islightuserdata(L,1)) v = (Vec2*)lua_topointer(L,1);

  double mod = -10;
  if(lua_gettop(L) > 1 && lua_isnumber(L,2))
    switch((int)lua_tonumber(L,2)) {
      case 0: break;
      case 1: mod *= -1; break;
      case 2: break;
    }
  double diff = angle_diff(cur_pilot->solid->dir, 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 friendly planet's position to the pilot.
static int ai_getrndplanet(lua_State* L) {
  if(cur_system->nplanets == 0) return 0; // No planets.

  Planet** planets;
  int nplanets, i;
  planets = malloc(sizeof(Planet*) * cur_system->nplanets);

  for(nplanets = 0, i = 0; i < cur_system->nplanets; i++)
    if(!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);
  lua_pushlightuserdata(L, &planets[i]->pos);
  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;
}

// 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) {
  MIN_ARGS(1);

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

// Set the secondary weapon. Biassed towards launchers..
static int ai_secondary(lua_State* L) {
  if(cur_pilot->secondary) {
    lua_pushstring(L, outfit_getTypeBroad(cur_pilot->secondary->outfit));
    return 1;
  }

  PilotOutfit* po = NULL;
  int i;
  for(i = 0; i < cur_pilot->noutfits; i++) {
    if((po == NULL) && (outfit_isWeapon(cur_pilot->outfits[i].outfit) ||
          outfit_isLauncher(cur_pilot->outfits[i].outfit)))
      po = &cur_pilot->outfits[i];
    else if((po != NULL) && outfit_isWeapon(po->outfit) && 
          outfit_isLauncher(cur_pilot->outfits[i].outfit))
      po = &cur_pilot->outfits[i];
  }
  if(po) {
    cur_pilot->secondary = po;
    pilot_setAmmo(cur_pilot);
    lua_pushstring(L, outfit_getTypeBroad(po->outfit));
    return 1;
  }
  lua_pushstring(L, "None");
  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) {
  lua_pushnumber(L,pilot_getNearest(cur_pilot));
  return 1;
}

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

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

  return 0;
}

// Set the timer.
static int ai_settimer(lua_State* L) {
  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) {
  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;
}

// Create a vector.
static int ai_createvect(lua_State* L) {
  MIN_ARGS(2);
  Vec2* v = MALLOC_L(Vec2);
  double x = (lua_isnumber(L, 1)) ? (double)lua_tonumber(L,1) : 0.;
  double y = (lua_isnumber(L, 2)) ? (double)lua_tonumber(L,2) : 0.;

  vect_cset(v, x, y);

  lua_pushlightuserdata(L, v);
  return 1;
}

// Have the pilot say something to player.
static int ai_comm(lua_State* L) {
  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) {
  MIN_ARGS(1);

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

  return 0;
}

// Return a number between low and high.
static int ai_rng(lua_State* L) {
  MIN_ARGS(2);

  int l, h;

  if(lua_isnumber(L,1)) l = (int)lua_tonumber(L, 1);
  if(lua_isnumber(L,1)) h = (int)lua_tonumber(L, 2);

  lua_pushnumber(L, RNG(l,h));
  return 1;
}