/*

    Support for SkymapII / SkymapIIIC & KMD150 ascii files

    Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA

 */


#include "defs.h"
#include <ctype.h>
#include <time.h>
#include "strptime.h"


#define MYNAME "skyforce"


static
arglist_t skyforce_args[] = {
  ARG_TERMINATOR
};

static gbfile *fin, *fout;
static int rte_num, wpt_num;
static short_handle short_h;
static const waypoint *prev_wpt;


static waypoint *
skyforce_parse_coords(const char *str)
{
  waypoint *wpt;

  if (strlen(str) < 38) {
    fatal(MYNAME ": Incomplete line!\n");
  }

  wpt = waypt_new();

  wpt->latitude = atof(str + 21);
  if (str[20] == 'S') {
    wpt->latitude = -wpt->latitude;
  }
  wpt->latitude = ddmm2degrees(wpt->latitude);

  wpt->longitude = atof(str + 30);
  if (str[29] == 'W') {
    wpt->longitude = -wpt->longitude;
  }
  wpt->longitude = ddmm2degrees(wpt->longitude);

  return wpt;
}


static waypoint *
skyforce_parse_wpt(const char *str, int *rte_num)
{
  waypoint *wpt;

  wpt = skyforce_parse_coords(str);
  if (wpt == NULL) {
    return NULL;
  }

  wpt->shortname = lrtrim(xstrndup(str + 10, 9));

  if (rte_num) {
    *rte_num = atoi(str + 2);
  }

  return wpt;
}


static waypoint *
skyforce_parse_trk(const char *str)
{
  char *cx;
  struct tm tm;
  char buf[15];
  int len;

  waypoint *wpt;

  wpt = skyforce_parse_coords(str);
  if (wpt == NULL) {
    return NULL;
  }

  memset(&tm, 0, sizeof(tm));
  strncpy(buf, str + 2, sizeof(buf) - 1);
  buf[14] = 0;

  cx = strptime(buf, "%d%m%y  %H%M%S ", &tm);
  if ((cx != NULL) && (*cx != '\0')) {
    fatal(MYNAME ": Could not parse date string (%s - %s).\n", buf, cx);
  }

  wpt->creation_time = mkgmtime(&tm);

  len = strlen(str);

  if (len >= 45) {
    WAYPT_SET(wpt, speed, KNOTS_TO_MPS(atof(str + 39)));
  }
  if (len >= 59) {
    wpt->altitude = FEET_TO_METERS(atof(str + 54));
    if (str[53] == '-') {
      wpt->altitude = -wpt->altitude;
    }
  }

  return wpt;
}


static void
skyforce_head_disp_cb(const route_head *head)
{
  prev_wpt = NULL;
  if (head->rte_waypt_ct <= 0) {
    return;
  }

  wpt_num = 0;
  rte_num++;

  if (rte_num > 999) {
    if (rte_num == 1000) {
      warning(MYNAME ": Can't store more than 999 routes. Some routes skipped!\n");
    }
    return;
  }
}


static void
skyforce_waypt_disp_cb(const waypoint *wpt)
{
  char buf[75];	/* long enough for all data types */
  double lat, lon;


  memset(buf, ' ', sizeof(buf));
  buf[sizeof(buf) - 1] = '\0';

  switch (global_opts.objective) {
  case wptdata:
    buf[0] = 'W';
    break;
  case trkdata:
    buf[0] = 'L';
    break;
  case rtedata:
    buf[0] = 'R';
    break;
  default: ; /* should never happen */
  }

  if (global_opts.objective == trkdata) {
    struct tm tm;

    tm = *gmtime(&wpt->creation_time);
    strftime(buf + 2, sizeof(buf) - 2, "%d%m%y  %H%M%S    ", &tm);
  } else {
    char *name;

    if (rte_num > 999) {
      return;
    }

    wpt_num++;
    if (wpt_num > 999) {
      if (wpt_num == 1000) {
        warning(MYNAME ": Can't store more than 999 waypoints. Some waypoints skipped!\n");
      }
      return;
    }
    if (global_opts.synthesize_shortnames) {
      name = mkshort_from_wpt(short_h, wpt);
    } else {
      name = mkshort(short_h, wpt->shortname);
    }

    if (global_opts.objective == rtedata) {
      snprintf(buf + 2, sizeof(buf) - 2, "%03d ", rte_num);
    }
    snprintf(buf + 6, sizeof(buf) - 6, "%03d %-9s ", wpt_num, name);
  }


  lat = degrees2ddmm(wpt->latitude);
  buf[20] = (wpt->latitude < 0) ? 'S' : 'N';
  snprintf(&buf[21], sizeof(buf) - 21, "%06.2f ", fabs(lat));

  lon = degrees2ddmm(wpt->longitude);
  buf[29] = (wpt->longitude < 0) ? 'W' : 'E';
  snprintf(&buf[30], sizeof(buf) - 30, "%08.2f ", fabs(lon));

  if (global_opts.objective == trkdata) {
    double alt, speed;

    if (wpt->altitude == unknown_alt) {
      alt = 0;
    } else {
      alt = METERS_TO_FEET(wpt->altitude);
    }
    speed = MPS_TO_KNOTS(waypt_speed(prev_wpt, wpt));

    snprintf(&buf[39], sizeof(buf) - 39, "%06.2f 000.00 %c%05d",
             speed,
             alt < 0 ? '-' : '+', si_round(fabs(alt)));
  }

  rtrim(buf);
  gbfprintf(fout, "%s\n", buf);

  prev_wpt = wpt;
}

/*******************************************************************************
* %%%        global callbacks called by gpsbabel main process              %%% *
*******************************************************************************/

static void
skyforce_rd_init(const char *fname)
{
  fin = gbfopen(fname, "r", MYNAME);
}


static void
skyforce_rd_deinit(void)
{
  gbfclose(fin);
}


static void
skyforce_read(void)
{
  char *str;
  route_head *rte, *trk;

  wpt_num = 0;
  rte = trk = NULL;
  rte_num = -1;

  while ((str = gbfgetstr(fin))) {

    waypoint *wpt;
    int i;

    str = lrtrim(str);
    if (*str == '\0') {
      continue;
    }

    switch (*str) {

    case 'W':
      wpt = skyforce_parse_wpt(str, NULL);
      if (wpt == NULL) {
        continue;
      }
      waypt_add(wpt);
      break;

    case 'R':
      wpt = skyforce_parse_wpt(str, &i);
      if (wpt == NULL) {
        continue;
      }

      if (i != rte_num) {
        rte_num = i;
        rte = NULL;
      }

      if (rte == NULL) {
        rte = route_head_alloc();
        route_add_head(rte);
        rte->rte_num = rte_num;
      }
      route_add_wpt(rte, wpt);
      break;

    case 'L':
      wpt = skyforce_parse_trk(str);
      if (wpt == NULL) {
        continue;
      }
      if (trk == NULL) {
        trk = route_head_alloc();
        track_add_head(trk);
      }
      track_add_wpt(trk, wpt);
      break;

    default:
      fatal(MYNAME ": Invalid line marker '%c'!\n", *str);
    }
  }
}


static void
skyforce_wr_init(const char *fname)
{
  fout = gbfopen(fname, "w", MYNAME);

  short_h = mkshort_new_handle();

  setshort_length(short_h, 9);
  setshort_badchars(short_h, "\r\n\t");
  setshort_mustupper(short_h, 1);
  setshort_mustuniq(short_h, 1);
  setshort_whitespace_ok(short_h, 0);
  setshort_repeating_whitespace_ok(short_h, 0);

  wpt_num = 0;
  rte_num = 0;
}


static void
skyforce_wr_deinit(void)
{
  mkshort_del_handle(&short_h);
  gbfclose(fout);
}


static void
skyforce_write(void)
{
  switch (global_opts.objective) {	/* We can only write one data type at a time */

  case wptdata:
    setshort_defname(short_h, "WPT");
    waypt_disp_all(skyforce_waypt_disp_cb);
    break;

  case rtedata:
    setshort_defname(short_h, "RTE");
    setshort_mustuniq(short_h, 0);
    route_disp_all(skyforce_head_disp_cb, NULL, skyforce_waypt_disp_cb);
    break;

  case trkdata:
    track_disp_all(skyforce_head_disp_cb, NULL, skyforce_waypt_disp_cb);
    break;

  case posndata:
    fatal(MYNAME ": Realtime positioning not supported.\n");
    break;

  default:
    fatal(MYNAME ": Unknown data mode!\n");
  }
}


/**************************************************************************/

ff_vecs_t skyforce_vecs = {
  ff_type_file,
  FF_CAP_RW_ALL,	/* read and write waypoints, tracks and routes*/
  skyforce_rd_init,
  skyforce_wr_init,
  skyforce_rd_deinit,
  skyforce_wr_deinit,
  skyforce_read,
  skyforce_write,
  NULL,
  skyforce_args,
  CET_CHARSET_ASCII, 1
};

/**************************************************************************/
