/***************************************************************************
 *            map.c
 *
 *  Thu Jul 19 19:00:10 2007
 *  Copyright  2007  Steffen Pohle
 *  steffen@gulpe.de
 ****************************************************************************/

/*
 *  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-1307, USA.
 */

#include <math.h>
#include "map.h"
#include "memoryleak.h"
#include "system.h"


struct map_hash **mhash = NULL;
int mhash_max = 0;
struct map_lsstat *mlsstat = NULL;
int map_busy = 0;
extern int map_search_fd;


/******************************************************
 * Initialize the map data, allocate memory and
 * increase the memory.
 */
void map_init () {
	int i, j;
	char fn[LEN_FILENAME];

	d_printf ("map_init");
	d_printf ("  MAP_HASH_DEGFACTOR:%f", MAP_HASH_DEGFACTOR);
	d_printf ("  MAP_OSMWEB_DELTA:%f", MAP_OSMWEB_DELTA);
	d_printf ("  MAP_LSHASHFACTOR:%d", MAP_LSHASHFACTOR);
	d_printf ("  MAP_LSSTAT_MAX:%d", MAP_LSSTAT_MAX);
	d_printf ("  Directory: %s", cfg.mappath);

	strncpy (fn, cfg.mappath, LEN_FILENAME);
	if (fn[(i=strlen(fn))-1] == DIR_SEP) fn[i] = '\0';
	else {
		d_printf ("  append '%c' to mappath.", DIR_SEP);
		cfg.mappath[strlen(cfg.mappath)+1] = '\0';
		cfg.mappath[strlen(cfg.mappath)] = DIR_SEP;
	}
	if (!dir_exist (fn)) {
		d_printf ("***************** dir does not exist **************************");
	}

	if (mlsstat == NULL)
		mlsstat = (struct map_lsstat*) ml_malloc (MAP_LSSTAT_MAX*sizeof (struct map_lsstat));
	for (i = 0; i < MAP_LSSTAT_MAX; i++) {
		mlsstat[i].fname[0] = '\0';
		mlsstat[i].fnametmp[0] = '\0';
		for (j = 0; j < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; j++) {
			mlsstat[i].r_lshpos[j].pos = 0;
			mlsstat[i].r_lshpos[j].size = 0;
			mlsstat[i].w_lshpos[j].pos = 0;
			mlsstat[i].w_lshpos[j].size = 0;
			if (j < MAP_LSFREECNT) {
				mlsstat[i].w_free[j].pos = 0;
				mlsstat[i].w_free[j].size = 0;
			}
		}
		mlsstat[i].w_fd = 0;
		mlsstat[i].r_fd = 0;
	}

	mhash_max = MAP_HASH_MAX;
	mhash = (struct map_hash**) ml_malloc (sizeof (void*) * (mhash_max));
	for (i = 0; i < mhash_max; i++) 
		mhash[i] = NULL;
};


void map_clear () {
	int i;

	d_printf ("map_clean");
	if (mhash != NULL) {
		d_printf ("map_clean mhash reset");
		for (i = 0; i < mhash_max; i++)
			if (mhash[i]) {
				ml_free (mhash[i]);
				mhash[i] = NULL;
			}
		ml_free (mhash);
		mhash = NULL;
		mhash_max = 0;
	}
	if (mlsstat) {
		d_printf ("map_clean mlsstat reset");
		for (i = 0; i < MAP_LSSTAT_MAX; i++) {
			if (mlsstat[i].w_fd != 0 || mlsstat[i].r_fd != 0)
				map_ls_statclose (&mlsstat[i]);
		}
		ml_free (mlsstat);
		mlsstat = NULL;
	}
	if (map_search_fd) {
		close (map_search_fd);
		map_search_fd = 0;
	}

	map_init ();
	d_printf ("map_clean left");
};


/*********************************************************************************
 *
 * convert input text to geo-data needed for the programm
 * RETURN 1 on success, 0 on error
 */
int map_text2geo (char *text, float *dest) {
	int i;
	int type = 0;	// 0 = grad, 1=min, 2=sec
	char ctab[3][32];
	int itab[3] = { 0, 0, 0};
	float ftab[3] = {0.0, 0.0, 0.0 };

	memset (ctab, 0x0, sizeof (ctab));
	if (text == NULL || dest == NULL) return 0;
	for (i = 0; itab[type] < 32 && i < 63; i++) {
		if (text[i] == '\0') break;
		else if (text[i] == '"') type = 1;
		else if (text[i] == '\'') type = 2;
		else 
			ctab[type][itab[type]++] = text[i];
	}

	for (i = 0; i < 3; i++)
		ftab[i] = atof(ctab[i]);

	*dest = ftab[0] + ftab[1]/60.0 + ftab[2]/3600.0;
	return 1;
};


/*********************************************************************************
 *
 * convert between stretched km/map and geographial coordinates
 *
 */
float map_lon2km (float lon, float lat) {
	return (lon * cosf(M_PI * lat/180.0) * M_PI * MAP_EARTHRADIUS/180.0);
};

float map_lat2km (float lat) {
	return (lat * M_PI * MAP_EARTHRADIUS/180.0);
};

float map_km2lon (float km, float lat) {
	return (180.0 * km / (cosf(M_PI * lat/180.0) * M_PI * MAP_EARTHRADIUS));
};

float map_km2lat (float km) {
	return (180.0 * km / (M_PI * MAP_EARTHRADIUS));
};



/*********************************************************************************
 *
 * convert between integer lon and lat and the float lon lat values
 * needed for handling with the hash tables
 *
 */
int map_geo2igeo (float f) {
	if (f < -720.0 || f > 720.0) {
		d_printf ("map_geo2igeo f out of range. f:%f", f);
		d_print_backtrace ();
		f = 0.0;
	}
	
	f = f + 180.0;

	while (f < 0.0) f = f + 360.0;
	while (f >= 360.0) f = f - 360.0;

	return ((int)(f*MAP_HASH_DEGFACTOR));
};


float map_igeo2geo (int i) {
	return ((((float)i)/MAP_HASH_DEGFACTOR)-180.0);
};


struct map_hashpos map_pos2ipos (struct map_pos pos) {
	struct map_hashpos hpos;

	hpos.ilon = map_geo2igeo(pos.lon);
	hpos.ilat = map_geo2igeo(pos.lat);
	
	return hpos; 
};


float map_getdistance (struct map_pos p1, struct map_pos p2) {
	fPoint v;

	v.x = map_lon2km (p1.lon - p2.lon, p2.lat);
	v.y = map_lat2km (p1.lat - p2.lat);

	return sqrtf (v.x*v.x + v.y*v.y);
};


/******************************************************************************
 *
 * Debugging Functions..
 *
 */
void map_way_print (struct map_way *mw) {
	int i;

	d_printf ("");
	d_printf ("DEBUG WAY: %lld:%d name:%s", mw->id, mw->subid, mw->name);
	d_printf ("| Points: %d @ %p   Nodes:%d @ %p", mw->p_cnt, mw->p, mw->n_cnt, mw->n);
	for (i = 0; i < mw->p_cnt; i++)
		d_printf ("|   %3d: %g , %g", i, mw->p[i].lon, mw->p[i].lat);
	for (i = 0; i < mw->n_cnt; i++) 
		d_printf ("|   %3d to %lld:%d pnr:%d    %g , %g", i, mw->n[i].d_id, mw->n[i].d_subid, mw->n[i].d_pnr, mw->n[i].d.lon, mw->n[i].d.lat);
	
};


/*
 * refresh the map by downloading from internet 
 */
void map_refresh (float lon, float lat) {
	char fn[LEN_FILENAME];
	char rfn[LEN_FILENAME];
	
	map_ls_get_filename_only (fn, LEN_FILENAME, map_geo2igeo (lon), map_geo2igeo (lat));
	map_ls_get_filename (rfn, LEN_FILENAME, map_geo2igeo (lon), map_geo2igeo (lat));
#if defined(SPOSMROUTE)
	map_webload (fn, rfn, NULL);
	map_clear ();
	map_init ();
	draw_redrawmap ();
#endif
};


/*
 * load all needed blocks from the server
 * start, end rectangle to download
 * delta, how many km addition to the left, right, up and down.
 */
void map_refreshblock (struct map_pos start, struct map_pos end, float delta) {
	char fn[LEN_FILENAME];
	char rfn[LEN_FILENAME];
	int ilon, ilon_s, ilon_e, ilat, ilat_s, ilat_e;
	fPoint p1, p2;
	char text[255];

	/* check that we download the data from top/left to bottom/right */
	p1.x = map_lon2km (start.lon, start.lat);
	p1.y = map_lat2km (start.lat);
	p2.x = map_lon2km (end.lon, end.lat);
	p2.y = map_lat2km (end.lat);
	if (p1.x > p2.x) {
		memswap (&p1.x, &p2.x, sizeof (float));
	}
	if (p1.y < p2.y) {
		memswap (&p1.y, &p2.y, sizeof (float));
	}
	p1.x -= delta;
	p1.y += delta;
	p2.x += delta;
	p2.y -= delta;
	
	d_printf (" km %g,%g --> %g,%g", p1.x, p1.y, p2.x, p2.y);

	/* latitude loop */
	ilat_s = map_geo2igeo (map_km2lat(p2.y)) / MAP_LSHASHFACTOR;
	ilat_e = map_geo2igeo (map_km2lat(p1.y)) / MAP_LSHASHFACTOR;
	for (ilat = ilat_s; ilat <= ilat_e; ilat++) {

		/* longitude loop */
		ilon_s = map_geo2igeo (map_km2lon (p1.x, map_igeo2geo (ilat * MAP_LSHASHFACTOR))) / MAP_LSHASHFACTOR;
		ilon_e = map_geo2igeo (map_km2lon (p2.x, map_igeo2geo (ilat * MAP_LSHASHFACTOR))) / MAP_LSHASHFACTOR;
		for (ilon = ilon_s; ilon <= ilon_e; ilon++) {

			/* download data */
			map_ls_get_filename_only (fn, LEN_FILENAME, MAP_LSHASHFACTOR*ilon, MAP_LSHASHFACTOR*ilat);
			map_ls_get_filename (rfn, LEN_FILENAME, MAP_LSHASHFACTOR*ilon, MAP_LSHASHFACTOR*ilat);
			snprintf (text, 255, _("%d,%d  "), ilat, ilon);
#if defined(SPOSMROUTE)
			map_webload (fn, rfn, text);
#endif
		}
	}

	map_clear ();
	map_init ();
#if defined(SPOSMROUTE)
	draw_redrawmap ();
#endif
};



/*
 * Display Map Informations
 */
void map_debug () {
	int i;
	d_printf ("**** Mapinformation");
	d_printf ("Hash Information:");
	for (i = 0; i < mhash_max; i++) if (mhash[i]) {
		d_printf ("  mhash[%d] : prt:%p", i, mhash[i]);
	}
};
