#include "log.h"
#include "lephisto.h"
#include "toolkit.h"
#include "space.h"
#include "opengl.h"
#include "map.h"

#define WINDOW_WIDTH  550
#define WINDOW_HEIGHT  440

#define MAP_WIDTH    (WINDOW_WIDTH-150)
#define MAP_HEIGHT   (WINDOW_HEIGHT-100)

#define BUTTON_WIDTH  60
#define BUTTON_HEIGHT  40

static int   map_wid  = 0;
static double map_zoom = 1.; // Zoom of the map.
static double map_xpos  = 0.; // Map position.
static double map_ypos  = 0.;
static int map_selected = 0;

static int map_drag = 0; // Is the user dragging the map?

// Extern.
// space.c
extern StarSystem* systems_stack;
extern int systems_nstack;
// player.c
extern int planet_target;
extern int hyperspace_target;

static void map_close(char* str);
static void map_update(void);
static void map_render(double bx, double by, double w, double h);
static void map_mouse(SDL_Event* event, double mx, double my);
static void map_buttonZoom(char* str);

// Open the map window.
void map_open(void) {
  if(map_wid) {
    map_close(NULL);
    return;
  }

  // Set the position to focus on current system.
  map_xpos = cur_system->pos.x;
  map_ypos = cur_system->pos.y;

  map_wid = window_create("Star Map", -1, -1, WINDOW_WIDTH, WINDOW_HEIGHT);

  window_addText(map_wid, -20, -20, 100, 20, 1, "txtSysname",
                 &gl_defFont, &cDConsole, systems_stack[map_selected].name);

  window_addText(map_wid, -20, -60, 90, 20, 0, "txtSFaction",
                 &gl_smallFont, &cDConsole, "Faction:");

  window_addText(map_wid, -20, -60-gl_smallFont.h-5, 80, 100, 0, "txtFaction",
                 &gl_smallFont, &cBlack, NULL);

  window_addText(map_wid, -20, -110, 90, 20, 0, "txtSPlanets",
                 &gl_smallFont, &cDConsole, "Planets:");

  window_addText(map_wid, -20, -110-gl_smallFont.h-5, 80, 100, 0, "txtPlanets",
                 &gl_smallFont, &cBlack, NULL);

  window_addCust(map_wid, 20, -40, MAP_WIDTH, MAP_HEIGHT,
                 "cstMap", 1, map_render, map_mouse);

  window_addButton(map_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
                   "btnClose", "Close", map_close);

  window_addButton(map_wid, 40, 20, 30, 30, "btnZoomIn",  "+", map_buttonZoom);
  window_addButton(map_wid, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom);

  map_update();
}

static void map_close(char* str) {
  (void)str;
  if(map_wid) {
    window_destroy(map_wid);
    map_wid = 0;
  }
}

static void map_update(void) {
  int i;
  StarSystem* sys;
  int f;
  char buf[100];

  sys = &systems_stack[map_selected];

  window_modifyText(map_wid, "txtSysname", sys->name);

  if(sys->nplanets == 0)
    // No planets -> no factions.
    snprintf(buf, 100, "NA");
  else {
    f = -1;
    for(i = 0; i < sys->nplanets; i++) {
      if(f == -1)
        f = sys->planets[i].faction;
      else if(f != sys->planets[i].faction) {
        // TODO: more verbosity.
        snprintf(buf, 100, "Multiple");
        break;
      }
    }
    if(i == sys->nplanets)
      // Saw them all, and all the same.
      snprintf(buf, 100, "%s", faction_name(f));
  }

  window_modifyText(map_wid, "txtFaction", buf);

  buf[0] = '\0';
  if(sys->nplanets == 0)
    snprintf(buf, 100, "None");
  else {
    for(i = 0; i < sys->nplanets; i++) {
      strcat(buf, sys->planets[i].name);
      strcat(buf, "\n");
    }
  }
  window_modifyText(map_wid, "txtPlanets", buf);
}

// Render the map as a custom widget.
static void map_render(double bx, double by, double w, double h) {
  int i, j;
  double x, y, r, tx, ty;
  StarSystem* sys;
  glColour* col;

  r = 5.;
  x = (bx - map_xpos + w/2) * 1.;
  y = (by - map_ypos + h/2) * 1.;
  // Background
  COLOUR(cBlack);
  glBegin(GL_QUADS);
  glVertex2d(bx,   by);
  glVertex2d(bx,   by+h);
  glVertex2d(bx+w,  by+h);
  glVertex2d(bx+w, by);
  glEnd();

  // Render the star systems.
  for(i = 0; i < systems_nstack; i++) {
    sys = &systems_stack[i];

    // Draw the system.
    if(sys == cur_system) COLOUR(cRadar_targ);
    else if(sys->nplanets==0) COLOUR(cInert); // TODO: dependant on planet type.
    else if(areEnemies(player->faction, sys->faction)) COLOUR(cRed);
    else COLOUR(cYellow);

    gl_drawCircleInRect(x + sys->pos.x*map_zoom,
                        y + sys->pos.y*map_zoom,
                        r, bx, by, w, h);

    // Draw the system name.
    tx = x + 7. + sys->pos.x * map_zoom;
    ty = y - 5. + sys->pos.y * map_zoom;
    if((map_zoom >= 1.) && // Can't be tiny.
       ((tx > bx) && (ty > by) && (ty < by+h-6.))) // Width checking.
      gl_printMax(&gl_smallFont, (bx+w)-(tx),
                  tx + gl_screen.w/2., ty + gl_screen.h/2.,
                  &cWhite, sys->name);

    // Draw the hyperspace paths.
    glShadeModel(GL_SMOOTH);
    // Cheaply use transparency instead of actually
    // calculating from x to y the line must go. :)
    for(j = 0; j < sys->njumps; j++) {
      // Set the colours, is the route the current one?
      if(((cur_system == sys) && (j == hyperspace_target)) ||
         ((cur_system == &systems_stack[sys->jumps[j]]) &&
          (sys = &systems_stack[cur_system->jumps[hyperspace_target]])))
        col = &cRed;
      else col = &cDarkBlue;
      glBegin(GL_LINE_STRIP);
      ACOLOUR(*col, 0.);
      tx = x + sys->pos.x * map_zoom;
      ty = y + sys->pos.y * map_zoom;
      if(!((tx < bx) || (tx > bx + w) || (ty < by) || (ty > by+h)))
        glVertex2d(tx, ty);
      COLOUR(*col);
      tx += (systems_stack[sys->jumps[j]].pos.x - sys->pos.x)/2. * map_zoom;
      ty += (systems_stack[sys->jumps[j]].pos.y - sys->pos.y)/2. * map_zoom;
      if(!((tx < bx) || (tx > bx+w) || (ty < by) || (ty > by+h)))
        glVertex2d(tx, ty);
      ACOLOUR(*col, 0.);
      tx = x + systems_stack[sys->jumps[j]].pos.x * map_zoom;
      ty = y + systems_stack[sys->jumps[j]].pos.y * map_zoom;
      if(!((tx < bx) || (tx > bx+w) || (ty < by) || (ty > by+h)))
        glVertex2d(tx, ty);
      glEnd();
    }
    glShadeModel(GL_FLAT);
  }
  // Selected planet.
  sys = &systems_stack[map_selected];
  COLOUR(cRed);
  gl_drawCircleInRect(x + sys->pos.x*map_zoom, y + sys->pos.y*map_zoom,
                      r+3., bx, by, w, h);
}

// Map event handling.
static void map_mouse(SDL_Event* event, double mx, double my) {
  int i, j;
  double x, y, t;

  t = 13.*15.; // Threshold.

  mx -= MAP_WIDTH/2 - map_xpos;
  my -= MAP_HEIGHT/2 - map_ypos;

  switch(event->type) {
  case SDL_MOUSEBUTTONDOWN:
    if(event->button.button == SDL_BUTTON_WHEELUP)
      map_buttonZoom("btnZoomOut");
    else if(event->button.button == SDL_BUTTON_WHEELDOWN)
      map_buttonZoom("btnZoomIn");

    // Selecting star system.
    for(i = 0; i < systems_nstack; i++) {
      x = systems_stack[i].pos.x * map_zoom;
      y = systems_stack[i].pos.y * map_zoom;

      if((pow2(mx-x)+pow2(my-y)) < t) {
        map_selected = i;
        for(j = 0; j < cur_system->njumps; j++) {
          if(i == cur_system->jumps[j]) {
            planet_target = -1; // Override planet_target.
            hyperspace_target = j;
            break;
          }
        }
        map_update();
        break;
      }
    }
    map_drag = 1;
    break;

  case SDL_MOUSEBUTTONUP:
    if(map_drag) map_drag = 0;
    break;

  case SDL_MOUSEMOTION:
    if(map_drag) {
      // Axis is inverted.
      map_xpos -= event->motion.xrel;
      map_ypos += event->motion.yrel;
    }
    break;
  }
}

static void map_buttonZoom(char* str) {
  if(strcmp(str, "btnZoomIn")==0) {
    map_zoom += (map_zoom >= 1.) ? 0.5 : 0.25;
    map_zoom = MIN(2.5, map_zoom);
  }

  else if(strcmp(str, "btnZoomOut")==0) {
    map_zoom -= (map_zoom > 1.) ? 0.5 : 0.25;
    map_zoom = MAX(0.5, map_zoom);
  }
}