/***************************************************************************
 *            map_hash.c
 *
 *  Copyright  2009 - 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 <time.h>
#include "map.h"
#include "memoryleak.h"
#include "system.h"


/*****************************************************
 *
 *  Hash handling
 *
 */
struct map_hash *map_hash_alloc (int memsize) {
	struct map_hash *mh;

	mh = (struct map_hash*) ml_malloc (sizeof (struct map_hash) + memsize);

	mh->status = 0;
	mh->ways = NULL;
	mh->areas = NULL;
	mh->places = NULL;
	mh->pois = NULL;
	mh->free = mh->data;
	mh->datasize = memsize;
	mh->wayscnt = 0;
	mh->poiscnt = 0;
	mh->placescnt = 0;
	mh->areascnt = 0;

	return mh;
};


int map_hash_calc_size (int ways, int areas, int pois, int places, int pcnt, int ncnt, int ccnt) {
	return (POINTERALIGNMENT + sizeof (struct map_hash) + ways * (POINTERALIGNMENT + sizeof (struct map_way)) + areas * (POINTERALIGNMENT + sizeof (struct map_area))
			+ pcnt * (POINTERALIGNMENT + sizeof (struct map_pos)) + ncnt * (POINTERALIGNMENT + sizeof (struct map_waynode))
	    	+ pois * (POINTERALIGNMENT + sizeof (struct map_poi)) + places * (POINTERALIGNMENT + sizeof (struct map_place)) + ccnt);
};


/******************************************************************************
 * find the hash file inside the memory, if we can't find do as the loadflags
 * sais, return NULL or load the hash and add it into the map_hash table
 */
struct map_hash *map_hash_get (float lon, float lat, int loadflags) {
	return (map_hash_geti (map_geo2igeo(lon), map_geo2igeo(lat), loadflags));
};


struct map_hash *map_hash_geti (int ilon, int ilat, int loadflags) {
	struct map_hash *res = NULL;
	int hi;

	for (hi = 0; hi < mhash_max; hi++)
		if (mhash[hi] && mhash[hi]->pos.ilon == ilon && mhash[hi]->pos.ilat == ilat) {
			res = mhash[hi];
			for (; hi > 0; hi--) mhash[hi] = mhash[hi-1];
			mhash[0] = res;
			return res;
		}

	if (loadflags & MHLOAD_RELOAD) {
		if (mhash[mhash_max-1]) {
			map_hash_free (mhash[mhash_max-1], 1);
			mhash[mhash_max-1] = NULL;
		}
		memmove (&mhash[1], &mhash[0], sizeof (struct map_hash*)*mhash_max-1);
		mhash[0] = NULL;

		res = map_load_hash (ilon, ilat);
		if (res) {
			mhash[0] = res;
			DELFLAG (res->status, MS_changed);
		}
	}

	return res;
};


/******************************************************************************
 * add this hash data to the memory.
 * if it already excist exchange it and free the old hash data from the memory
 */
int map_hash_add (struct map_hash *mh) {
	int i, fi = -1;  /* fi: free hash position */
	struct map_hash *mhold = NULL;

	if (mh == NULL) return 0;
	
	/* check if we need to "UPDATE" or to add the hash data */
	for (mhold = NULL, i = 0; i < mhash_max; i++) {
		if (mhash[i]) {
			if (mhash[i]->pos.ilon == mh->pos.ilon && mhash[i]->pos.ilat == mh->pos.ilat) {
				mhold = mhash[i];
				fi = i;
				break;
			}
		}
		else if (fi == -1) fi = i;
	}

	if (mhold != NULL) {					/* update !! NOSAVEING !! */
		mhash[i] = NULL;
		ml_free (mhold);
		mhold = NULL;
	}

	/* add on the givin place, delete old entry */
	if (fi == -1) {
		if (mhash[mhash_max-1]) {
			map_hash_free (mhash[mhash_max-1], 1);
			mhash[mhash_max-1] = NULL;
		}
		memmove (&mhash[0], &mhash[1], sizeof (struct map_hash*)*mhash_max-1);
		mhash[0] = NULL;
		mhash[0] = mh;
	}
	else
		mhash[fi] = mh;
	
	return 1;
};


/******************************************************************************
 * remove the hash data from the memory, but in case it has been changed, 
 * save it. If noremove is set to 1 it won't be checked if we still have the 
 * pointer inside the hash list.
 */
void map_hash_free (struct map_hash *mh, int noremove) {
	int i;
	
	if (ISFLAG(mh->status, MS_changed)) { /* save the hash */
		map_save_hash (mh);
	}

	if (!noremove)
		for (i = 0; i < mhash_max; i++) if (mhash[i] == mh) mhash[i] = NULL;
	ml_free (mh);
};


/******************************************************************************
 * allocate new memory, copy all the data and free the momory again.
 */
struct map_hash *map_hash_realloc (struct map_hash *mhorg, int memsize) {
	struct map_hash *res = NULL;
	int i = memsize;
	
	if (i < mhorg->datasize) return res;

	res = map_hash_alloc (i);
	map_hash_copy (res, mhorg);

	/* check if we have to exchange the hash inside the hashtable */
	for (i = 0; i < mhash_max; i++) if (mhash[i] == mhorg) mhash[i] = res;
	ml_free (mhorg);

	return res;
};


/******************************************************************************
 * copy data from one to another hash... be sure to reserve enought memory.. 
 * we do no checking. hd (destination) <-- hs (source)
 *
 */
void map_hash_copy (struct map_hash *hd, struct map_hash *hs) {
	struct map_way *mw;
	struct map_area *ma;
	struct map_place *mp;
	struct map_poi *mpoi;
	int i;

	/* main informations */
	hd->status = hs->status;
	hd->pos.ilon = hs->pos.ilon;
	hd->pos.ilat = hs->pos.ilat;
	hd->wayscnt = 0;
	hd->areascnt = 0;
	hd->placescnt = 0;
	hd->poiscnt = 0;
	
	/* copy way to the hash */
	i = 0;
	mw = hs->ways;
	while (mw && i < hs->wayscnt) {
		map_way_append_to_hash (hd, mw);
		mw = mw->next;
		i++;
	}
	if (mw) {
		d_printf ("%s:%d map_hash_copy all ways copied but: mw != NULL (%p)", __FILE__,__LINE__, mw);
	}

	/* copy area to the hash */
	ma = hs->areas;
	i = 0;
	while (ma && i < hs->areascnt) {
		map_area_append_to_hash (hd, ma);
		ma = ma->next;
		i++;
	}
	if (ma) {
		d_printf ("%s:%d map_hash_copy all areas copied but: ma != NULL (%p)", __FILE__,__LINE__, ma);
	}

	/* copy place to the hash */
	mp = hs->places;
	i = 0;
	while (mp && i < hs->placescnt) {
		map_place_add_to_hash (hd, mp);
		mp = mp->next;
		i++;
	}
	if (mp) {
		d_printf ("%s:%d map_hash_copy all places copied but: mp != NULL (%p)", __FILE__, __LINE__, mp);
	}

	/* copy place to the hash */
	mpoi = hs->pois;
	i = 0;
	while (mpoi && i < hs->poiscnt) {
		map_poi_add_to_hash (hd, mpoi);
		mpoi = mpoi->next;
		i++;
	}
	if (mpoi) {
		d_printf ("%s:%d map_hash_copy all pois copied but: mpoi != NULL (%p) i:%d poiscnt:%d", __FILE__, __LINE__, mpoi, i , hs->poiscnt);
	}
};


int map_hash_countways (struct map_hash *mh) {
	struct map_way *mw;
	int w = 0;
	
	if (mh == NULL) return -1;
	for (mw = mh->ways, w = 0; 	mw; mw = mw->next) w++;

	if (mh->wayscnt != w) {
		d_printf ("%s:%s map_hash_countways numbers of ways found (%d) is not identical with wayscnt (%d)", __FILE__, __LINE__, w, mh->wayscnt);
		mh->wayscnt = w;
	}
	return w;
};


int map_hash_countareas (struct map_hash *mh) {
	struct map_area *ma;
	int a = 0;
	
	if (mh == NULL) return -1;
	ma = mh->areas;

	for (ma = mh->areas, a = 0;	ma; ma = ma->next) a++;

	return a;
};


/*
 * return amount of free space
 */
int map_hash_getfree_size (struct map_hash *mh) {
	return (mh->datasize - (mh->free - mh->data));
};


void *map_hash_getfree_aligned (struct map_hash *mh, int align) {
	int offset = (long int)mh->free % align;

	if (offset !=0) mh->free = mh->free + (align-offset);

	return mh->free;
};
