#include <malloc.h>
#include <math.h>
#include <float.h>

#include "xml.h"
#include "lephisto.h"
#include "opengl.h"
#include "log.h"
#include "physics.h"
#include "rng.h"
#include "pack.h"
#include "space.h"
#include "faction.h"
#include "xml.h"
#include "pause.h"
#include "weapon.h"
#include "toolkit.h"
#include "spfx.h"
#include "ltime.h"
#include "player.h"

#define XML_PLANET_ID       "Planets"
#define XML_PLANET_TAG      "planet"

#define XML_SYSTEM_ID       "Systems"
#define XML_SYSTEM_TAG      "ssys"

#define PLANET_DATA         "../dat/planet.xml"
#define SYSTEM_DATA         "../dat/ssys.xml"

#define PLANET_GFX_SPACE    "../gfx/planet/space/"
#define PLANET_GFX_EXTERIOR "../gfx/planet/exterior/"

#define PLANET_GFX_EXTERIOR_W 400
#define PLANET_GFX_EXTERIOR_H 400

// Overcome warning due to zero value.

#define FLAG_XSET           (1<<0)
#define FLAG_YSET           (1<<1)
#define FLAG_ASTEROIDSSET   (1<<2)
#define FLAG_INTEFERENCESET (1<<3)
#define FLAG_SERVICESET     (1<<4)
#define FLAG_TECHSET        (1<<5)
#define FLAG_FACTIONSET     (1<<6)

// Planet <-> system name stack.
static char** planetname_stack  = NULL;
static char** systemname_stack  = NULL;
static int    spacename_nstack  = 0;

// Star system stack and co.
StarSystem* systems_stack = NULL; // Star system stack.
int systems_nstack     = 0;       // Number of star systems.
static int  nplanets    = 0;      // Total number of loaded planets - A little silly.
StarSystem* cur_system   = NULL;  // Current star system.

// Fleet spawn rate.
unsigned int spawn_timer = 0; // Controls spawn rate.

// Star stack and co.
#define STAR_BUF 100 // Area to leave around screen, more = less repitition.
typedef struct Star_ {
  double x, y; // Position. It is simpler ligher to use two doubles than the physics.
  double brightness;
} Star;

static Star* stars = NULL;  // Star array.
static int nstars = 0;      // Total stars.
static int mstars = 0;      // Memory stars are taking.

// Intern.
static StarSystem* system_get(const char* sysname);
static Planet* planet_pull(const char* name);
static void space_addFleet(Fleet* fleet);
static StarSystem* system_parse(const xmlNodePtr parent);
static void system_parseJumps(const xmlNodePtr parent);
static PlanetClass planetclass_get(const char a);
// Extern.
extern void player_message(const char* fmt, ...);

// Draw the planet. Used in planet.c
// Matrix mode is already displaced to center of the minimap.
#define PIXEL(x,y)  if((shape == RADAR_RECT && ABS(x)<w/2. && ABS(y)<h/2.) || \
  (shape == RADAR_CIRCLE && (((x)*(x)+(y)*(y)) < rc))) glVertex2i((x),(y))
void planets_minimap(const double res,
                     const double w, const double h, const RadarShape shape) {
  int i;
  int cx, cy, x, y, r, rc;
  double p;

  if(shape == RADAR_CIRCLE) rc = (int)(w*w);

  glBegin(GL_POINTS);
  for(i = 0; i < cur_system->nplanets; i++) {
    if(areEnemies(player->faction, cur_system->planets[i].faction))
      COLOUR(cHostile);
    else if(areAllies(player->faction, cur_system->planets[i].faction))
      COLOUR(cFriend);
    else COLOUR(cNeutral);
    r = (int)(cur_system->planets[i].gfx_space->sw / res);
    cx = (int)((cur_system->planets[i].pos.x - player->solid->pos.x) / res);
    cy = (int)((cur_system->planets[i].pos.y - player->solid->pos.y) / res);

    x = 0;
    y = r;
    p = (5. - (double)(r*3)) / 4.;

    PIXEL(cx,   cy+y);
    PIXEL(cx,   cy-y);
    PIXEL(cx+y, cy);
    PIXEL(cx-y, cy);

    while(x < y) {
      x++;
      if(p < 0) p += 2*(double)(x)+1;
      else p += 2*(double)(x-(--y))+1;

      if(x == 0) {
        PIXEL(cx,   cy+y);
        PIXEL(cx,   cy-y);
        PIXEL(cx+y, cy);
        PIXEL(cx-y, cy);
      } else
        if(x == y) {
          PIXEL(cx+x, cy+y);
          PIXEL(cx-x, cy+y);
          PIXEL(cx+x, cy-y);
          PIXEL(cx-x, cy-y);
        } else
          if(x < y) {
            PIXEL(cx+x, cy+y);
            PIXEL(cx-x, cy+y);
            PIXEL(cx+x, cy-y);
            PIXEL(cx-x, cy-y);
            PIXEL(cx+y, cy+x);
            PIXEL(cx-y, cy+x);
            PIXEL(cx+y, cy-x);
            PIXEL(cx-y, cy-x);
          }
    }
  }
  glEnd();
}
#undef PIXEL

// A* Algorithm fo shortest path finding.

// The node struct.
typedef struct SysNode_ {
  struct SysNode_* next, *gnext;

  struct SysNode_* parent;
  StarSystem* sys;
  double r; // Ranking.
  int g;    // Step.
} SysNode;

static SysNode* A_gc;
// Prototypes.
static SysNode* A_newNode(StarSystem* sys, SysNode* parent);
static double A_h(StarSystem* n, StarSystem* g);
static double A_g(SysNode* n);
static SysNode* A_add(SysNode* first, SysNode* cur);
static SysNode* A_rm(SysNode* first, StarSystem* cur);
static int A_in(SysNode* first, StarSystem* cur);
static SysNode* A_lowest(SysNode* first);
static void A_freeList(SysNode* first);

// Creates a new node link to star system.
static SysNode* A_newNode(StarSystem* sys, SysNode* parent) {
  SysNode* n;

  n = malloc(sizeof(SysNode));

  n->next = NULL;
  n->parent = parent;
  n->sys = sys;
  n->r = DBL_MAX;
  n->g = 0.;

  n->gnext = A_gc;
  A_gc = n;

  return n;
}

static double A_h(StarSystem* n, StarSystem* g) {
  // Euclidean distance.
  return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.;
}

// Get the g from a node.
static double A_g(SysNode* n) {
  return n->g;
}

// Add a node to the linkes list.
static SysNode* A_add(SysNode* first, SysNode* cur) {
  SysNode* n;

  if(first == NULL)
    return cur;

  n = first;
  while(n->next != NULL)
    n = n->next;
  n->next = cur;

  return first;
}

// Remove a node from a linked list.
static SysNode* A_rm(SysNode* first, StarSystem* cur) {
  SysNode* n, *p;

  if(first->sys == cur) {
    first->next = NULL;
    n = first->next;
    return n;
  }

  p = first;
  n = p->next;
  do {
    if(n->sys == cur) {
      n->next = NULL;
      p->next = n->next;
      break;
    }
  } while((n=n->next) != NULL);

  return first;
}

// Check if node is in linked list.
static int A_in(SysNode* first, StarSystem* cur) {
  SysNode* n;
  
  if(first == NULL)
    return 0;

  n = first;
  do {
    if(n->sys == cur)
      return 1;
  } while((n=n->next) != NULL);
  return 0;
}

// Return the lowest ranking node from a linked list of nodes.
static SysNode* A_lowest(SysNode* first) {
  SysNode* lowest, *n;

  if(first == NULL)
    return NULL;

  n = first;
  lowest = n;
  do {
    if(n->r < lowest->r)
      lowest = n;
  } while((n=n->next) != NULL);

  return lowest;
}

// Free a linked list.
static void A_freeList(SysNode* first) {
  SysNode* p, *n;

  if(first == NULL)
    return;

  p = NULL;
  n = first;
  do {
    if(p != NULL)
      free(p);
    p = n;
  } while((n=n->gnext) != NULL);

  free(p);
}

StarSystem** system_getJumpPath(int* njumps, char* sysstart, char* sysend) {
  int i, cost;

  StarSystem* ssys, *esys, **res;

  SysNode* cur, *neighbour;
  SysNode* open, *closed;

  A_gc = NULL;

  // Initial and target systems.
  ssys = system_get(sysstart);  // Start.
  esys = system_get(sysend);    // End.

  // Start the linked lists.
  open = closed = NULL;
  cur = A_newNode(ssys, NULL);
  open = A_add(open, cur); // Initial open node is the start system.

  while((cur = A_lowest(open))->sys != esys) {
    // Get best from open and toss to closed.
    open = A_rm(open, cur->sys);
    closed = A_add(closed, cur);

    for(i = 0; i < cur->sys->njumps; i++) {
      neighbour = A_newNode(&systems_stack[cur->sys->jumps[i]], cur);
      cost = A_g(cur) + 1;

      if(A_in(open, neighbour->sys) && (cost < A_g(neighbour)))
        open = A_rm(open, neighbour->sys); // New path is better.

      if(A_in(closed, neighbour->sys) && (cost < A_g(neighbour)))
        closed = A_rm(closed, neighbour->sys); // Shouldn't happen.

      if(!A_in(open, neighbour->sys) && !A_in(closed, neighbour->sys)) {
        neighbour->g = cost;
        open = A_add(open, neighbour);
        neighbour->r = (double)A_g(neighbour) + A_h(neighbour->sys, esys);
      }
    }
  }
  // Build the path backwards.
  (*njumps) = A_g(cur);
  res = malloc(sizeof(StarSystem*) * (*njumps));
  for(i = 0; i < (*njumps); i++) {
    res[(*njumps)-i-1] = cur->sys;
    cur = cur->parent;
  }

  // Free the linked list.
  //A_freeList(open);
  //A_freeList(closed);
  A_freeList(A_gc);
  return res;
}

static PlanetClass planetclass_get(const char a) {
  switch(a) {
  // Planets use letters.
  case 'A': return PLANET_CLASS_A;
  case 'B': return PLANET_CLASS_B;
  case 'C': return PLANET_CLASS_C;
  case 'D': return PLANET_CLASS_D;
  case 'E': return PLANET_CLASS_E;
  case 'F': return PLANET_CLASS_F;
  case 'G': return PLANET_CLASS_G;
  case 'H': return PLANET_CLASS_H;
  case 'I': return PLANET_CLASS_I;
  case 'J': return PLANET_CLASS_J;
  case 'K': return PLANET_CLASS_K;
  case 'L': return PLANET_CLASS_L;
  case 'M': return PLANET_CLASS_M;
  case 'N': return PLANET_CLASS_N;
  case 'O': return PLANET_CLASS_O;
  case 'P': return PLANET_CLASS_P;
  case 'Q': return PLANET_CLASS_Q;
  case 'R': return PLANET_CLASS_R;
  case 'S': return PLANET_CLASS_S;
  case 'T': return PLANET_CLASS_T;
  case 'X': return PLANET_CLASS_X;
  case 'Y': return PLANET_CLASS_Y;
  case 'Z': return PLANET_CLASS_Z;

    // Stations use numbers as there isn't as many.
  case '0' : return STATION_CLASS_A;

  default: return PLANET_CLASS_NULL;
  };
}

// Check distance to ensure we can go into hyperspace.
int space_canHyperspace(Pilot* p) {
  int i;
  double d;
  if(p->fuel < HYPERSPACE_FUEL) return 0;

  for(i = 0; i < cur_system->nplanets; i++) {
    d = vect_dist(&p->solid->pos, &cur_system->planets[i].pos);
    if(d < MIN_HYPERSPACE_DIST)
      return 0;
  }
  return 1;
}

// Hyperspace, returns 0 if entering hyperspace, or the distance if not.
int space_hyperspace(Pilot* p) {
  if(p->fuel < HYPERSPACE_FUEL) return -3;
  if(!space_canHyperspace(p)) return -1;

  // Pilot is now going to get automatically ready for hyperspace.
  pilot_setFlag(p, PILOT_HYP_PREP);

  return 0;
}

// Return the name of all the planets that belong to factions.
char** space_getFactionPlanet(int* nplanets, int* factions, int nfactions) {
  int i, j, k;
  Planet* planet;
  char** tmp;
  int ntmp;
  int mtmp;

  ntmp = 0;
  mtmp = 25;
  tmp = malloc(sizeof(char*) * mtmp);

  for(i = 0; i < systems_nstack; i++)
    for(j = 0; j < systems_stack[i].nplanets; j++) {
      planet = &systems_stack[i].planets[j];
      for(k = 0; k < nfactions; k++)
        if((faction_isFaction(factions[k]) &&
              (planet->faction == factions[k])) ||
            (faction_isAlliance(factions[k]) &&
             faction_ofAlliance(planet->faction, factions[k]))) {
          ntmp++;
          if(ntmp > mtmp) {
            mtmp += 25;
            tmp = realloc(tmp, sizeof(char*) * mtmp);
          }
          tmp[ntmp-1] = planet->name;
          break; // No need to check all factions.
        }
    }
  (*nplanets) = ntmp;
  return tmp;
}

// Return the name of a random planet.
char* space_getRndPlanet(void) {
  int i, j;
  char** tmp;
  int ntmp;
  int mtmp;
  char* res;

  ntmp = 0;
  mtmp = 25;
  tmp = malloc(sizeof(char)*mtmp);

  for(i = 0; i < systems_nstack; i++)
    for(j = 0; j < systems_stack[i].nplanets; j++) {
      ntmp++;
      if(ntmp > mtmp) {
        mtmp += 25;
        tmp = realloc(tmp, sizeof(char*) * mtmp);
      }
      tmp[ntmp-1] = systems_stack[i].planets[j].name;
    }

  res = tmp[RNG(0, ntmp-1)];
  free(tmp);

  return res;
}

// Get the system from it's name.
static StarSystem* system_get(const char* sysname) {
  int i;

  for(i = 0; i < systems_nstack; i++)
    if(strcmp(sysname, systems_stack[i].name)==0)
      return &systems_stack[i];

  DEBUG("System '%s' not found in stack", sysname);
  return NULL;
}

// Get the name of a system from a planetname.
char* planet_getSystem(char* planetname) {
  int i;
  for(i = 0; i < spacename_nstack; i++)
    if(strcmp(planetname_stack[i], planetname)==0)
      return systemname_stack[i];

  DEBUG("Planet '%s' not found in planetname stack", planetname);
  return NULL;
}

// Get a planet based on it's name.
Planet* planet_get(char* planetname) {
  int i;
  char* sys;
  StarSystem* system;

  sys = planet_getSystem(planetname);
  system = system_get(sys);

  for(i = 0; i < system->nplanets; i++)
    if(strcmp(planetname, system->planets[i].name)==0)
      return &system->planets[i];
  DEBUG("Planet '%s' not found in the universe", planetname);
  return NULL;
}

// Basically used for spawning fleets.
void space_update(const double dt) {
  unsigned int t;
  int i, j, f;

  (void)dt; // Don't need it right now.

  if(cur_system == NULL) return; // Can't update a null system.

  t = SDL_GetTicks();

  if(cur_system->nfleets == 0)
    // Please stop checking that there are no fleets.
    spawn_timer = t + 300000;

  if(spawn_timer < t) {
    // Time to possibly spawn.

    // Spawn chance is based on overall percentage.
    f = RNG(0, 100*cur_system->nfleets);
    j = 0;
    for(i = 0; i < cur_system->nfleets; i++) {
      j += cur_system->fleets[i].chance;
      if(f < j) {
        // Add one fleet.
        space_addFleet(cur_system->fleets[i].fleet);
        break;
      }
    }
    spawn_timer = t + 60000./(float)cur_system->nfleets;
  }
}

// Crate a fleet.
static void space_addFleet(Fleet* fleet) {
  int i;
  double a;
  Vec2 vv, vp, vn;

  // Simulate them coming from hyperspace.
  vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*1.5),
            RNG(0, 360)*M_PI/180.);
  vectnull(&vn);

  for(i = 0; i < fleet->npilots; i++)
    if(RNG(0, 100) <= fleet->pilots[i].chance) {
      vect_cadd(&vp, RNG(75, 150) * (RNG(0,1) ? 1 : -1),
                RNG(75, 150) * (RNG(0,1) ? 1 : -1));

      a = vect_angle(&vp, &vn);
      vectnull(&vv);

      pilot_create(fleet->pilots[i].ship,
                   fleet->pilots[i].name,
                   fleet->faction,
                   fleet->ai,
                   a,
                   &vp,
                   &vv,
                   0);
    }
}

// Init the system.
void space_init(const char* sysname) {
  char* lt;
  int i;

  // Cleanup some stuff.
  player_clear(); // Clears targets.
  pilots_clean(); // Destroy all the current pilots, exept player.
  weapon_clear(); // Get rid of all the weapons.
  spfx_clear();  // Remove of explosions.

  if((sysname == NULL) && (cur_system == NULL))
    ERR("Cannot reinit system if there is no system previously loaded");
  else if(sysname != NULL) {
    for(i = 0; i < systems_nstack; i++)
      if(strcmp(sysname, systems_stack[i].name)==0)
        break;
    if(i == systems_nstack) ERR("System %s not found in stack", sysname);
    cur_system = systems_stack+i;

    lt = ltime_pretty(0);
    player_message("Entering System %s on %s", sysname, lt);
    free(lt);

    // Set up stars.
    nstars = (cur_system->stars*SCREEN_W*SCREEN_H+STAR_BUF*STAR_BUF)/(800*640);
    if(mstars < nstars)
      stars = realloc(stars, sizeof(Star)*nstars); // should realloc not malloc.
    for(i = 0; i < nstars; i++) {
      stars[i].brightness = (double)RNG(50, 200)/256.;
      stars[i].x = (double)RNG(-STAR_BUF, SCREEN_W + STAR_BUF);
      stars[i].y = (double)RNG(-STAR_BUF, SCREEN_H + STAR_BUF);
    }
  }
  // Set up fleets -> pilots.
  for(i = 0; i < cur_system->nfleets; i++)
    if(RNG(0,100) <= (cur_system->fleets[i].chance/2)) // Fleet check (50% chance).
      space_addFleet(cur_system->fleets[i].fleet);

  // Start the spawn timer.
  spawn_timer = SDL_GetTicks() + 120000./(float)(cur_system->nfleets+1);
}

// Load the planets of name 'name'.
static Planet* planet_pull(const char* name) {
  int i;

  Planet* tmp = NULL;

  char str[PATH_MAX] = "\0";
  char* tstr;

  uint32_t flags = 0;

  uint32_t bufsize;
  char* buf = pack_readfile(DATA, PLANET_DATA, &bufsize);

  xmlNodePtr node, cur, ccur;
  xmlDocPtr doc = xmlParseMemory(buf, bufsize);

  node = doc->xmlChildrenNode;
  if(strcmp((char*)node->name, XML_PLANET_ID)) {
    ERR("Malformed "PLANET_DATA" file: missing root element '"XML_PLANET_ID"'");
    return NULL;
  }

  node = node->xmlChildrenNode; // First system node.
  if(node == NULL) {
    ERR("Malformed "PLANET_DATA" file: does not contain elements");
    return NULL;
  }

  do {
    if(xml_isNode(node, XML_PLANET_TAG)) {
      tstr = xml_nodeProp(node, "name");
      if(strcmp(tstr, name)==0) { // Found.
        tmp = CALLOC_L(Planet);
        tmp->name = tstr;

        node = node->xmlChildrenNode;

        do {
          if(xml_isNode(node, "GFX")) {
            cur = node->children;
            do {
              if(xml_isNode(cur, "space")) {
                // Load space gfx.
                snprintf(str, strlen(xml_get(cur))+sizeof(PLANET_GFX_SPACE),
                         PLANET_GFX_SPACE"%s", xml_get(cur));
                tmp->gfx_space = gl_newImage(str);
              }
              else if(xml_isNode(cur, "exterior")) {
                // Load land gfx.
                snprintf(str, strlen(xml_get(cur))+sizeof(PLANET_GFX_EXTERIOR),
                         PLANET_GFX_EXTERIOR"%s", xml_get(cur));
                tmp->gfx_exterior = gl_newImage(str);
              }
            } while(xml_nextNode(cur));
          }
          else if(xml_isNode(node, "pos")) {
            cur = node->children;
            do {
              if(xml_isNode(cur, "x")) {
                flags |= FLAG_XSET;
                tmp->pos.x = xml_getFloat(cur);
              }
              else if(xml_isNode(cur, "y")) {
                flags |= FLAG_YSET;
                tmp->pos.y = xml_getFloat(cur);
              }
            } while(xml_nextNode(cur));
          }
          else if(xml_isNode(node, "general")) {
            cur = node->children;
            do {
              if(xml_isNode(cur, "class"))
                tmp->class = planetclass_get(cur->children->content[0]);
              else if(xml_isNode(cur, "faction")) {
                flags |= FLAG_FACTIONSET;
                tmp->faction = faction_get(xml_get(cur));
              }
              else if(xml_isNode(cur, "description"))
                tmp->description = strdup(xml_get(cur));
              else if(xml_isNode(cur, "bar"))
                tmp->bar_description = strdup(xml_get(cur));
              else if(xml_isNode(cur, "services")) {
                flags |= FLAG_SERVICESET;
                tmp->services = xml_getInt(cur);
              }
              else if(xml_isNode(cur, "tech")) {
                ccur = cur->children;
                do {
                  if(xml_isNode(ccur, "main")) {
                    flags |= FLAG_TECHSET;
                    tmp->tech[0] = xml_getInt(ccur);
                  }
                  else if(xml_isNode(ccur, "special")) {
                    for(i = 1; i < PLANET_TECH_MAX; i++)
                      if(tmp->tech[i]==0) {
                        tmp->tech[i] = xml_getInt(ccur);
                        break;
                      }
                    if(i == PLANET_TECH_MAX) WARN("Planet '%s' has too many"
                                                  "'special tech' entries", tmp->name);
                  }
                } while((ccur = ccur->next));
              }
              else if(xml_isNode(cur, "commodities")) {
                ccur = cur->children;
                do {
                  if(xml_isNode(ccur, "commodity")) {
                    tmp->commodities = realloc(tmp->commodities,
                                               (tmp->ncommodities+1) * sizeof(Commodity*));
                    tmp->commodities[tmp->ncommodities] =
                        commodity_get(xml_get(ccur));
                    tmp->ncommodities++;
                  }
                } while((ccur = ccur->next));
              }
            } while(xml_nextNode(cur));
          }
        } while(xml_nextNode(node));
        break;
      } else
        free(tstr); // xmlGetProp mallocs the string.
    }
  } while(xml_nextNode(node));

  xmlFreeDoc(doc);
  free(buf);
  xmlCleanupParser();

  // Check elements.
  if(tmp) {
#define MELEMENT(o,s) if(o) WARN("Planet '%s' missing '"s"' element", tmp->name)
    MELEMENT(tmp->gfx_space==NULL,         "GFX_space");
    MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
             tmp->gfx_exterior==NULL, "GFX exterior");
    MELEMENT((flags&FLAG_XSET)==0,         "x");
    MELEMENT((flags&FLAG_YSET)==0,         "y");
    MELEMENT(tmp->class==PLANET_CLASS_NULL,   "class");
    MELEMENT(planet_hasService(tmp, PLANET_SERVICE_LAND) &&
             tmp->description==NULL, "description");
    MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
             tmp->bar_description==NULL, "bar");
    MELEMENT(planet_hasService(tmp, PLANET_SERVICE_BASIC) &&
             (flags & FLAG_FACTIONSET)==0, "faction");
    MELEMENT((flags&FLAG_SERVICESET)==0,    "services");

    MELEMENT((planet_hasService(tmp, PLANET_SERVICE_OUTFITS) ||
              planet_hasService(tmp, PLANET_SERVICE_SHIPYARD)) &&
             (flags&FLAG_TECHSET)==0, "tech");
    MELEMENT(planet_hasService(tmp, PLANET_SERVICE_COMMODITY) &&
             (tmp->ncommodities==0), "commodity");
#undef MELEMENT
  } else
    WARN("No planet found matching name '%s'", name);

  return tmp;
}

// Parse node 'parent' which should be the node of a system.
// Return the StarSystem fully loaded.
static StarSystem* system_parse(const xmlNodePtr parent) {
  Planet* planet = NULL;
  SystemFleet* fleet = NULL;
  StarSystem* tmp = CALLOC_L(StarSystem);
  char* ptrc;
  xmlNodePtr cur, node;

  uint32_t flags;

  tmp->name = xml_nodeProp(parent, "name"); // Already mallocs.

  node = parent->xmlChildrenNode;

  do {
    // Load all the things!
    if(xml_isNode(node, "pos")) {
      cur = node->children;
      do {
        if(xml_isNode(cur, "x")) {
          flags |= FLAG_XSET;
          tmp->pos.x = xml_getFloat(cur);
        }
        if(xml_isNode(cur, "y")) {
          flags |= FLAG_YSET;
          tmp->pos.y = xml_getFloat(cur);
        }
      } while(xml_nextNode(cur));
    }
    else if(xml_isNode(node, "general")) {
      cur = node->children;
      do {
        if(xml_isNode(cur, "stars")) // Non-zero.
          tmp->stars = xml_getInt(cur);
        else if(xml_isNode(cur, "asteroids")) {
          flags |= FLAG_ASTEROIDSSET;
          tmp->asteroids = xml_getInt(cur);
        }
        else if(xml_isNode(cur, "interference")) {
          flags |= FLAG_INTEFERENCESET;
          tmp->interference = xml_getFloat(cur)/100;
        }
      }while(xml_nextNode(cur));
    }
    // Load all the planets.
    else if(xml_isNode(node, "planets")) {
      cur = node->children;
      do {
        if(xml_isNode(cur, "planet")) {
          // Add planet to system.
          nplanets++; // Increase planet counter.
          planet = planet_pull(xml_get(cur));
          tmp->planets = realloc(tmp->planets, sizeof(Planet)*(++tmp->nplanets));
          memcpy(tmp->planets+(tmp->nplanets-1), planet, sizeof(Planet));

          // Add planet <-> star system to name stack.
          spacename_nstack++;
          planetname_stack = realloc(planetname_stack,
              sizeof(char*)*spacename_nstack);
          systemname_stack = realloc(systemname_stack,
              sizeof(char*)*spacename_nstack);
          planetname_stack[spacename_nstack-1] = planet->name;
          systemname_stack[spacename_nstack-1] = tmp->name;
          free(planet);
        }
      } while(xml_nextNode(cur));
    }
    // Load all the fleets.
    else if(xml_isNode(node, "fleets")) {
      cur = node->children;
      do {
        if(xml_isNode(cur, "fleet")) {
          fleet = CALLOC_L(SystemFleet);
          fleet->fleet = fleet_get(xml_get(cur));
          if(fleet->fleet == NULL)
            WARN("Fleet %s for Star System %s not found", xml_get(cur), tmp->name);

          ptrc = xml_nodeProp(cur, "chance"); // Malloc ptrc.
          fleet->chance = atoi(ptrc);
          if(fleet->chance == 0)
            WARN("Fleet %s for Star System %s has 0%% chance to appear",
                 fleet->fleet->name, tmp->name);
          if(ptrc) free(ptrc); // Free the ptrc.

          tmp->fleets = realloc(tmp->fleets, sizeof(SystemFleet)*(++tmp->nfleets));
          memcpy(tmp->fleets+(tmp->nfleets-1), fleet, sizeof(SystemFleet));
          free(fleet);
        }
      } while(xml_nextNode(cur));
    }
  } while(xml_nextNode(node));

  // Check elements.
#define MELEMENT(o,s) if((o) == 0) WARN("Star System '%s' missing '"s"' element", tmp->name)
  MELEMENT(flags&FLAG_XSET, "x");
  MELEMENT(flags&FLAG_YSET, "y");
  MELEMENT(tmp->stars, "stars");
  MELEMENT(flags&FLAG_ASTEROIDSSET, "asteroids"); // Can be 0.
  MELEMENT(flags&FLAG_INTEFERENCESET, "inteference");
#undef MELEMENT

  // Post processing.
  if(tmp->nplanets > 0)
    // TODO: Make dependant on overall planet faction.
    tmp->faction = tmp->planets[0].faction;
  return tmp;
}

// Load the jumps into a system.
static void system_parseJumps(const xmlNodePtr parent) {
  int i;
  StarSystem* system;
  char* name;
  xmlNodePtr cur, node;

  name = xml_nodeProp(parent, "name"); // Already mallocs.
  for(i = 0; i < systems_nstack; i++)
    if(strcmp(systems_stack[i].name, name)==0) {
      system = &systems_stack[i];
      break;
    }
  if(i == systems_nstack)
    WARN("System '%s' was not found in the stack for some reason", name);
  free(name); // No need for it now.

  node = parent->xmlChildrenNode;

  do {
    // Load the data.
    if(xml_isNode(node, "jumps")) {
      cur = node->children;
      do {
        if(xml_isNode(cur, "jump")) {
          for(i = 0; i < systems_nstack; i++)
            if(strcmp(systems_stack[i].name, xml_get(cur))==0) {
              system->njumps++;
              system->jumps = realloc(system->jumps, system->njumps*sizeof(int));
              system->jumps[system->njumps-1] = i;
              break;
            }
          if(i == systems_nstack)
            WARN("System '%s' not found for jump linking", xml_get(cur));
        }
      } while(xml_nextNode(cur));
    }
  } while(xml_nextNode(node));
}

// Load the ENTIRE universe into RAM. -- WOAH!
// -- Used a two system pass to first load the star systems_stack and then set jump routes.
int space_load(void) {
  uint32_t bufsize;
  char* buf = pack_readfile(DATA, SYSTEM_DATA, &bufsize);

  StarSystem* tmp;

  xmlNodePtr node;
  xmlDocPtr doc = xmlParseMemory(buf, bufsize);

  node = doc->xmlChildrenNode;
  if(!xml_isNode(node, XML_SYSTEM_ID)) {
    ERR("Malformed "SYSTEM_DATA" file: missing root element '"XML_SYSTEM_ID"'");
    return -1;
  }
  node = node->xmlChildrenNode; // First system node.
  if(node == NULL) {
    ERR("Malformed "SYSTEM_DATA" file: does not contain elements");
    return -1;
  }
  // Fist pass - Load all the star systems_stack.
  do {
    if(xml_isNode(node, XML_SYSTEM_TAG)) {
      tmp = system_parse(node);
      systems_stack = realloc(systems_stack, sizeof(StarSystem)*(++systems_nstack));
      memcpy(systems_stack+systems_nstack-1, tmp, sizeof(StarSystem));
      free(tmp);
    }
  } while(xml_nextNode(node));

  // Second pass - Load all the jump routes.
  node = doc->xmlChildrenNode->xmlChildrenNode;
  do {
    if(xml_isNode(node, XML_SYSTEM_TAG))
      system_parseJumps(node); // Automatically load the jumps into the system.
  } while(xml_nextNode(node));

  // Cleanup.
  xmlFreeDoc(doc);
  free(buf);
  xmlCleanupParser();

  DEBUG("Loaded %d star system%s with %d planet%s",
        systems_nstack, (systems_nstack==1) ? "" : "s",
        nplanets, (nplanets==1) ? "" : "s");

  return 0;
}

// Render the system. -- Just playing god now.
void space_render(double dt) {
  int i;
  unsigned int t, timer;
  double x, y, m, b;

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix(); // Translation matrix.
  glTranslated(-(double)SCREEN_W/2., -(double)SCREEN_H/2., 0);

  t = SDL_GetTicks();
  if(!player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING) &&
     pilot_isFlag(player, PILOT_HYPERSPACE) && // Hyperspace fancy effect.
     !paused && (player->ptimer-HYPERSPACE_STARS_BLUR < t)) {

    timer = player->ptimer - HYPERSPACE_STARS_BLUR;

    // Fancy hyperspace effects.
    glShadeModel(GL_SMOOTH);

    glBegin(GL_LINES);

    // Lines will be based on velocity.
    m = HYPERSPACE_STARS_LENGTH * (double)(t-timer) / (HYPERSPACE_STARS_BLUR);
    x = m*cos(VANGLE(player->solid->vel)+M_PI);
    y = m*sin(VANGLE(player->solid->vel)+M_PI);

    for(i = 0; i < nstars; i++) {
      glColor4d(1., 1., 1., stars[i].brightness);
      glVertex2d(stars[i].x, stars[i].y);
      glColor4d(1., 1., 1., 0.);
      glVertex2d(stars[i].x+x*stars[i].brightness,stars[i].y+y*stars[i].brightness);
    }
    glEnd();

    glShadeModel(GL_FLAT);

  } else {
    glBegin(GL_POINTS); // Normal rendering.
    if(!paused && !player_isFlag(PLAYER_DESTROYED) &&
        !player_isFlag(PLAYER_CREATING)) { // Update position.
      for(i = 0; i < nstars; i++) {
        b = 13.-10.*stars[i].brightness;
        stars[i].x -= player->solid->vel.x/b*dt;
        stars[i].y -= player->solid->vel.y/b*dt;
        
        // Check for boundaries.
        if(stars[i].x > SCREEN_W + STAR_BUF) stars[i].x = -STAR_BUF;
        else if(stars[i].x < -STAR_BUF) stars[i].x = SCREEN_W + STAR_BUF;
        if(stars[i].y > SCREEN_H + STAR_BUF) stars[i].y = -STAR_BUF;
        else if(stars[i].y < -STAR_BUF) stars[i].y = SCREEN_H + STAR_BUF;

        // Render.
        if((stars[i].x < SCREEN_W) && (stars[i].x > 0) &&
            (stars[i].y < SCREEN_H) && (stars[i].y > 0)) {
          glColor4d(1., 1., 1., stars[i].brightness);
          glVertex2d(stars[i].x, stars[i].y);
        }
      }
    } else { // Just render.
      for(i = 0; i < nstars; i++) {
        if((stars[i].x < SCREEN_W) && (stars[i].x > 0) &&
            (stars[i].y < SCREEN_H) && (stars[i].y > 0)) {
          glColor4d(1., 1., 1., stars[i].brightness);
          glVertex2d(stars[i].x, stars[i].y);
        }
      }
    }
    glEnd(); // GL_POINTS
  }
  glPopMatrix(); // Translation matrix.
}

// Render the planets.
void planets_render(void) {
  if(cur_system == NULL) return;

  int i;
  for(i = 0; i < cur_system->nplanets; i++)
    gl_blitSprite(cur_system->planets[i].gfx_space,
                  cur_system->planets[i].pos.x, cur_system->planets[i].pos.y, 0, 0, NULL);
}

// Clean up the system.
void space_exit(void) {
  int i,j;
  
  // Free the names.
  //if(planetname_stack) free(planetname_stack);
  //if(systemname_stack) free(systemname_stack);
  if(planetname_stack) {
  	 free(planetname_stack);
  	 planetname_stack = NULL;
  }
  if(systemname_stack) {
   free(systemname_stack);
   systemname_stack = NULL;
  }
  spacename_nstack = 0;

  // Free the systems.
  for(i = 0; i < systems_nstack; i++) {
    free(systems_stack[i].name);
    if(systems_stack[i].fleets)
      free(systems_stack[i].fleets);
    if(systems_stack[i].jumps)
      free(systems_stack[i].jumps);

    // Free some planets.
    for(j = 0; j < systems_stack[i].nplanets; j++) {
      free(systems_stack[i].planets[j].name);
      if(systems_stack[i].planets[j].description)
        free(systems_stack[i].planets[j].description);
      if(systems_stack[i].planets[j].bar_description)
        free(systems_stack[i].planets[j].bar_description);

      // Graphics.
      if(systems_stack[i].planets[j].gfx_space)
        gl_freeTexture(systems_stack[i].planets[j].gfx_space);
      if(systems_stack[i].planets[j].gfx_exterior)
        gl_freeTexture(systems_stack[i].planets[j].gfx_exterior);

      // Commodities.
      free(systems_stack[i].planets[j].commodities);
    }
    free(systems_stack[i].planets);
  }
  free(systems_stack);
  systems_stack   = NULL;
  systems_nstack  = 0;

  // Stars must be set free too.
  if(stars) free(stars);
  stars   = NULL;
  nstars  = 0;
}