/* 
 * Copyright (C) 2003 Tim Martin
 *
 * 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 <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>

#include "utils.h"
#include "mapspot.h"

typedef struct mapspot_item_s {
    int mapx;
    int mapy;    
    void *data;    
} mapspot_item_t;

struct mapspot_list_s {
    mapspot_item_t *items;
    int alloclen;
    int len;
    int next;
};

extern void
mapspot_list_add(mapspot_list_t **list, int x, int y, void *data)
{
    mapspot_list_t *l;
    mapspot_item_t *item;

    if (!*list) {
	*list = calloc(1, sizeof(mapspot_list_t));
    }
    l = *list;
    
    if (l->next + 1 >= l->alloclen) {
	int newalloc = (l->alloclen ? l->alloclen * 2 : 8);
	
	l->items = realloc(l->items, newalloc*sizeof(mapspot_item_t));
	l->alloclen = newalloc;
    }

    item = &(l->items[l->next]);
    l->next++;

    item->mapx = x;
    item->mapy = y;
    item->data = data;
    l->len++;
}

extern void
mapspot_list_remove(mapspot_list_t **list, int x, int y)
{    
    mapspot_list_t *l = *list;
    int i;
    int found = 0;

    if (!l) return;

    for (i = 0; i < l->next; i++) {
	mapspot_item_t *item = &(l->items[i]);
	
	if ((item->mapx == x) && (item->mapy == y)) {
	    item->mapx = -1;
	    item->mapy = -1;
	    l->len--;
	    found++;
	}
    }
    
    if (found > 1) {
	printf(_("Multiple of %d,%d were in list\n"), x, y);
    } else if (found == 0) {
	printf(_("%d,%d was not found in list\n"), x, y);
    }
}

extern void
mapspot_list_remove_data(mapspot_list_t **list, int x, int y, void *data)
{
    mapspot_list_t *l = *list;
    int i;
    int found = 0;

    if (!l) return;

    for (i = 0; i < l->next; i++) {
	mapspot_item_t *item = &(l->items[i]);
	
	if ((item->mapx == x) && (item->mapy == y) && (item->data == data)) {
	    item->mapx = -1;
	    item->mapy = -1;
	    l->len--;
	    found++;
	}
    }
    
    if (found > 1) {
	printf(_("Multiple of %d,%d were in list\n"), x, y);
    } else if (found == 0) {
	printf(_("%d,%d was not found in list\n"), x, y);
    }
}

extern void
mapspot_list_extract(mapspot_list_t **list, int *x, int *y, void **data)
{
    mapspot_list_t *l = *list;
    mapspot_item_t *item;
    int i = 0;

    if (!l) return;

    do {
	item = &(l->items[i]);
	i++;
	if (i > l->next) {
	    printf(_("Nothing to extract from list!\n"));
	    return;
	}
    } while ((item->mapx == -1) && (item->mapy == -1));

    *x = item->mapx;
    *y = item->mapy;
    if (data) {
	*data = item->data;
    }

    /* remove item */
    item->mapx = -1;
    item->mapy = -1;
    l->len--;
    
    /* xxx do a memove if i is high */

    /* free list if it became empty */
    if (l->len == 0) {
	free(l);
	*list = NULL;
    }
}

extern void
mapspot_list_extract_end(mapspot_list_t **list, int *x, int *y, void **data)
{
    mapspot_list_t *l = *list;
    mapspot_item_t *item;
    int i;

    if (!l) return;

    i = l->next - 1;
    do {
	item = &(l->items[i]);
	i--;
	if (i < 0) {
	    printf(_("Nothing to extract from list!\n"));
	    return;
	}
    } while ((item->mapx == -1) && (item->mapy == -1));

    *x = item->mapx;
    *y = item->mapy;
    if (data) {
	*data = item->data;
    }

    /* remove item */
    item->mapx = -1;
    item->mapy = -1;
    l->len--;

    l->next--;

    /* free list if it became empty */
    if (l->len == 0) {
	free(l);
	*list = NULL;
    }
}

extern void
mapspot_list_free(mapspot_list_t *list)
{
    free(list);
}

extern int
mapspot_list_length(mapspot_list_t *list)
{
    if (!list) {
	return 0;
    } else {
	return list->len;
    }
}

extern float
mapspot_list_nearest(mapspot_list_t *list, int x, int y)
{
    float nearest = MAXFLOAT;
    int i;

    if (!list) return MAXFLOAT;

    if ((x < 0) || (y < 0)) {
	return MAXFLOAT;
    }

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx != -1) || (item->mapy != -1)) {
	    float dist;
	    float distx = abs(item->mapx - x);
	    float disty = abs(item->mapy - y);

	    dist = sqrt(distx*distx + disty*disty);

	    if (dist < nearest) {
		nearest = dist;
	    }
	}
    }

    return nearest;    
}

extern int
mapspot_list_num_within(mapspot_list_t *list, int x, int y, float within)
{
    int i;
    int ret = 0;

    if (!list) return 0;

    if ((x < 0) || (y < 0)) {
	return 0;
    }

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx != -1) || (item->mapy != -1)) {
	    float dist;
	    float distx = abs(item->mapx - x);
	    float disty = abs(item->mapy - y);

	    dist = sqrt(distx*distx + disty*disty);

	    if (dist <= within) {
		ret++;
	    }
	}
    }

    return ret;
}

extern int
mapspot_within_range(mapspot_list_t *list, int x, int y, map_t *map, tiles_t *tiles)
{
    int i;

    if (!list) return 0;

    if ((x < 0) || (y < 0)) {
	return 0;
    }

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx != -1) || (item->mapy != -1)) {
	    float dist;
	    float distx = abs(item->mapx - x);
	    float disty = abs(item->mapy - y);
	    int range = tiles_getrange(tiles, map_get_type(map, item->mapx, item->mapy));

	    dist = sqrt(distx*distx + disty*disty);

	    if (dist <= range) {
		return 1;
	    }
	}
    }

    return 0;
}

extern int
mapspot_is_in_list(mapspot_list_t *list, int x, int y)
{
    int i;

    if (!list) return 0;

    if ((x < 0) || (y < 0)) {
	return 0;
    }

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx == x) && (item->mapy == y)) {
	    return 1;
	}
    }

    return 0;
}

extern int
mapspot_within_iterate(mapspot_list_t *list, int x, int y,
		       float within,
		       mapspot_iterator_t *cb, void *rock)
{
    int i;

    if (!list) return 0;

    if ((x < 0) || (y < 0)) {
	return 0;
    }

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx != -1) || (item->mapy != -1)) {
	    float dist;
	    float distx = abs(item->mapx - x);
	    float disty = abs(item->mapy - y);

	    dist = sqrt(distx*distx + disty*disty);

	    if (dist <= within) {
		int r;
		r = cb(item->mapx, item->mapy, item->data, dist, rock);
		if (r) return r;
	    }
	}
    }

    return 0;
}

extern int
mapspot_iterate(mapspot_list_t *list, 
		mapspot_iterator_t *cb, void *rock)
{
    int i;

    if (!list) return 0;

    for (i = 0; i < list->next; i++) {
	mapspot_item_t *item = &(list->items[i]);
	if ((item->mapx != -1) || (item->mapy != -1)) {
	    int r;
	    r = cb(item->mapx, item->mapy, item->data, 0, rock);
	    if (r) return r;	
	}
    }

    return 0;
}
