/* 
 * 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 <string.h>
#include <math.h>

#include "map.h"

int *landvalue_cache;

typedef int circle_enumerate_func(map_t *map, int x, int y, float dist, void *rock);

static void
circle_iterate(map_t *map, int x, int y, int radius, 
	       circle_enumerate_func *func, void *rock)
{
    int j;
    int k;
    int mapsizex = map_get_sizex(map);
    int mapsizey = map_get_sizey(map);

    for (j = x-radius; j <= x+radius; j++) {
	for (k = y-radius; k <= y+radius; k++) {
	    float distaway;

	    /* must be within radius */
	    distaway = sqrt((abs(x-j) * abs(x-j)) + (abs(y-k) * abs(y-k)));

	    if ( distaway > radius)
		continue;
	    
	    /* must be within map */
	    if ((j < 0) || (j >= mapsizex)) continue;
	    if ((k < 0) || (k >= mapsizey)) continue;
	    
	    /* make callback */
	    func(map, j, k, distaway, rock);
	}
    }
}

/*
 * Water - no value (can't be bought)
 * Hills - steepness 0: +100
 *         steepness 1: divide by 2
 *         steepness 2: divide by 4
 *         steepness 3: divide by 8
 * Near other - (look up to radius of 10)
 *              farm: 1,000 / distance
 *              housing: 5,000 / distance
 *              water: 10,000 / distance
 *              road: 1000 / distance
 *              flat land: 10 / distance
 *              water: 500 / dististance^2
 *              work: 5,000 / distance
 *
 * If within 5 of roads -
 *                        0 roads: divide by 4
 *                        1-2 roads: divide by 1
 *                        3+ roads: mult by 2
 *
 */
typedef struct landvalue_obj_s {
    int value;
    int roads_seen;
    tiles_t *tiles;
} landvalue_obj_t;

static int 
getvariable_cb(char *name, void *rock)
{
    int dist = *(int *)rock;

    if (strcasecmp(name, "dist") == 0) {
	return dist;
    }

    printf(_("variable not understood: %s\n"), name);
    return 0;
}

static int
calc_landvalue_func(map_t *map, int x, int y, float dist, void *rock)
{
    mapobj_t obj;
    landvalue_obj_t *landvalue = (landvalue_obj_t *)rock;
    int disti;
 

    if (dist < 1.0) dist = 1.0;

    obj = map_get_type(map, x, y);

    if (map_islevel(map, x, y)) {
	landvalue->value += 30 / dist; /* xxx */
    }

    disti = (int)dist;
    landvalue->value += tiles_getlandvalue(landvalue->tiles, obj, &getvariable_cb, &disti);

    return 0;
}

int
government_calculate_onelandvalue(map_t *map, tiles_t *tiles, int x, int y)
{
    mapobj_t obj;
    landvalue_obj_t landval;
    int mapsizex = map_get_sizex(map);
    int mapsizey = map_get_sizey(map);

    if ((x < 0) || (y < 0) ||
	(x >= mapsizex) || (y >= mapsizey)) {
	return 0;
    }

    /*
     * First check cache
     */
    if ((landvalue_cache) && (landvalue_cache[y*mapsizex+x] != -1)) {
	return landvalue_cache[y*mapsizex+x];
    }

    obj = map_get_type(map, x, y);
    if (map_item_gettype(obj) == MAPTYPE_WATER) {
	if (landvalue_cache) {
	    landvalue_cache[y*mapsizex+x] = 0;
	}
	return 0;
    }

    memset(&landval, 0, sizeof(landvalue_obj_t));
    landval.tiles = tiles;

    circle_iterate(map, x, y, 10, &calc_landvalue_func, &landval);
    if (landval.value < 0) {
	landval.value = 0; /* xxx */
    }
		
    /* steepness */
    switch(map_getsteepness(map, x, y)) {
    case 0: landval.value += 100; break;
    case 1: landval.value /= 4; break;
    case 2: landval.value /= 8; break;
    default:landval.value /= 16; break;
    }
    
    /* road bonus */
    if (landval.roads_seen <= 0) {
	landval.value /= 4;
    } else if (landval.roads_seen >= 3) {
	landval.value *= 2;
    }

    /*
     * Plus value of whatever is there now
     */
    landval.value += tiles_cost(tiles, obj);

    if (landvalue_cache) {
	landvalue_cache[y*mapsizex+x] = landval.value;
    }
		   
    return landval.value;
}

int
government_calculate_landvalues(map_t *map, tiles_t *tiles)
{
    int mapsizex = map_get_sizex(map);
    int mapsizey = map_get_sizey(map);
    int i;
    int j;

    for (i = 0; i < mapsizex; i++) {
	for (j = 0; j < mapsizey; j++) {
	    government_calculate_onelandvalue(map, tiles, i, j);
	}
    }

    return 0;
}

void
government_enable_landvalue_cache(map_t *map)
{
    int mapsizex = map_get_sizex(map);
    int mapsizey = map_get_sizey(map);
    int x;
    int y;

    if (landvalue_cache) {
	free(landvalue_cache);
    }

    landvalue_cache = calloc(1, sizeof(int)*mapsizex*mapsizey);
    for (x = 0; x < mapsizex; x++) {
	for (y = 0; y < mapsizey; y++) {
	    landvalue_cache[y*mapsizex+x] = -1;
	}
    }
}

void
government_disable_landvalue_cache(void)
{
    if (landvalue_cache) {    
	free(landvalue_cache);    
    }

    landvalue_cache = NULL;
}
