#include "lephisto.h"
#include "log.h"
#include "toolkit.h"
#include "player.h"
#include "rng.h"
#include "music.h"
#include "economy.h"
#include "land.h"

// Global/main window.
#define LAND_WIDTH    700
#define LAND_HEIGHT   600
#define BUTTON_WIDTH  200
#define BUTTON_HEIGHT 40

// Commodity window.
#define COMMODITY_WIDTH   400
#define COMMODITY_HEIGHT  400

// Outfits.
#define OUTFITS_WIDTH   700
#define OUTFITS_HEIGHT  600

// Shipyard.
#define SHIPYARD_WIDTH  700
#define SHIPYARD_HEIGHT  600

// News window.
#define NEWS_WIDTH    400
#define NEWS_HEIGHT   500

// Bar window.
#define BAR_WIDTH     460
#define BAR_HEIGHT    300

#define MUSIC_TAKEOFF  "liftoff"
#define MUSIC_LAND   "agriculture"

int landed = 0;

static int land_wid = 0; // Primary land window.
// For the second opened land window
static int secondary_wid = 0;
static int terciary_wid = 0; // For fancy things like news, your ship etc..
Planet* land_planet = NULL;

// Extern.
extern char** player_ships(int* nships);
extern Pilot* player_getShip(char* shipname);
// Static.
// Commodity excahnge.
static void commodity_exchange(void);
static void commodity_exchange_close(char* str);
static void commodity_update(char* str);
static void commodity_buy(char* str);
static void commodity_sell(char* str);
// Outfits.
static void outfits(void);
static void outfits_close(char* str);
static void outfits_update(char* str);
static void outfits_buy(char* str);
static void outfits_sell(char* str);
static int  outfits_getMod(void);
static void outfits_renderMod(double bx, double by, double w, double h);
// Shipyard.
static void shipyard(void);
static void shipyard_close(char* str);
static void shipyard_update(char* str);
static void shipyard_info(char* str);
static void shipyard_buy(char* str);
// Your ship.
static void shipyard_yours(char* str);
static void shipyard_yoursClose(char* str);
// Spaceport bar.
static void spaceport_bar(void);
static void spaceport_bar_close(char* str);
// News.
static void news(void);
static void news_close(char* str);

// The local market.
static void commodity_exchange(void) {
  int i;
  char** goods;
  secondary_wid = window_create("Commodity Exchange", -1, -1,
                                COMMODITY_WIDTH, COMMODITY_HEIGHT);

  window_addButton(secondary_wid, -20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnCommodityClose",
                   "Close", commodity_exchange_close);

  window_addButton(secondary_wid, -40-((BUTTON_WIDTH-20)/2), 20*2+BUTTON_HEIGHT,
                   (BUTTON_WIDTH-20)/2, BUTTON_HEIGHT, "btnCommodityBuy",
                   "Buy", commodity_buy);

  window_addButton(secondary_wid, -20, 20*2+BUTTON_HEIGHT,
                   (BUTTON_WIDTH-20)/2, BUTTON_HEIGHT, "btnCommoditySell",
                   "Sell", commodity_sell);

  window_addText(secondary_wid, -20, -40, BUTTON_WIDTH, 60, 0,
                 "txtSInfo", &gl_smallFont, &cDConsole,
                 "You have:\n"
                 "Market price:\n"
                 "\n"
                 "Free Space:\n");

  window_addText(secondary_wid, -20, -40, BUTTON_WIDTH/2, 60, 0,
                 "txtDInfo", &gl_smallFont, &cBlack, NULL);

  window_addText(secondary_wid, -40, -100, BUTTON_WIDTH-20,
                 BUTTON_WIDTH, 0, "txtDesc", &gl_smallFont, &cBlack, NULL);

  goods = malloc(sizeof(char*)*land_planet->ncommodities);
  for(i = 0; i < land_planet->ncommodities; i++)
    goods[i] = strdup(land_planet->commodities[i]->name);

  window_addList(secondary_wid, 20, -40,
                 COMMODITY_WIDTH-BUTTON_WIDTH-60,
                 COMMODITY_HEIGHT-80-BUTTON_HEIGHT, "lstGoods",
                 goods, land_planet->ncommodities, 0, commodity_update);

  commodity_update(NULL);
}

static void commodity_exchange_close(char* str) {
  if(strcmp(str, "btnCommodityClose")==0)
    window_destroy(secondary_wid);
}

static void commodity_update(char* str) {
  (void)str;
  char buf[128];
  char* comname;
  Commodity* com;

  comname = toolkit_getList(secondary_wid, "lstGoods");
  com = commodity_get(comname);

  snprintf(buf, 128,
      "%d tons\n"
      "%d Scred\n"
      "\n"
      "%d tons\n",
      player_cargoOwned(comname),
      com->medium,
      player->cargo_free);

  window_modifyText(secondary_wid, "txtDInfo", buf);
  window_modifyText(secondary_wid, "txtDesc", com->description);
}

static void commodity_buy(char* str) {
  (void)str;
  char* comname;
  Commodity* com;
  int q;

  q = 10;

  comname = toolkit_getList(secondary_wid, "lstGoods");
  com = commodity_get(comname);

  if(player_credits <= q * com->medium) {
    toolkit_alert("Not enough Scred!");
    return;
  }
  else if(player->cargo_free <= 0) {
    toolkit_alert("not enough free space!");
    return;
  }

  q = pilot_addCargo(player, com, q);
  player_credits -= q * com->medium;
  commodity_update(NULL);
}

static void commodity_sell(char* str) {
  (void)str;
  char* comname;
  Commodity* com;
  int q;

  q = 10;
  comname = toolkit_getList(secondary_wid, "lstGoods");
  com = commodity_get(comname);

  q = pilot_rmCargo(player, com, q);
  player_credits += q * com->medium;
  commodity_update(NULL);
}

static void outfits(void) {
  char** outfits;
  int noutfits;
  char buf[128];

  // Create window.
  snprintf(buf, 128, "%s - Outfits", land_planet->name);
  secondary_wid = window_create(buf, -1, -1,
                                OUTFITS_WIDTH, OUTFITS_HEIGHT);
  // Will allow buying from keyboard.
  window_setFptr(secondary_wid, outfits_buy);

  // Buttons.
  window_addButton(secondary_wid, -20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnCloseOutfits",
                   "Close", outfits_close);

  window_addButton(secondary_wid, -40-BUTTON_WIDTH, 40+BUTTON_HEIGHT,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnBuyOutfits",
                   "Buy", outfits_buy);

  window_addButton(secondary_wid, -40-BUTTON_WIDTH, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnSellOutfit",
                   "Sell", outfits_sell);

  // Fancy 128x128 image.
  window_addRect(secondary_wid, -20, -50, 128, 128, "rctImage", &cBlack, 0);
  window_addImage(secondary_wid, -20-128, -50-128, "imgOutfit", NULL, 1);

  window_addCust(secondary_wid, -40-BUTTON_WIDTH, 60+2*BUTTON_HEIGHT,
                 BUTTON_WIDTH, BUTTON_HEIGHT,
                 "cstMod", 0, outfits_renderMod, NULL);

  window_addText(secondary_wid, 40+200+20, -60,
                 80, 96, 0, "txtSDesc", &gl_smallFont, &cDConsole,
                 "Name:\n"
                 "Type:\n"
                 "Owned:\n"
                 "\n"
                 "Space taken:\n"
                 "Free Space:\n"
                 "\n"
                 "Price:\n"
                 "Money:\n");

  window_addText(secondary_wid, 40+200+40+60, -60,
                 250, 96, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL);

  window_addText(secondary_wid, 20+200+40, -200,
                 OUTFITS_WIDTH-300, 200, 0, "txtDescription",
                 &gl_smallFont, NULL, NULL);

  // Set up the outfits to buy/sell.
  outfits = outfit_getTech(&noutfits, land_planet->tech, PLANET_TECH_MAX);
  window_addList(secondary_wid, 20, 40,
                 200, OUTFITS_HEIGHT-80, "lstOutfits",
                 outfits, noutfits, 0, outfits_update);

  // Write the outfits stuff.
  outfits_update(NULL);
}

static void outfits_close(char* str) {
  if(strcmp(str, "btnCloseOutfits")==0)
    window_destroy(secondary_wid);
}

static void outfits_update(char* str) {
  (void)str;
  char* outfitname;
  Outfit* outfit;
  char buf[80], buf2[16], buf3[16];

  outfitname = toolkit_getList(secondary_wid, "lstOutfits");
  outfit = outfit_get(outfitname);

  window_modifyImage(secondary_wid, "imgOutfit", outfit->gfx_store);

  window_modifyText(secondary_wid, "txtDescription", outfit->description);
  credits2str(buf2, outfit->price,  2);
  credits2str(buf3, player_credits, 2);
  snprintf(buf, 80,
           "%s\n"
           "%s\n"
           "%d\n"
           "\n"
           "%d\n"
           "%d\n"
           "\n"
           "%s SCred\n"
           "%s SCred\n",
           outfit->name,
           outfit_getType(outfit),
           player_outfitOwned(outfitname),
           outfit->mass,
           player_freeSpace(),
           buf2,
           buf3);

  window_modifyText(secondary_wid, "txtDDesc", buf);
}

static void outfits_buy(char* str) {
  (void)str;
  char* outfitname;
  Outfit* outfit;
  int q;
  char buf[16];

  outfitname = toolkit_getList(secondary_wid, "lstOutfits");
  outfit = outfit_get(outfitname);

  q = outfits_getMod();

  // Can player actually fit the outfit?
  if((player_freeSpace() - outfit->mass) < 0) {
    toolkit_alert("No enough free space (you need %d more slots).",
                  outfit->mass - player_freeSpace());
    return;
  }
  else if(player_outfitOwned(outfitname) >= outfit->max) {
    // Already has too many.
    toolkit_alert("You can only carry %d of this outfit.", outfit->max);
    return;
  }
  else if(outfit_isAfterburner(outfit) && (player->afterburner != NULL)) {
    toolkit_alert("You can only have one afterburner.");
    return;
  }
  // Not enough $$.
  else if(q*(int)outfit->price >= player_credits) {
    credits2str(buf, q*outfit->price - player_credits, 2);
    toolkit_alert("You need %s more SCred.", buf);
    return;
  }

  player_credits -= outfit->price * pilot_addOutfit(player, outfit,
                                                    MIN(q, outfit->max));
  outfits_update(NULL);
}

static void outfits_sell(char* str) {
  (void)str;
  char* outfitname;
  Outfit* outfit;
  int q;

  outfitname = toolkit_getList(secondary_wid, "lstOutfits");
  outfit = outfit_get(outfitname);

  q = outfits_getMod();

  if(player_outfitOwned(outfitname) <= 0) {
    // No outfits to sell.
    toolkit_alert("You can't sell something you don't have!");
    return;
  }

  player_credits += outfit->price * pilot_rmOutfit(player, outfit, q);
  outfits_update(NULL);
}

// Return the current modifier status.
static int outfits_getMod(void) {
  SDLMod mods;
  int q;

  mods = SDL_GetModState();
  q = 1;
  if(mods & (KMOD_LCTRL  | KMOD_RCTRL))  q *= 5;
  if(mods & (KMOD_LSHIFT  | KMOD_RSHIFT)) q *= 10;

  return q;
}

static void outfits_renderMod(double bx, double by, double w, double h) {
  (void) h;
  int q;
  char buf[8];

  q = outfits_getMod();
  if(q == 1) return;

  snprintf(buf, 8, "%dx", q);
  gl_printMid(&gl_smallFont, w,
              bx + (double)gl_screen.w/2.,
              by + (double)gl_screen.h/2.,
              &cBlack, buf);
}

static void shipyard(void) {
  char** ships;
  int nships;
  char buf[128];

  snprintf(buf, 128, "%s - Shipyard", land_planet->name);
  secondary_wid = window_create(buf,
                                -1, -1, SHIPYARD_WIDTH, SHIPYARD_HEIGHT);

  window_addButton(secondary_wid, -20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnCloseShipyard",
                   "Close", shipyard_close);

  window_addButton(secondary_wid, -20, 40+BUTTON_HEIGHT,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnYourShips",
                   "Your Ships", shipyard_yours);

  window_addButton(secondary_wid, -40-BUTTON_WIDTH, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnBuyShip",
                   "Buy", shipyard_buy);

  window_addButton(secondary_wid, -40-BUTTON_WIDTH, 40+BUTTON_HEIGHT,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnInfoShip",
                   "Info", shipyard_info);

  window_addRect(secondary_wid, -40, -50,
                 128, 96, "rctTarget", &cBlack, 0);

  window_addImage(secondary_wid, -40-128, -50-96,
                  "imgTarget", NULL, 1);

  window_addText(secondary_wid, 40+200+40, -55,
                 80, 96, 0, "txtSDesc", &gl_smallFont, &cDConsole,
                 "Name:\n"
                 "Class:\n"
                 "Fabricator:\n"
                 "\n"
                 "Price:\n"
                 "Money:\n");

  window_addText(secondary_wid, 40+200+40+80, -55,
                 130, 96, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL);


  window_addText(secondary_wid, 20+200+40, -160,
                 SHIPYARD_WIDTH-300, 200, 0, "txtDescription",
                 &gl_smallFont, NULL, NULL);

  // Setup the ships to buy/sell.
  ships = ship_getTech(&nships, land_planet->tech, PLANET_TECH_MAX);
  window_addList(secondary_wid, 20, 40,
                 200, SHIPYARD_HEIGHT-80, "lstShipyard",
                 ships, nships, 0, shipyard_update);

  // Write the shipyard stuff.
  shipyard_update(NULL);
}

static void shipyard_close(char* str) {
  if(strcmp(str, "btnCloseShipyard")==0)
    window_destroy(secondary_wid);
}

static void shipyard_update(char* str) {
  (void)str;
  char* shipname;
  Ship* ship;
  char buf[80], buf2[16], buf3[16];

  shipname = toolkit_getList(secondary_wid, "lstShipyard");
  ship = ship_get(shipname);

  window_modifyImage(secondary_wid, "imgTarget", ship->gfx_target);
  window_modifyText(secondary_wid, "txtDescription", ship->description);

  credits2str(buf2, ship->price,   2);
  credits2str(buf3, player_credits, 2);
  snprintf(buf, 80,
           "%s\n"
           "%s\n"
           "%s\n"
           "\n"
           "%s SCred\n"
           "%s SCred\n",
           ship->name,
           ship_class(ship),
           ship->fabricator,
           buf2,
           buf3);

  window_modifyText(secondary_wid, "txtDDesc", buf);

}

static void shipyard_info(char* str) {
  (void)str;
  char* shipname;

  shipname = toolkit_getList(secondary_wid, "lstShipyard");
  ship_view(shipname);
}

static void shipyard_buy(char* str) {
  (void)str;
  char* shipname;
  Ship* ship;

  shipname = toolkit_getList(secondary_wid, "lstShipyard");
  ship = ship_get(shipname);

  player_newShip(ship, player->solid->pos.x, player->solid->pos.y,
                 0., 0., player->solid->dir);
}

static void shipyard_yours(char* str) {
  (void)str;
  char** ships;
  int nships;

  terciary_wid = window_create("Your Ships",
      -1, -1, SHIPYARD_WIDTH, SHIPYARD_HEIGHT);

  window_addButton(terciary_wid, -20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnCloseYourShips",
                   "Shipyard", shipyard_yoursClose);

  ships = player_ships(&nships);
  window_addList(terciary_wid, 20, 40,
                 200, SHIPYARD_HEIGHT-80, "lstYourShips",
                 ships, nships, 0, NULL);
}

static void shipyard_yoursClose(char* str) {
  (void)str;
  window_destroy(terciary_wid);
}

// Spaceport bar.
static void spaceport_bar(void) {
  secondary_wid = window_create("SpacePort Bar", -1, -1, BAR_WIDTH, BAR_HEIGHT);

  window_addButton(secondary_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
                   "btnCloseBar", "Close", spaceport_bar_close);

  window_addButton(secondary_wid, 20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT, "btnNews",
                   "News", (void(*)(char*))news);

  window_addText(secondary_wid, 20, -30,
                 BAR_WIDTH-40, BAR_HEIGHT - 40 - BUTTON_HEIGHT, 0,
                 "txtDescription", &gl_smallFont, &cBlack,
                 land_planet->bar_description);
}

static void spaceport_bar_close(char* str) {
  if(strcmp(str, "btnCloseBar")==0)
    window_destroy(secondary_wid);
}

// Planet news reports.
static void news(void) {
  terciary_wid = window_create("News Reports",
                           -1, -1, NEWS_WIDTH, NEWS_HEIGHT);

  window_addButton(terciary_wid, -20, 20,
                   BUTTON_WIDTH, BUTTON_HEIGHT,
                   "btnCloseNews", "Close", news_close);

  window_addText(terciary_wid, 20, 20 + BUTTON_HEIGHT + 20,
                 NEWS_WIDTH-40, NEWS_HEIGHT - 20 - BUTTON_HEIGHT - 20 - 20 -20,
                 0, "txtNews", &gl_smallFont, &cBlack,
                 "News reporters report that they are on strike right now! D:");
}

static void news_close(char* str) {
  if(strcmp(str, "btnCloseNews")==0)
    window_destroy(terciary_wid);
}

// Land the player.
void land(Planet* p) {
  if(landed) return;

  // Change music.
  music_load(MUSIC_LAND);
  music_play();

  land_planet = p;
  land_wid = window_create(p->name, -1, -1, LAND_WIDTH, LAND_HEIGHT);

  // Pretty display.
  window_addImage(land_wid, 20, -40, "imgPlanet", p->gfx_exterior, 1);
  window_addText(land_wid, 440, 80, LAND_WIDTH-460, 460, 0,
                 "txtPlanetDesc", &gl_smallFont, &cBlack, p->description);
  // Buttons.
  window_addButton(land_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
                   "btnTakeoff", "Takeoff", (void(*)(char*))takeoff);

  if(planet_hasService(land_planet, PLANET_SERVICE_COMMODITY))
    window_addButton(land_wid, -20, 20 + BUTTON_HEIGHT + 20,
                     BUTTON_WIDTH, BUTTON_HEIGHT, "btnCommodity",
                     "Commodity Exchange", (void(*)(char*))commodity_exchange);

  if(planet_hasService(land_planet, PLANET_SERVICE_SHIPYARD))
    window_addButton(land_wid, -20 - BUTTON_WIDTH - 20, 20,
                     BUTTON_WIDTH, BUTTON_HEIGHT, "btnShipyard",
                     "Shipyard", (void(*)(char*))shipyard);

  if(planet_hasService(land_planet, PLANET_SERVICE_OUTFITS))
    window_addButton(land_wid, -20 - BUTTON_WIDTH - 20, 20 + BUTTON_HEIGHT + 20,
                     BUTTON_WIDTH, BUTTON_HEIGHT, "btnOutfits",
                     "Outfits", (void(*)(char*))outfits);

  if(planet_hasService(land_planet, PLANET_SERVICE_BASIC)) {
    window_addButton(land_wid, 20, 20,
                     BUTTON_WIDTH, BUTTON_HEIGHT, "btnNews",
                     "Mission Terminal", NULL);

    window_addButton(land_wid, 20, 20 + BUTTON_HEIGHT + 20,
                     BUTTON_WIDTH, BUTTON_HEIGHT, "btnBar",
                     "Spaceport Bar", (void(*)(char*))spaceport_bar);
  }


  landed = 1;
}

// Takeoff from the planet.
void takeoff(void) {
  if(!landed) return;

  music_load(MUSIC_TAKEOFF);
  music_play();

  int sw, sh;
  sw = land_planet->gfx_space->w;
  sh = land_planet->gfx_space->h;

  // No longer authorized to land.
  player_rmFlag(PLAYER_LANDACK);

  // Set player to another position with random facing direction and no velocity.
  player_warp(land_planet->pos.x + RNG(-sw/2, sw/2),
      land_planet->pos.y + RNG(-sh/2, sh/2));
  vect_pset(&player->solid->vel, 0., 0.);
  player->solid->dir = RNG(0, 359) * M_PI/180.;

  // Heal the player.
  player->armour = player->armour_max;
  player->shield = player->shield_max;
  player->energy = player->energy_max;

  space_init(NULL);

  land_planet = NULL;
  window_destroy(land_wid);
  landed = 0;
}