/*
    Read and write GeocachingPDB files.

    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net

    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"
#if PDBFMTS_ENABLED
#include "pdbfile.h"

#define MYNAME "GeocachingDB"
#define MYTYPE  0x44415441  	/* DATA */
#define MYCREATOR 0x42726174 	/* Brat */

#define MAXRECSZ 500 /* This is overkill as the records seem to be around 100
bytes a piece, but being conservative and dealing
with realloc issues just doesn't seem worth it. */

typedef enum {
RECTYPE_TEXT = 0,
RECTYPE_DATE = 2
} gcdb_rectype;

struct dbfld {
char	fldname[4];
pdb_16  fldtype;
pdb_16  fldlen;
};

struct dbrec {
pdb_16  nflds;
struct dbfld dbfld[1];
};

static pdbfile* file_in, *file_out;
static const char* out_fname;
static int ct;

static char* tbuf = NULL;
static char* tbufp = NULL;

static void
rd_init(const char* fname)
{
file_in = pdb_open(fname, MYNAME);
}

static void
rd_deinit(void)
{
pdb_close(file_in);
}

static void
wr_init(const char* fname)
{
file_out = pdb_create(fname, MYNAME);
out_fname = fname;
ct = 0;
}

static void
wr_deinit(void)
{
pdb_close(file_out);
if (tbuf) {
xfree(tbuf);
}
}

static void
data_read(void)
{
pdbrec_t* pdb_rec;

if ((file_in->creator != MYCREATOR) || (file_in->type != MYTYPE)) {
fatal(MYNAME ": Not a GeocachingDB file.\n");
}

for (pdb_rec = file_in->rec_list; pdb_rec; pdb_rec=pdb_rec->next) {
waypoint* wpt = waypt_new();
struct dbrec* rec = (struct dbrec*) pdb_rec->data;
int nflds;
int length;
int type;
int i;
char* recdata;
int lat_dir = 0;
int lat_deg = 0;
float lat_min = 0.0;
int lon_dir = 0;
int lon_deg = 0;
float lon_min = 0.0;

nflds = be_read16(&rec->nflds);
recdata = (char*) &rec->dbfld[nflds];

for (i = 0; i < nflds; i++) {
length = (unsigned short) be_read16(&rec->dbfld[i].fldlen);
type = be_read16(&rec->dbfld[i].fldtype);

switch (type) {
case RECTYPE_TEXT: /* Text */
if (!strncmp("gcid", rec->dbfld[i].fldname,4)) {
wpt->shortname = xstrdup(recdata);
} else if (!strncmp("gcna", rec->dbfld[i].fldname,4)) {
wpt->description = xstrdup(recdata);
} else if (!strncmp("lat0", rec->dbfld[i].fldname,4)) {
lat_dir = *recdata == 'N' ? 1 : -1;
} else if (!strncmp("lat1", rec->dbfld[i].fldname,4)) {
lat_deg = atoi(recdata);
} else if (!strncmp("lat2", rec->dbfld[i].fldname,4)) {
lat_min = atof(recdata);
}
if (!strncmp("lon0", rec->dbfld[i].fldname,4)) {
lon_dir = *recdata == 'E' ? 1 : -1;
} else if (!strncmp("lon1", rec->dbfld[i].fldname,4)) {
lon_deg = atoi(recdata);
} else if (!strncmp("lon2", rec->dbfld[i].fldname,4)) {
lon_min = atof(recdata);
} else if (!strncmp("take", rec->dbfld[i].fldname,4)) {
wpt->notes = xstrappend(wpt->notes,  " Took ");
wpt->notes = xstrappend(wpt->notes,  recdata);
} else if (!strncmp("left", rec->dbfld[i].fldname,4)) {
wpt->notes = xstrappend(wpt->notes, " Left ");
wpt->notes = xstrappend(wpt->notes,  recdata);
} else if (!strncmp("diff", rec->dbfld[i].fldname,4)) {
waypt_alloc_gc_data(wpt)->diff = 10 * atof(recdata);
} else if (!strncmp("terr", rec->dbfld[i].fldname,4)) {
waypt_alloc_gc_data(wpt)->terr = 10 * atof(recdata);
}
break;
#if 0
/* This really is the date of the find,
* not the cache creation date.
*/
case RECTYPE_DATE:
if (!strncmp("date", rec->dbfld[i].fldname,4)) {
time_t tm;
tm = be_read32(recdata) * 24 * 3600;
tm -= EPOCH_1904;
wpt->creation_time = tm;
warning("date %d\n", tm);
}
break;
#endif
}
recdata += (length + 1) & (~1);
}
wpt->latitude = lat_dir * (lat_deg + lat_min/60);
wpt->longitude = lon_dir * (lon_deg + lon_min/60);
waypt_add(wpt);
}
}


static int
gcdb_add_to_rec(struct dbrec* rec, const char* fldname, gcdb_rectype rectype, const void* data)
{
int length;
static int rec_cnt;

if (!tbuf) {
tbuf = (char*) xcalloc(MAXRECSZ, 1);
tbufp = tbuf;
}

if (fldname == NULL) {
length = tbufp - tbuf;
be_write16(&rec->nflds, rec_cnt);
memcpy(&rec->dbfld[rec_cnt],tbuf, length);
tbufp = tbuf;
length += 4 + sizeof(struct dbfld) * rec_cnt;
rec_cnt = 0;
return length;
}

be_write16(&rec->dbfld[rec_cnt].fldtype,rectype);
strncpy(rec->dbfld[rec_cnt].fldname, fldname, 4);

switch (rectype) {
case RECTYPE_TEXT:
length = 1 + strlen((const char*)data);
be_write16(&rec->dbfld[rec_cnt].fldlen, length);
strcpy(tbufp, (const char*)data);
tbufp += (length + 1) & (~1);
break;
case RECTYPE_DATE:
length = 4;
be_write16(&rec->dbfld[rec_cnt].fldlen, length);
be_write32(tbufp, ((time_t)data - EPOCH_1904)/ (3600 * 24));
tbufp += length;
break;
default:
abort();
}
rec_cnt++;

return length;
}

static void
gcdb_write_wpt(const waypoint* wpt)
{
struct dbrec* rec;
int reclen;
char tbuf[100];

/*
* We don't really know how many fields we'll have or how long
* they'll be so we'll just lazily create a huge place to hold them.
*/
rec = (struct dbrec*) xcalloc(sizeof(*rec) + 500, 1);

gcdb_add_to_rec(rec, "gcna", RECTYPE_TEXT, wpt->description);
gcdb_add_to_rec(rec, "gcid", RECTYPE_TEXT, wpt->shortname);

gcdb_add_to_rec(rec, "lat0", RECTYPE_TEXT,
wpt->latitude < 0 ? "S" : "N");

sprintf(tbuf, "%d", (int) wpt->latitude);
gcdb_add_to_rec(rec, "lat1", RECTYPE_TEXT, tbuf);

sprintf(tbuf, "%f", 60 * (wpt->latitude -
(int) wpt->latitude));
gcdb_add_to_rec(rec, "lat2", RECTYPE_TEXT, tbuf);


gcdb_add_to_rec(rec, "lon0", RECTYPE_TEXT,
wpt->longitude < 0 ? "W" : "E");

sprintf(tbuf, "%d", (int) wpt->longitude);
gcdb_add_to_rec(rec, "lon1", RECTYPE_TEXT, tbuf);

sprintf(tbuf, "%f", 60 * (wpt->longitude -
(int) wpt->longitude));
gcdb_add_to_rec(rec, "lon2", RECTYPE_TEXT, tbuf);

if (wpt->gc_data->diff) {
sprintf(tbuf, "%f", wpt->gc_data->diff / 10.0);
gcdb_add_to_rec(rec, "diff", RECTYPE_TEXT, tbuf);
}

if (wpt->gc_data->terr) {
sprintf(tbuf, "%f", wpt->gc_data->terr / 10.0);
gcdb_add_to_rec(rec, "terr", RECTYPE_TEXT, tbuf);
}

#if 0
/* This really is the date of the find,
* not the cache creation date.
*/
if (wpt->creation_time) {
gcdb_add_to_rec(rec, "date", RECTYPE_DATE, (void*) wpt->creation_time);
}
#endif

/*
* We're done.  Build the record.
*/
reclen = gcdb_add_to_rec(rec, NULL, (gcdb_rectype)0, NULL);

pdb_write_rec(file_out, 0, 2, ct++, rec, reclen);
xfree(rec);
}

static void
data_write(void)
{
  strncpy(file_out->name, out_fname, PDB_DBNAMELEN);
  strncpy(file_out->name, "GeocachingDB", PDB_DBNAMELEN);
  file_out->name[PDB_DBNAMELEN-1] = 0;
  file_out->attr = PDB_FLAG_BACKUP;
  file_out->ctime = file_out->mtime = current_time() + 2082844800U;
  file_out->type = MYTYPE;  /* CWpt */
  file_out->creator = MYCREATOR; /* cGPS */
  file_out->version = 1;

  waypt_disp_all(gcdb_write_wpt);
}


ff_vecs_t gcdb_vecs = {
  ff_type_file,
  FF_CAP_RW_WPT,
  rd_init,
  wr_init,
  rd_deinit,
  wr_deinit,
  data_read,
  data_write,
  NULL,
  NULL,
  CET_CHARSET_ASCII, 0	/* CET-REVIEW */
};
#endif
