/*
    Copyright (C) 2002 Robert Lipe, robertlipe+source@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

 */
#include "defs.h"
#include "src/core/file.h"
#include <QDebug>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>

static char* deficon = nullptr;
static char* nuke_placer;
static gbfile* ofd;
static QString ostring;
static QXmlStreamWriter writer(&ostring);

static
QVector<arglist_t> geo_args = {
  {"deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr },
  {"nuke_placer", &nuke_placer, "Omit Placer name", nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr },
};

#define MYNAME "geo"

// This really should be class-local...
static QXmlStreamReader reader;
static QString geo_read_fname;

static geocache_container wpt_container(const QString&);

static void GeoReadLoc()
{
  Waypoint* wpt = nullptr;
  QString current_tag;

  while (!reader.atEnd()) {
    auto tag_name = reader.name();
    if (reader.tokenType()==QXmlStreamReader::StartElement) {
      current_tag.append("/");
      current_tag.append(tag_name);
      if (current_tag == u"/loc/waypoint") {
        wpt = new Waypoint;
        wpt->AllocGCData();
        // There is no 'unknown' alt value and so many reference files have
        // leaked it that we just paper over that here.
        wpt->altitude = 0;
      } else if (current_tag == u"/loc/waypoint/name") {
        QXmlStreamAttributes a = reader.attributes();
        wpt->shortname = a.value("id").toString();
        wpt->description = reader.readElementText();
      } else if (current_tag == u"/loc/waypoint/coord") {
        QXmlStreamAttributes a = reader.attributes();
        wpt->latitude = a.value("lat").toString().toDouble();
        wpt->longitude = a.value("lon").toString().toDouble();
      } else if (current_tag == u"/loc/waypoint/type") {
        wpt->icon_descr = reader.readElementText();
      } else if (current_tag == u"/loc/waypoint/link") {
        QXmlStreamAttributes a = reader.attributes();
        waypt_add_url(wpt,
                      reader.readElementText(), a.value("text").toString());
      } else if (current_tag == u"/loc/waypoint/difficulty") {
        wpt->gc_data->diff = reader.readElementText().toDouble() * 10;
      } else if (current_tag == u"/loc/waypoint/terrain") {
        wpt->gc_data->terr = reader.readElementText().toDouble() * 10;
      } else if (current_tag == u"/loc/waypoint/container") {
        wpt->gc_data->container = wpt_container(reader.readElementText());
      }
    }

    // The tokenType may have changed to EndElement as a result of readElementText.
    if (reader.tokenType() == QXmlStreamReader::EndElement) {
      if (current_tag == u"/loc/waypoint") {
        waypt_add(wpt);
      }
      current_tag.chop(tag_name.length() + 1);
    }

    reader.readNext();
  }
}

static void
geo_rd_init(const QString& fname)
{
  geo_read_fname = fname;
}

static void
geo_read()
{
  gpsbabel::File file(geo_read_fname);
  file.open(QIODevice::ReadOnly);
  reader.setDevice(&file);

  GeoReadLoc();
  if (reader.hasError())  {
    fatal(MYNAME ":Read error: %s (%s, line %ld, col %ld)\n",
          qPrintable(reader.errorString()),
          qPrintable(file.fileName()),
          (long) reader.lineNumber(),
          (long) reader.columnNumber());
  }
}

geocache_container wpt_container(const QString& args)
{
  geocache_container v;

  switch (args.toInt()) {
  case 1:
    v = gc_unknown;
    break;
  case 2:
    v = gc_micro;
    break;
  case 3:
    v = gc_regular;
    break;
  case 4:
    v = gc_large;
    break;
  case 5:
    v = gc_virtual;
    break;
  case 6:
    v = gc_other;
    break;
  case 8:
    v = gc_small;
    break;
  default:
    v = gc_unknown;
    break;
  }
  return v;
}

static void
geo_rd_deinit()
{
}

static void
geo_wr_init(const QString& fname)
{
  ofd = gbfopen(fname, "w", MYNAME);

  //writer.setAutoFormatting(true);
  writer.setAutoFormattingIndent(0);
  writer.writeStartDocument();

}

static void
geo_wr_deinit()
{
  writer.writeEndDocument();
  gbfputs(ostring,ofd);
  gbfclose(ofd);
  ofd = nullptr;
}

static void
geo_waypt_pr(const Waypoint* waypointp)
{
  writer.writeStartElement(QStringLiteral("waypoint"));

  writer.writeStartElement(QStringLiteral("name"));
  writer.writeAttribute(QStringLiteral("id"), waypointp->shortname);
  // TODO: this could be writeCharacters, but it's here for compat with pre
  // Qt writer.
  writer.writeCDATA(waypointp->description);
  writer.writeEndElement();

  writer.writeStartElement(QStringLiteral("coord"));
  writer.writeAttribute(QStringLiteral("lat"), QString::number(waypointp->latitude, 'f'));
  writer.writeAttribute(QStringLiteral("lon"), QString::number(waypointp->longitude, 'f'));
  writer.writeEndElement();

  writer.writeTextElement(QStringLiteral("type"), deficon ? deficon : waypointp->icon_descr);

  if (waypointp->HasUrlLink()) {
    writer.writeStartElement(QStringLiteral("link"));
    writer.writeAttribute(QStringLiteral("text "), QStringLiteral("Cache Details"));
    UrlLink link = waypointp->GetUrlLink();
    writer.writeCharacters(link.url_);
    writer.writeEndElement();
  }

  if (waypointp->gc_data && waypointp->gc_data->diff) {
    writer.writeTextElement(QStringLiteral("difficulty"),
                            QString::number(waypointp->gc_data->diff/10));
    writer.writeTextElement(QStringLiteral("terrain"),
                            QString::number(waypointp->gc_data->terr/10));

    int v = 1;
    switch (waypointp->gc_data->container) {
    case gc_unknown:
      v = 1;
      break;
    case gc_micro:
      v = 2;
      break;
    case gc_regular:
      v = 3;
      break;
    case gc_large:
      v = 4;
      break;
    case gc_virtual:
      v = 5;
      break;
    case gc_other:
      v = 6;
      break;
    case gc_small:
      v = 8;
      break;
    default:
      v = 1;
      break;
    }
    writer.writeTextElement(QStringLiteral("container"),
                            QString::number(v));
  }

  writer.writeEndElement();
}

static void
geo_write()
{
  writer.writeStartElement(QStringLiteral("loc"));
  writer.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
  // TODO: This could be moved to wr_init, but the pre GPX version put the two
  // lines above this, so mimic that behaviour exactly.
  writer.setAutoFormatting(true);
  writer.writeAttribute(QStringLiteral("src"), QStringLiteral("EasyGPS"));
  waypt_disp_all(geo_waypt_pr);
  writer.writeEndElement();
}

ff_vecs_t geo_vecs = {
  ff_type_file,
  { (ff_cap)(ff_cap_read | ff_cap_write), ff_cap_none, ff_cap_none },
  geo_rd_init,
  geo_wr_init,
  geo_rd_deinit,
  geo_wr_deinit,
  geo_read,
  geo_write,
  nullptr,
  &geo_args,
  CET_CHARSET_UTF8, 0,	/* CET-REVIEW */
  NULL_POS_OPS,
  nullptr
};
