/**
 * @file escorts.c
 *
 * @brief Handles the players escorts.
 */

#include "lephisto.h"
#include "log.h"
#include "player.h"
#include "llua.h"
#include "lluadef.h"
#include "llua_space.h"
#include "escort.h"

#define ESCORT_PREALLOC   8 /**< Number of escorts to automatically allocate first. */

#define ESCORT_ATTACK 1 /**< Attack order. */
#define ESCORT_HOLD   2 /**< Hold order. */
#define ESCORT_RETURN 3 /**< Return to ship order. */
#define ESCORT_CLEAR  4 /**< Clear orders. */

/* Static. */
static int escort_command(Pilot* parent, int cmd, int param);
/* Extern. */
extern void ai_setPilot(Pilot* p);

/**
 * @fn int escort_create(unsigned int parent, char* ship,
 *         Vec2* pos, Vec2* vel, int carried)
 *
 * @brief Create an escort.
 *    @param parent Parent of the escort (who she's guarding).
 *    @param ship Name of the ship escort should have.
 *    @param pos Position to create escort at.
 *    @param vel Velocity to create escort with.
 *    @param carried Does escort come out of the parent?
 */
int escort_create(unsigned int parent, char* ship,
    Vec2* pos, Vec2* vel, int carried) {

  Ship* s;
  Pilot* p, *pe;
  char buf[16];
  unsigned int e, f;
  double dir;

  /* Get important stuff. */
  p = pilot_get(parent);
  s = ship_get(ship);
  snprintf(buf, 16, "tpl/escort*%u", parent);

  /* Set flags. */
  f = PILOT_ESCORT;
  if(carried) f |= PILOT_CARRIED;

  /* Get the direction. */
  if(carried) dir = p->solid->dir;
  else dir = 0.;

  /* Create the pilot. */
  e = pilot_create(s, NULL, p->faction, buf, dir, pos, vel, f);
  pe = pilot_get(e);
  pe->parent = parent;

  /* Add to escort list. */
  p->nescorts++;
  if(p->nescorts == 1)
    p->escorts = malloc(sizeof(unsigned int) * ESCORT_PREALLOC);
  else if(p->nescorts > ESCORT_PREALLOC)
    p->escorts = realloc(p->escorts, sizeof(unsigned int) * p->nescorts);
  p->escorts[p->nescorts-1] = e;

  return 0;
}

/**
 * @fn static int escort_command(Pilot* parent, int cmd, int param)
 *
 * @brief Run an escort command on all of a pilots escorts.
 *    @param parent Pilot who is giving orders.
 *    @param cmd Order to give.
 *    @param param Parameter for order.
 *    @return 0 on success, 1 if no orders given.
 */
static int escort_command(Pilot* parent, int cmd, int param) {
  int i, n;
  lua_State* L;
  Pilot* e;
  char* buf;

  if(parent->nescorts == 0)
    return 1;

  n = 0;
  for(i = 0; i < parent->nescorts; i++) {
    e = pilot_get(parent->escorts[i]);
    if(e == NULL) /* Most likely died. */
      continue;

    /* Check if command makes sense. */
    if((cmd == ESCORT_RETURN) && !pilot_isFlag(e, PILOT_CARRIED))
      continue;
    
    n++; /* Amount of escorts left. */

    /* Prepare ai. */
    ai_setPilot(e);

    /* Set up stack. */
    L = e->ai->L;
    switch(cmd) {
      case ESCORT_ATTACK:
        buf = "e_attack";
        break;
      case ESCORT_HOLD:
        buf = "e_hold";
        break;
      case ESCORT_RETURN:
        buf = "e_return";
        break;
      case ESCORT_CLEAR:
        buf = "e_clear";
        break;
    }
    lua_getglobal(L, buf);
    if(param >= 0)
      lua_pushnumber(L, param);

    /* Run command. */
    if(lua_pcall(L, (param >= 0) ? 1 : 0, 0, 0))
      WARN("Pilot '%s' ai -> '%s' : %s", e->name,
          buf, lua_tostring(L, -1));
  }
  return !n;
}

/**
 * @fn int escorts_attack(Pilot* parent)
 *
 * @brief HAve a pilot order it's escorts to attack its target.
 *    @param parent Pilot giving the order.
 */
int escorts_attack(Pilot* parent) {
  int ret;
  Pilot* t;

  ret = 1;
  if(parent->target != parent->id)
    ret = escort_command(parent, ESCORT_ATTACK, parent->target);
  if((ret == 0) && (parent == player)) {
    t = pilot_get(parent->target);
    if(t != NULL)
      player_message("Escorts: Attacking %s.", t->name);
  }
  return ret;
}

/**
 * @fn int escorts_hold(Pilot* parent)
 *
 * @brief Have a pilot order its escorts to hold position.
 *    @param parent Pilot giving the order.
 */
int escorts_hold(Pilot* parent) {
  int ret;
  ret = escort_command(parent, ESCORT_HOLD, -1);
  if((ret == 0) && (parent == player))
    player_message("Escorts: Holding position.");
  return ret;
}

/**
 * @fn int escorts_return(Pilot* parent)
 *
 * @brief Have a pilot order its escorts to dock.
 *    @param parent Pilot giving the order.
 */
int escorts_return(Pilot* parent) {
  int ret;
  ret = escort_command(parent, ESCORT_RETURN, -1);
  if((ret == 0) && (parent == player))
    player_message("Escorts: Returning to ship.");
  return ret;
}

/**
 * @fn int escorts_clear(Pilot* parent)
 * 
 * @brief Have a pilot order its escorts to clear orders.
 *    @param parent Pilot giving the order.
 */
int escorts_clear(Pilot* parent) {
  int ret;
  ret = escort_command(parent, ESCORT_CLEAR, -1);
  if((ret == 0) && (parent == player))
    player_message("Escorts: Clearing orders.");
  return ret;
}