/***************************************************************************
 *            map_loadsave.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.
 */


#ifdef HAVE_LOCALE_H
  #include <locale.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>

#include "map.h"
#include "system.h"
#include "memoryleak.h"
#include "osmroute.h"

#define MAP_BITS_SUBID 8

char *lsrw_data = NULL;
int lsrw_data_size = 0;
char *lshash_data = NULL;
int lshash_data_size = 0;


/*******************************************************************
 * create filename out of the ilon and ilat field.
 */
void map_ls_get_filename_only (char *fn, int len, int ilon, int ilat) {
	int flon, flat;

	flon = ilon/MAP_LSHASHFACTOR;
	flat = ilat/MAP_LSHASHFACTOR;
	
#ifdef HAVE_LOCALE_H
	setlocale(LC_ALL, "C");
	snprintf (fn, len, "mlon%dlat%d.mapdata", flon, flat);
	setlocale(LC_ALL, NULL);
#else
	snprintf (fn, len, "mlon%dlat%d.mapdata", flon, flat);
#endif
};

void map_ls_get_filename (char *fn, int len, int ilon, int ilat) {
	int flon, flat;

	flon = ilon/MAP_LSHASHFACTOR;
	flat = ilat/MAP_LSHASHFACTOR;
	
#ifdef HAVE_LOCALE_H
	setlocale(LC_ALL, "C");
	snprintf (fn, len, "%smlon%dlat%d.mapdata", cfg.mappath, flon, flat);
	setlocale(LC_ALL, NULL);
#else
	snprintf (fn, len, "%smlon%dlat%d.mapdata", cfg.mappath, flon, flat);
#endif
};


/******************************************************************
 * fine the stats inside the hash block, if not just load the file
 * into the memory.
 */
struct map_lsstat *map_ls_statget (char *fname) {
	int i, j;
	struct map_lsstat *mlssptr = NULL;
	struct map_lsstat mlss;

	/* check inside the mlsstat block */
	for (i = 0; i < MAP_LSSTAT_MAX; i++) {
		if (strncmp (fname, mlsstat[i].fname, LEN_FILENAME) == 0) {
			/* found in list, push it to the first entry, free whats left */
			if (i > 0) {
				mlss = mlsstat[i];
				for (j = i; j > 0; j--)
					mlsstat[j] = mlsstat[j-1];
				mlsstat[0] = mlss;
			}
			mlssptr = mlsstat;
			break;
		}
	}

	/* if not, load file from disk */
	if (mlssptr == NULL) {
		mlssptr = map_ls_statopen (fname);
		/* free last element in mlsstat */
		if (mlsstat[MAP_LSSTAT_MAX-1].fname[0] != '\0')
			map_ls_statclose (&mlsstat[MAP_LSSTAT_MAX-1]);

		/* push to the first entry */
		for (j = MAP_LSSTAT_MAX-1; j > 0; j--)
			mlsstat[j] = mlsstat[j-1];
		mlsstat[0] = *mlssptr;
		mlssptr = mlsstat;
	}

	return mlssptr;
};


/******************************************************************
 * check if we have enought free data in rw_data buffer
 */
void map_ls_rwdatacheck (int size) {
	char *old;

	if (lsrw_data == NULL) {
		lsrw_data_size = size;
		lsrw_data = ml_malloc (lsrw_data_size);
		ml_addinfo (lsrw_data, "lsrw_data");
	}
	else if (lsrw_data_size < size) {
		old = lsrw_data;
		ml_addinfo (old, "old lsrw_data");
		
		lsrw_data = ml_malloc (size);
		ml_addinfo (old, "lsrw_data new");
		if (old) memcpy (lsrw_data, old, lsrw_data_size);
		lsrw_data_size = size;
		ml_free (old);
	}
	else if (size == 0) {
		ml_free (lsrw_data);
		lsrw_data = NULL;
		lsrw_data_size = 0;
	}
};


void map_ls_rwdatacheckpos (int size, char **oldpos) {
	int i;

	i = *oldpos-lsrw_data;
	map_ls_rwdatacheck (size);
	*oldpos = lsrw_data+i;
};


void map_ls_hashdatacheck (int size) {
	char *old;

	if (lshash_data == NULL) {
		lshash_data_size = size;
		lshash_data = ml_malloc (lshash_data_size);
		ml_addinfo (lshash_data, "lshash_data");
	}
	else if (lshash_data_size < size) {
		old = lshash_data;
		ml_addinfo (old, "old lshash_data");

		lshash_data = ml_malloc (size);
		ml_addinfo (lshash_data, "lshash_data new");
		if (old) memcpy (lshash_data, old, lshash_data_size);
		lshash_data_size = size;
		ml_free (old);
	}
};


void map_ls_hashdatacheckpos (int size, char **oldpos) {
	int i;

	i = *oldpos-lshash_data;
	map_ls_hashdatacheck (size);
	*oldpos = lshash_data+i;
};


/*********************************************************************
 * try to open a map file, load imortant data into the mlss structure
 * and return it
 */
struct map_lsstat *map_ls_statopen (char *fname) {
	static struct map_lsstat mlss;
	int i;
	char *pos;

	map_ls_rwdatacheck (12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
	
	/* clear mlss */
	strncpy (mlss.fname, fname, LEN_FILENAME);
	mlss.fnametmp[0] = '\0';
	for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
		mlss.r_lshpos[i].pos = 0;
		mlss.r_lshpos[i].size = 0;
		mlss.w_lshpos[i].pos = 0;
		mlss.w_lshpos[i].size = 0;
		if (i < MAP_LSFREECNT) {
			mlss.w_free[i].pos = 0;
			mlss.w_free[i].size = 0;
		}
	}
	mlss.r_fd = mlss.w_fd = 0;
	mlss.changed = 0;

	if (file_exist (mlss.fname) == 0) {
		d_printf ("map_ls_statopen file '%s' not found.", mlss.fname);
		mlss.r_fd = 0;
		return &mlss;
	}

#if defined(__MINGW32CE__) || defined(_WIN32_WCE) || defined(__MINGW32__)
	mlss.r_fd = open (mlss.fname, O_RDWR | _O_BINARY);
	if (mlss.r_fd == -1) {
		int error = GetLastError();
		
		d_printf ("map_ls_statopen open file '%s'.  ERROR (GetLastError): %d", mlss.fname, error);
		mlss.r_fd = 0;
		return &mlss;
	}
#else
	mlss.r_fd = open (mlss.fname, O_RDWR);
	if (mlss.r_fd == -1) {
		d_printf ("map_ls_statopen open file '%s'.  ERROR (errno): %d:%s", mlss.fname, errno, strerror(errno));
		mlss.r_fd = 0;
		return &mlss;
	}
#endif
	// else d_printf ("map_ls_statopen open file '%s'.  OK fd:%d", mlss.fname, mlss.r_fd);

	read (mlss.r_fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
	pos = lsrw_data;
	for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
		pos = mempop (pos, &mlss.r_lshpos[i].pos, 8);
		pos = mempop (pos, &mlss.r_lshpos[i].size, 4);
	}

	for (i = 0; i < MAP_LSFREECNT; i++) {
		pos = mempop (pos, &mlss.w_free[i].pos, 8);
		pos = mempop (pos, &mlss.w_free[i].size, 4);
	}

	return &mlss;
};


/*********************************************************************
 * close the read and write file but save first the w_free, r_lshpos 
 * and the w_lshpos arrays
 */
void map_ls_statclose (struct map_lsstat *lsstat) {
	char *pos;
	int i;

	// d_printf ("map_ls_statclose called with: %p changed: %d r_fd: %d w_fd: %d", lsstat, lsstat->changed, lsstat->r_fd, lsstat->w_fd);
	if (lsstat->r_fd) {
		if (lsstat->changed) {
			map_ls_rwdatacheck (12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
			/* data changed so need to save, all filenames should be right
			 * so we only take care about r_fd and w_fd */
			if (lsstat->w_fd == 0) {
				pos = lsrw_data;
				for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
					pos = mempush (pos, &lsstat->r_lshpos[i].pos, 8);
					pos = mempush (pos, &lsstat->r_lshpos[i].size, 4);
				}
				for (i = 0; i < MAP_LSFREECNT; i++) {
					pos = mempush (pos, &lsstat->w_free[i].pos, 8);
					pos = mempush (pos, &lsstat->w_free[i].size, 4);
				}
				
				if (lseek (lsstat->r_fd, 0, SEEK_SET)  < 0) 
					d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
				write (lsstat->r_fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
			}
			else {
				/* need to copy all unchanged data into the new file */
				for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
					if (lsstat->w_lshpos[i].pos == 0 && lsstat->r_lshpos[i].pos != 0) {
						/* read data */
						map_ls_rwdatacheck (lsstat->r_lshpos[i].size);
						if (lseek (lsstat->r_fd, lsstat->r_lshpos[i].pos, SEEK_SET) < 0) 
							d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
						read (lsstat->r_fd, lsrw_data, lsstat->r_lshpos[i].size);
						
						/* write data */
						if ((lsstat->w_lshpos[i].pos = lseek (lsstat->w_fd, 0, SEEK_END)) < 0)
							d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
						
						lsstat->w_lshpos[i].size = lsstat->r_lshpos[i].size;
						write (lsstat->w_fd, lsrw_data, lsstat->w_lshpos[i].size);
					}
				}

				pos = lsrw_data;
				for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
					pos = mempush (pos, &lsstat->w_lshpos[i].pos, 8);
					pos = mempush (pos, &lsstat->w_lshpos[i].size, 4);
				}
				for (i = 0; i < MAP_LSFREECNT; i++) {
					pos = mempush (pos, &lsstat->w_free[i].pos, 8);
					pos = mempush (pos, &lsstat->w_free[i].size, 4);
				}
				
				if (lseek (lsstat->w_fd, 0, SEEK_SET) < 0) 
					d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
				write (lsstat->w_fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
			}
		}

		close (lsstat->r_fd);
		// d_printf ("map_ls_statclose close file r_fd:%d", lsstat->r_fd);
		if (lsstat->w_fd) {
			close (lsstat->w_fd);
			// d_printf ("map_ls_statclose close file w_fd:%d", lsstat->w_fd);
			unlink (lsstat->fnametmp);
		}
	}
	
	lsstat->fname[0] = '\0';
	lsstat->fnametmp[0] = '\0';
	lsstat->r_fd = 0;
	lsstat->w_fd = 0;
};


/*********************************************************
 * load the data, do not convert them yet..  but check
 * if we need to load the data from r_fd or w_fd
 */
char *map_ls_loaddata (struct map_lsstat *lsstat, int *size, int ilon, int ilat) {
	int flon = ilon - (ilon/MAP_LSHASHFACTOR)*MAP_LSHASHFACTOR;
	int flat = ilat - (ilat/MAP_LSHASHFACTOR)*MAP_LSHASHFACTOR;
	int nr = flon * MAP_LSHASHFACTOR + flat;
	int fd;
	uint64_t pos;
	
	if (nr < 0 || nr >= MAP_LSHASHFACTOR*MAP_LSHASHFACTOR) {
		d_printf ("WARN: map_ls_loaddata nr[%d] > MAP_LSHASHFACTOR^2[%d] ipos:%d,%d", nr, MAP_LSHASHFACTOR*MAP_LSHASHFACTOR, ilon, ilat);
		return NULL;
	}

	if (lsstat->r_fd == 0) return NULL;

	if (lsstat->w_fd > 0 && lsstat->w_lshpos[nr].pos != 0) {
		/* w_fd has data? */
		fd = lsstat->w_fd;
		pos = lsstat->w_lshpos[nr].pos;
		*size = lsstat->w_lshpos[nr].size;
	}
	else if (lsstat->r_lshpos[nr].pos != 0) {
		/* r_fd has data ? */
		fd = lsstat->r_fd;
		pos = lsstat->r_lshpos[nr].pos;
		*size = lsstat->r_lshpos[nr].size;
	}
	else {
		return NULL;
	}

	
	/* read the data */
	map_ls_hashdatacheck (*size);
	if (lseek (fd, pos, SEEK_SET) < 0) 
		d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
	if (read (fd, lshash_data, *size) != *size) {
		d_printf ("ERROR: map_ls_loaddata: ERROR hashdata could not been loaded.");
		d_printf ("       lsstat fname:%s fnametmp:%s r_fd:%d, w_fd:%d", lsstat->fname, lsstat->fnametmp, lsstat->r_fd, lsstat->w_fd);
		d_printf ("       fd:%d pos:%lld size:%d", fd, pos, *size);
		errorexit (1);
	}

	return lshash_data;	
};


void map_ls_savedata (struct map_lsstat *lsstat, char *data, int size, int ilon, int ilat) {
	int flon = ilon - (ilon/MAP_LSHASHFACTOR)*MAP_LSHASHFACTOR;
	int flat = ilat - (ilat/MAP_LSHASHFACTOR)*MAP_LSHASHFACTOR;
	int nr = flon * MAP_LSHASHFACTOR + flat;
	int fd, i;
	uint64_t pos;
	struct map_lshmap olddatapos = {0, 0};

	if (nr < 0 || nr >= MAP_LSHASHFACTOR*MAP_LSHASHFACTOR) {
		d_printf ("WARN: map_ls_savedata nr[%d] > MAP_LSHASHFACTOR^2[%d] ipos:%d,%d", nr, MAP_LSHASHFACTOR*MAP_LSHASHFACTOR, ilon, ilat);
		return;
	}

	if (lsstat->r_fd == 0) {
		/* open new file and write empty data in it*/
		map_ls_rwdatacheck (12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
		memset (lsrw_data, 0x0, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
		
#if defined(__MINGW32CE__) || defined(_WIN32_WCE) || defined(__MINGW32__)
		fd = open (lsstat->fname, O_CREAT|O_RDWR|O_TRUNC|_O_BINARY, 0644);
#else
		fd = open (lsstat->fname, O_CREAT|O_RDWR|O_TRUNC, 0644);
#endif
		if (fd == -1) {
			/* Error... so free all unneeded memory */
			d_printf ("ERROR: map_ls_savedata could not open file [%s]for writing.\n", lsstat->fname);
			errorexit (-1);
		}
		// else d_printf ("map_ls_savedata open file %s fd:%d", lsstat->fname, fd);
		write (fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
	}
	else if (lsstat->w_fd > 0) {
		/* new write file already opened?
		 * use new write file */
		fd = lsstat->w_fd;
		olddatapos = lsstat->w_lshpos[nr];
	}
	else if (lsstat->r_lshpos[nr].pos != 0) {
		/* defrag of readfile still below 50% use read file else,
		 * open new write file */
		fd = lsstat->r_fd;
		pos = lseek (lsstat->r_fd, 0, SEEK_END);
		if (pos < 0) d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
		else if (lsstat->w_free[0].size > pos || lsstat->w_free[0].pos >= (MAP_LSFREECNT-2)) {
			/*
			 * got over 25% lost already or maximum of free fields reached
			 */
			d_printf ("      maximum of free blocks reached. cnt:%lld size:%d bytes, file:%s", lsstat->w_free[0].pos, lsstat->w_free[0].size, lsstat->fname);
			close (lsstat->r_fd);
			d_printf ("map_ls_savedata close file fd: %d", lsstat->r_fd);
			snprintf (lsstat->fnametmp, LEN_FILENAME, "%s.tmp", lsstat->fname);
			if (rename (lsstat->fname, lsstat->fnametmp) != 0) {
				d_printf ("ERROR: map_ls_savedata rename(%s,%s) failed: %s", lsstat->fname, lsstat->fnametmp, strerror(errno));
				errorexit (1);
			}
			
			/* open new file and write empty data in it*/
			map_ls_rwdatacheck (12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
			memset (lsrw_data, 0x0, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
			lsstat->w_fd = open (lsstat->fname, O_CREAT|O_RDWR|O_TRUNC, 0644);
			if (lsstat->w_fd <= 0) {
				/* Error... so free all unneeded memory */
				d_printf ("ERROR: map_ls_savedata could not open file [%s]for writing: ", lsstat->fname, strerror(errno));
				errorexit (-1);
			}
			// else d_printf ("map_ls_savedata open file %s w_fd:%d", lsstat->fname, lsstat->w_fd);
			write (lsstat->w_fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));

			/* fill in new structure */
			for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
				lsstat->w_lshpos[i].pos = 0;
				lsstat->w_lshpos[i].size = 0;
				if (i < MAP_LSFREECNT) {
					lsstat->w_free[i].pos = 0;
					lsstat->w_free[i].size = 0;
				}
			}
			
			/* reopen old file for reading */
#if defined(__MINGW32CE__) || defined(_WIN32_WCE) || defined(__MINGW32__)
			lsstat->r_fd = open (lsstat->fnametmp, O_RDONLY | _O_BINARY);
#else
			lsstat->r_fd = open (lsstat->fnametmp, O_RDONLY);
#endif
			if (lsstat->r_fd <= 0) {
				/* Error... so free all unneeded memory */
				d_printf ("ERROR: map_ls_savedata could not open file [%s] for reading: ", lsstat->fnametmp, strerror(errno));
				errorexit (-1);
			}
			fd = lsstat->w_fd;
			olddatapos = lsstat->w_lshpos[nr];
		}
		else {
			olddatapos = lsstat->r_lshpos[nr];
		}
	}
	else {
		fd = lsstat->r_fd;
		olddatapos = lsstat->r_lshpos[nr];
	}

	/*
	 * write datablock, and mark
	 */
	if ((pos = lseek (fd, 0, SEEK_END)) < 0)
		d_printf ("%s:%d error with lseek.", __FILE__, __LINE__);
	if ((i = write (fd, data, size)) < size) {
		d_printf ("ERROR: map_ls_savedata written bytes are not as many as requested. i:%d size:%d error:%s", i, size, strerror (errno));
		errorexit (1);
	}

	/*
	 * mark data as free
	 */
	if (olddatapos.pos > 0) {
		lsstat->w_free[lsstat->w_free[0].pos+1] = olddatapos;
		lsstat->w_free[0].pos++;
		lsstat->w_free[0].size = lsstat->w_free[0].size +olddatapos.size;
	}

	/* 
	 * setup fd and r_fd and w_fd 
	 */
	if (lsstat->r_fd == 0) 
		lsstat->r_fd = fd;
	if (fd == lsstat->r_fd) {
		lsstat->w_fd = 0;
		lsstat->r_lshpos[nr].pos = pos;
		lsstat->r_lshpos[nr].size = size;
	}
	else {
		lsstat->w_fd = fd;
		lsstat->w_lshpos[nr].pos = pos;
		lsstat->w_lshpos[nr].size = size;
	}
	lsstat->changed = 1;

	/* if we have already alot unused data inside the 
	 * w_fd close it, so we can later on create a new file */
	if (lsstat->w_fd >0 && lsstat->w_free[0].pos >= MAP_LSFREECNT/2) map_ls_statclose (lsstat);
};


/************************************************************
 * convert hash to datablock and return also it's size
 */
char *map_ls_hash2data (struct map_hash *mh, int *size) {
	struct map_way *mw;
	struct map_area *ma;
	struct map_place *mp;
	struct map_poi *mpoi;
	struct map_lshentry lsh;
	int obj_cnt, j, cur_size;	
	char *woutpos = NULL;
	uint64_t ui64;		/* datatypes needed for convert */
	uint32_t ui32;
	uint16_t ui16;
	uint8_t ui8;

	/* 
	 * write ways 
	 */
	*size = MAP_LSSIZE_HASH;
	lsh.ways = 0;
	lsh.areas = 0;
	lsh.pois = 0;
	lsh.places = 0;
	lsh.ncnt = 0;
	lsh.pcnt = 0;
	lsh.ccnt = 0;

	map_ls_hashdatacheck (*size);
	woutpos = lshash_data+MAP_LSSIZE_HASH; /* the end of lshentry */

	mw = mh->ways;
	obj_cnt = 0;
	while (mw && obj_cnt < mh->wayscnt) {
		obj_cnt++;
		if (mw != NULL) {
			/* check if the allocated memory is enought */
			cur_size = 2;
			if (mw->name) cur_size += strlen (mw->name);
			if (mw->ref) cur_size += strlen (mw->ref);
			lsh.ccnt += cur_size;
			*size = *size + MAP_LSSIZE_WAY + mw->p_cnt * MAP_LSSIZE_POS + mw->n_cnt * MAP_LSSIZE_WAYNODE + cur_size;
			
			map_ls_hashdatacheckpos (*size, &woutpos);

			/* save the basic data */
			ui64 = mw->id;					/* id */
			ui64 = (ui64 << MAP_BITS_SUBID) | mw->subid;
			woutpos = mempush (woutpos, &ui64, 8);
			ui8 = mw->type;					/* type */
			woutpos = mempush (woutpos, &ui8, 1);
			ui8 = mw->flags;				/* flags */
			woutpos = mempush (woutpos, &ui8, 1);

											/* name */
			ui8 = 0x0;
			if (mw->name) woutpos = mempush (woutpos, mw->name, strlen (mw->name)+1);
			else woutpos = mempush (woutpos, &ui8, 1);
			
											/* ref */
			if (mw->ref) woutpos = mempush (woutpos, mw->ref, strlen (mw->ref)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			ui16 = mw->n_cnt;				/* n_cnt */
			woutpos = mempush (woutpos, &ui16, 2);

			ui16 = mw->p_cnt;				/* p_cnt */
			woutpos = mempush (woutpos, &ui16, 2);

			/* save the waynodes */
			for (j = 0; j < mw->n_cnt; j++) {
				woutpos = mempush (woutpos, &mw->n[j].d.lon, 4);
				woutpos = mempush (woutpos, &mw->n[j].d.lat, 4);
				ui64 = mw->n[j].d_id;
				woutpos = mempush (woutpos, &ui64, 8);
				ui16 = mw->n[j].d_subid;
				woutpos = mempush (woutpos, &ui16, 2);
				ui16 = mw->n[j].d_pnr;
				woutpos = mempush (woutpos, &ui16, 2);
				ui16 = mw->n[j].pnr;
				woutpos = mempush (woutpos, &ui16, 2);
				ui8 = mw->n[j].flags;
				woutpos = mempush (woutpos, &ui8, 1);
				lsh.ncnt++;
			}

			/* save the points */
			for (j = 0; j < mw->p_cnt; j++) {
				woutpos = mempush (woutpos, &mw->p[j].lon, 4);
				woutpos = mempush (woutpos, &mw->p[j].lat, 4);
				lsh.pcnt++;
			}

			/* write the way on disk and increase ways counter */
			lsh.ways++;	  // add the next way
		}

		mw = mw->next;
	}
	if (mw) 
		d_printf ("%s:%d map_save_hash all ways saved but mw != NULL (%p)", __FILE__, __LINE__, mw);
	/*
	 * write the areas
	 */
	ma = mh->areas;
	obj_cnt = 0;
	while (ma && obj_cnt < mh->areascnt) {
		obj_cnt++;
		if (ma != NULL) {
			/* check if the allocated memory is enought */
			*size = *size + MAP_LSSIZE_AREA + ma->p_cnt * MAP_LSSIZE_POS;
			map_ls_hashdatacheckpos (*size, &woutpos);
			
			/* save the basic data */
			ui64 = (ma->id << MAP_BITS_SUBID) | ma->subid;		/* id */
			woutpos = mempush (woutpos, &ui64, 8);
			ui8 = ma->type;					/* type */
			woutpos = mempush (woutpos, &ui8, 1);
			ui8 = ma->flags;				/* flags */
			woutpos = mempush (woutpos, &ui8, 1);
			ui16 = ma->p_cnt;				/* p_cnt */
			woutpos = mempush (woutpos, &ui16, 2);
			/* save the points */
			for (j = 0; j < ma->p_cnt; j++) {
				woutpos = mempush (woutpos, &ma->p[j].lon, 4);
				woutpos = mempush (woutpos, &ma->p[j].lat, 4);
				lsh.pcnt++;
			}
			/* write the way on disk and increase ways counter */
			lsh.areas++;	  // add the next way
		}
		ma = ma->next;
	}
	if (ma) 
		d_printf ("%s:%d map_save_hash all areas saved but ma != NULL (%p)", __FILE__, __LINE__, ma);

	/*
	 * save place data
	 */
	mp = mh->places;
	obj_cnt = 0;
	while (mp && obj_cnt < mh->placescnt) {
		obj_cnt++;
		if (mp != NULL) {
			/* check if the allocated memory is enought */
			cur_size = 5;
			if (mp->country) cur_size += strlen (mp->country);
			if (mp->state) cur_size += strlen (mp->state);
			if (mp->district) cur_size += strlen (mp->district);
			if (mp->name) cur_size += strlen (mp->name);
			if (mp->postalcode) cur_size += strlen (mp->postalcode);
			lsh.ccnt += cur_size;
			*size = *size + cur_size + MAP_LSSIZE_PLACE;
			map_ls_hashdatacheckpos (*size, &woutpos);
			
			/* save the basic data */
			woutpos = mempush (woutpos, &mp->lon, 4);
			woutpos = mempush (woutpos, &mp->lat, 4);

			ui64 = mp->id;					/* id */
			woutpos = mempush (woutpos, &ui64, 8);
			ui8 = mp->type;					/* type */
			woutpos = mempush (woutpos, &ui8, 1);
			ui32 = mp->population;			/* population */
			woutpos = mempush (woutpos, &ui32, 4);

			/* save the text */
			ui8 = 0x0;
			/* country */
			if (mp->country) woutpos = mempush (woutpos, mp->country, strlen (mp->country)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			/* state */
			if (mp->state) woutpos = mempush (woutpos, mp->state, strlen (mp->state)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			/* district */
			if (mp->district) woutpos = mempush (woutpos, mp->district, strlen (mp->district)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			/* name */
			if (mp->name) woutpos = mempush (woutpos, mp->name, strlen (mp->name)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			/* postalcode */
			if (mp->postalcode) woutpos = mempush (woutpos, mp->postalcode, strlen (mp->postalcode)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			lsh.places++;	  // add the next way
		}

		mp = mp->next;
	}
	if (mp) 
		d_printf ("%s:%d map_save_hash all places saved but mp != NULL (%p)", __FILE__, __LINE__, mp);

	/*
	 * save poi data
	 */
	mpoi = mh->pois;
	obj_cnt = 0;
	while (mpoi && obj_cnt < mh->poiscnt) {
		obj_cnt++;
		if (mpoi != NULL) {
			/* check if the allocated memory is enought */
			cur_size = 2;
			if (mpoi->name) cur_size += strlen (mpoi->name);
			if (mpoi->additional) cur_size += strlen (mpoi->additional);
				lsh.ccnt += cur_size;
			*size = *size + cur_size + MAP_LSSIZE_POI;
			map_ls_hashdatacheckpos (*size, &woutpos);
			
			/* save the basic data */
			woutpos = mempush (woutpos, &mpoi->lon, 4);
			woutpos = mempush (woutpos, &mpoi->lat, 4);
			ui64 = mpoi->id;					/* id */
			woutpos = mempush (woutpos, &ui64, 8);
			ui8 = mpoi->type;					/* type */
			woutpos = mempush (woutpos, &ui8, 1);
			/* save the text */
			ui8 = 0x0;

			/* name */
			if (mpoi->name) woutpos = mempush (woutpos, mpoi->name, strlen (mpoi->name)+1);
			else woutpos = mempush (woutpos, &ui8, 1);
			/* additional */
			if (mpoi->additional) woutpos = mempush (woutpos, mpoi->additional, strlen (mpoi->additional)+1);
			else woutpos = mempush (woutpos, &ui8, 1);

			lsh.pois++;	  // add the next poi
		}

		mpoi = mpoi->next;
	}
	if (mpoi) 
		d_printf ("%s:%d map_save_hash all pois saved but mpoi != NULL (%p)", __FILE__, __LINE__, mpoi);

	/* setup the lsh into the file */
	woutpos = lshash_data;
	ui32 = lsh.ways;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.areas;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.pois;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.places;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.ncnt;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.pcnt;
	woutpos = mempush (woutpos, &ui32, 4);
	ui32 = lsh.ccnt;
	woutpos = mempush (woutpos, &ui32, 4);

	return lshash_data;
};


/*******************************************************************
 * convert data into hash
 */
struct map_hash *map_ls_data2hash (char *data, int size) {
	struct map_lshentry lsh;
	struct map_hash *mh = NULL;
	struct map_way *way = NULL;
	struct map_area *area = NULL;
	struct map_place *place = NULL;
	struct map_poi *poi = NULL;
	char *inpos;
	int i, cnt;
	uint64_t ui64;		/* datatypes needed for convert */
	uint32_t ui32;
	uint16_t ui16;
	uint8_t ui8;

	inpos = data;
	inpos = mempop (inpos, &ui32, 4);
	lsh.ways = ui32;
	inpos = mempop (inpos, &ui32, 4);
	lsh.areas = ui32;
	inpos = mempop (inpos, &ui32, 4);
	lsh.pois = ui32;
	inpos = mempop (inpos, &ui32, 4);
	lsh.places = ui32;
	inpos = mempop (inpos, &ui32, 4);
	lsh.ncnt = ui32;
	inpos = mempop (inpos, &ui32, 4);
	lsh.pcnt = ui32;
	inpos = mempop (inpos, &ui32, 4);			
	lsh.ccnt = ui32;

	mh = map_hash_alloc (map_hash_calc_size (lsh.ways, lsh.areas, lsh.pois, lsh.places, lsh.pcnt, lsh.ncnt, lsh.ccnt));
	mh->wayscnt = lsh.ways;
	mh->areascnt = lsh.areas;
	mh->poiscnt = lsh.pois;
	mh->placescnt = lsh.places;

	if (lsh.ways > 0) {
		/* 
		 * prepare and read the way 
		 */
		way = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		mh->ways = way;
		mh->wayscnt = lsh.ways;

		/* read all ways */
		for (cnt = 0; cnt < lsh.ways; cnt++) {
			/* load basic data */
			inpos = mempop (inpos, &ui64, 8);
			way->subid = ui64 % (1<<MAP_BITS_SUBID);
			way->id = (ui64 >> MAP_BITS_SUBID);
			inpos = mempop (inpos, &ui8, 1);
			way->type = ui8;
			inpos = mempop (inpos, &ui8, 1);
			way->flags = ui8;
			inpos = memstrpop (inpos, way->name, &i);
			if (i >= MAP_W_NAMELEN) {
				static int _only_once_ = 1;
				if (_only_once_) d_printf ("%s:%d ERROR in %s: i(%d) > MAP_W_NAMELEN(%d). Name:'%s'", __FILE__, __LINE__, __FUNCTION__, i, MAP_W_NAMELEN, way->name);
				_only_once_ = 0;
				i = MAP_W_NAMELEN - 1;
			}
			way->name[i] = 0;
			inpos = memstrpop (inpos, way->ref, &i);
			if (i >= MAP_W_NAMELEN) {
				static int _only_once_ = 1;
				if (_only_once_) d_printf ("%s:%d ERROR in %s: i(%d) > MAP_W_NAMELEN(%d). Ref:'%s'", __FILE__, __LINE__, __FUNCTION__, i, MAP_W_NAMELEN, way->ref);
				_only_once_ = 0;
				i = MAP_W_NAMELEN - 1;
			}
			way->ref[i] = 0;
			
			inpos = mempop (inpos, &ui16, 2);
			way->n_cnt = ui16;
			inpos = mempop (inpos, &ui16, 2);
			way->p_cnt = ui16;

			/* save the waynodes */
			way->n = (struct map_waynode*)way->data;
			for (i = 0; i < way->n_cnt; i++) {
				inpos = mempop (inpos, &way->n[i].d.lon, 4);
				inpos = mempop (inpos, &way->n[i].d.lat, 4);
				inpos = mempop (inpos, &ui64, 8);
				way->n[i].d_id = ui64;
				inpos = mempop (inpos, &ui16, 2);
				way->n[i].d_subid = ui16;
				inpos = mempop (inpos, &ui16, 2);
				way->n[i].d_pnr = ui16;
				inpos = mempop (inpos, &ui16, 2);
				way->n[i].pnr = ui16;
				inpos = mempop (inpos, &ui8, 1);
				way->n[i].flags = ui8;
			}

			/* save the points */
			way->p = (struct map_pos*) (way->data + (way->n_cnt * sizeof (struct map_waynode)));
			for (i = 0; i < way->p_cnt; i++) {
				inpos = mempop (inpos, &way->p[i].lon, 4);
				inpos = mempop (inpos, &way->p[i].lat, 4);
			}
			
			mh->free = mh->free + map_way_getsize (way);
			if (cnt < (lsh.ways-1)) way->next = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
			else way->next = NULL;
			way = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		}
	}
	
	/*
	 * prepare to read the areas 
	 */
	if (lsh.areas > 0) {
		area = (struct map_area*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		mh->areascnt = lsh.areas;
		mh->areas = area;
		area->prev = NULL;

		/* read all area */
		for (cnt = 0; cnt < lsh.areas; cnt++) {			
			/* load and setup basic data */
			area->next = NULL;

			inpos = mempop (inpos, &ui64, 8);
			area->subid = ui64 % (1<<MAP_BITS_SUBID);
			area->id = ui64;
			inpos = mempop (inpos, &ui8, 1);
			area->type = ui8;
			inpos = mempop (inpos, &ui8, 1);
			area->flags = ui8;
			inpos = mempop (inpos, &ui16, 2);
			area->p_cnt = ui16;

			/* read the points */
			area->p = (struct map_pos*) area->data;
			for (i = 0; i < area->p_cnt; i++) {
				inpos = mempop (inpos, &area->p[i].lon, 4);
				inpos = mempop (inpos, &area->p[i].lat, 4);
			}
			
			mh->free = mh->free + map_area_getsize (area);

			/*
			 * check if we are still in order
			 */
			while (area->prev != NULL && area->type < area->prev->type) {
				struct map_area *parea = area->prev;
				
				/* nighbours pointers change */
				if (parea->prev == NULL) mh->areas = area;
				else parea->prev->next = area;
				if (area->next != NULL) area->next->prev = parea;

				/* swap area->prev with area */
				parea->next = area->next;
				area->prev = parea->prev;
				parea->prev = area;
				area->next = parea;

				/* check next */
				area = parea;
			}
			for (;area->next != NULL; area = area->next);
			
			if (cnt < (lsh.areas-1)) {
				area->next = (struct map_area*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
				area->next->prev = area;
				area = area->next;
			}
			else area->next = NULL;
		}
	}


	/*
	 * prepare to read places
	 */
	if (lsh.places > 0) {
		place = (struct map_place*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		mh->placescnt = lsh.places;
		mh->places = place;

		/* read all area */
		for (cnt = 0; (cnt < lsh.places); cnt++) {			
			/* load basic data */		
			inpos = mempop (inpos, &place->lon, 4);
			inpos = mempop (inpos, &place->lat, 4);
			inpos = mempop (inpos, &ui64, 8);
			place->id = ui64;
			inpos = mempop (inpos, &ui8, 1);
			place->type = ui8;
			inpos = mempop (inpos, &ui32, 4);
			place->population = ui32;

			/* read the text data */
			place->country = place->data;
			inpos = memstrpop (inpos, place->country, &i);

			place->state = place->country+i;
			inpos = memstrpop (inpos, place->state, &i);

			place->district = place->state+i;
			inpos = memstrpop (inpos, place->district, &i);

			place->name = place->district+i;
			inpos = memstrpop (inpos, place->name, &i);

			place->postalcode = place->name+i;
			inpos = memstrpop (inpos, place->postalcode, &i);

			mh->free = mh->free + map_place_getsize (place);
			if (cnt < (lsh.places-1)) place->next = (struct map_place*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
			else place->next = NULL;
			place = (struct map_place*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		}
	}

	/*
	 * prepare to read pois
	 */
	if (lsh.pois > 0) {
		poi = (struct map_poi*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		mh->poiscnt = lsh.pois;
		mh->pois = poi;

		/* read all area */
		for (cnt = 0; (cnt < lsh.pois); cnt++) {			
			/* load basic data */		
			inpos = mempop (inpos, &poi->lon, 4);
			inpos = mempop (inpos, &poi->lat, 4);
			inpos = mempop (inpos, &ui64, 8);
			poi->id = ui64;
			inpos = mempop (inpos, &ui8, 1);
			poi->type = ui8;

			/* read the text data */
			poi->name = poi->data;
			inpos = memstrpop (inpos, poi->name, &i);

			poi->additional = poi->name+i;
			inpos = memstrpop (inpos, poi->additional, &i);

			mh->free = mh->free + map_poi_getsize (poi);
			if (cnt < (lsh.pois-1)) poi->next = (struct map_poi*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
			else poi->next = NULL;
			poi = (struct map_poi*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
		}
	}

	mh->pos.ilon = 0;
	mh->pos.ilat = 0;
	if ((char*)mh->free - (char*)mh > mh->datasize) {
	    d_printf ("ERROR: map_ls_data2hash buffer overrun! memsize:%d < mh->free-mh:%d", mh->datasize, (char*)mh->free - (char*)mh);
		errorexit(1);
	}
	DELFLAG(mh->status, MS_changed);

	return mh;
};


/****************************************************************
 * load hash data 
 */
struct map_hash *map_load_hash (int ilon, int ilat) {
	char fn[LEN_FILENAME];
	struct map_lsstat *mlss =  NULL;
	char *data;
	int size;
	struct map_hash *mh;

	map_ls_get_filename (fn, LEN_FILENAME, ilon, ilat);
	mlss = map_ls_statget (fn);
	if (mlss == NULL) {
		d_printf ("WARN: map_load_hash no pointer to mlss structure (fn:%s igeo:%d,%d)", fn, ilon, ilat);
		return NULL;
	}
	data = map_ls_loaddata (mlss, &size, ilon, ilat);
	if (data == NULL) {
		return NULL;
	}

	mh = map_ls_data2hash (data, size);
	if (mh) {
		mh->pos.ilon = ilon;
		mh->pos.ilat = ilat;
	}

	return mh;
};


int map_save_hash (struct map_hash *mh) {
	char fn[LEN_FILENAME];
	struct map_lsstat *mlss =  NULL;
	char *data;
	int size = 0;

	if (ISNOTFLAG(mh->status, MS_changed)) return 0;

	map_ls_get_filename (fn, LEN_FILENAME, mh->pos.ilon, mh->pos.ilat);
	mlss = map_ls_statget (fn);
	if (mlss == NULL) {
		d_printf ("WARN: map_save_hash no pointer to mlss structure (fn:%s igeo:%d,%d)", fn, mh->pos.ilon, mh->pos.ilat);
		return 0;
	}
	
	data = map_ls_hash2data (mh, &size);
	map_ls_savedata (mlss, data, size, mh->pos.ilon, mh->pos.ilat);
	DELFLAG(mh->status, MS_changed);

	return 1;
};


void map_save_all () {
	int i = 0;
	
	while (i < mhash_max) for (i = 0; i < mhash_max; i++) {
		if (mhash[i]) if (ISFLAG(mhash[i]->status, MS_changed)) {
			if (mhash[i] != map_hash_geti (mhash[i]->pos.ilon , mhash[i]->pos.ilat, MHLOAD_NONE))
				d_printf ("%s:%d something went wrong mhash != map_hash_get",__FILE__,__LINE__);
			if (map_save_hash (mhash[i]) == 0) DELFLAG(mhash[i]->status, MS_changed);
			break;
		}
		main_wnd_update ();
	}

	for (i = 0; i < MAP_LSSTAT_MAX; i++) {
		if (mlsstat[i].fname[0] != '\0') 
			map_ls_statclose (&mlsstat[i]);
	}
};


/****************************************************************************
 * defrag the map data files, needed to decrease the size of the file.
 */
void map_defrag () {
	DIR *directory = NULL;
	struct dirent *direntry = NULL;
	static struct stat filestat;
	char fn[LEN_FILENAME];

	d_printf ("map_defrag");
	directory = opendir(cfg.mappath);
	if (directory == NULL) {
		d_printf ("map_defrag opendir (%s) failed:%s", cfg.mappath, strerror (errno));
		return;
	}

	while ((direntry = readdir (directory)) != NULL) {
		snprintf (fn, LEN_FILENAME, "%s/%s", cfg.mappath, direntry->d_name);
		if (strncmp ((direntry->d_name+strlen(direntry->d_name)-8), ".mapdata", 9) == 0 && stat(fn, &filestat) == 0) 
			if (S_ISREG(filestat.st_mode)) {
				d_printf ("defrag file:%s size:%ld", direntry->d_name, filestat.st_size);
				map_defragfile (fn);
				stat(fn, &filestat);
				d_printf ("        now:%s size:%ld", direntry->d_name, filestat.st_size);
			}
	}

	closedir (directory);
};


void map_defragfile (char *fn) {
	struct map_lsstat *lsstat = NULL;
	int i;

	/* get lsstat from old file */
	map_clear();
	lsstat = map_ls_statopen (fn);
	d_printf ("lsstat r_fd:%d, w_fd:%d", lsstat->r_fd, lsstat->w_fd);

	/* reopen file and create write file */
	close (lsstat->r_fd);
	d_printf ("map_defragfile close file r_fd:%d", lsstat->r_fd);
	snprintf (lsstat->fnametmp, LEN_FILENAME, "%s.tmp", lsstat->fname);
	if (rename (lsstat->fname, lsstat->fnametmp) != 0) {
		d_printf ("ERROR: map_defragfile rename(%s,%s) failed: %s", lsstat->fname, lsstat->fnametmp, strerror(errno));
		errorexit (1);
	}
			
	/* open new file and write empty data in it*/
	map_ls_rwdatacheck (12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
	memset (lsrw_data, 0x0, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));
	lsstat->w_fd = open (lsstat->fname, O_CREAT|O_RDWR|O_TRUNC, 0644);
	if (lsstat->w_fd <= 0) {
		/* Error... so quit */
		d_printf ("ERROR: map_defrag could not open file [%s]for writing: ", lsstat->fname, strerror(errno));
		errorexit (-1);
	}
	write (lsstat->w_fd, lsrw_data, 12*(MAP_LSHASHFACTOR*MAP_LSHASHFACTOR+MAP_LSFREECNT));

	/* fill in new structure */
	for (i = 0; i < MAP_LSHASHFACTOR*MAP_LSHASHFACTOR; i++) {
		lsstat->w_lshpos[i].pos = 0;
		lsstat->w_lshpos[i].size = 0;
		if (i < MAP_LSFREECNT) {
			lsstat->w_free[i].pos = 0;
			lsstat->w_free[i].size = 0;
		}
	}
			
	/* reopen old file for reading */
#if defined(__MINGW32CE__) || defined(_WIN32_WCE) || defined(__MINGW32__)
	lsstat->r_fd = open (lsstat->fnametmp, O_RDONLY | _O_BINARY);
#else
	lsstat->r_fd = open (lsstat->fnametmp, O_RDONLY);
#endif
	if (lsstat->r_fd <= 0) {
		/* Error... so free all unneeded memory */
		d_printf ("ERROR: map_defrag could not open file [%s] for reading: ", lsstat->fnametmp, strerror(errno));
		errorexit (-1);
	}
	else {
		d_printf ("map_defragfile open file %s r_fd:%d", lsstat->fnametmp, lsstat->r_fd);
	}

	lsstat->changed = 1;
	map_ls_statclose (lsstat);
	map_init ();
};

