/* 
 * 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 <ctype.h>
#include <string.h>
#include <sys/param.h>

#include "tiles.h"
#include "map.h"
#include "map_item.h"

#define NONE  0
#define ABOVE 1
#define RIGHT 2
#define LEFT  4
#define BELOW 8

#define ALL_DIRS (ABOVE | RIGHT | LEFT | BELOW)

#define NUMSLOPES 256

typedef struct tilelist_s {
    int cost;
    func_t *upkeep;
    func_t *revenue;
    int live;
    labor_t labor;
    int rent;
    int sizex;
    int sizey;
    int demolish_cost;   
    func_t *landvalue;
    int maxstudents;
    int maxpatrons;
    int range;
    int patronperc;
    int poweruse;
    int wateruse;
    int capacity;
    func_t *production;
    
    tile_t *dir[ALL_DIRS+1];
    tile_t *slopes[NUMSLOPES];
} tilelist_t;

struct tiles_s {
    char prefix[MAXPATHLEN+1];
    tilelist_t list[100];
    int numtiles;
    tiles_loadimage_func_t *loadfunc;
    void *rock;
};

struct labor_group labor_table[] = {
    {LABOR_NOEDUCATION, "No education", 'N', 0,  18000},
    {LABOR_UNSKILLED,   "Unskilled"   , 'U', 8,  25000},
    {LABOR_BLUECOLLAR,  "Blue collar" , 'B', 12, 30000},	
    {LABOR_PROFESSIONAL,"Professional", 'P', 16, 50000},
    {LABOR_EXPERT,      "Expert"      , 'E', 20, 75000}
};

static int
get_slope(int tl, int tr, int bl, int br)
{
    int ret = ((tl << 6) + (tr << 4) + (bl << 2) + (br << 0));
    return ret;
}

#if 0
static int
scale_and_convert(GdkPixbuf *pixbuf, int x, int y, GdkPixmap **pixmap, GdkBitmap **bitmap, int *outsizex, int *outsizey)
{    
    GdkPixbuf *scaledpixbuf;
    float curx;
    float cury;

    /* figure out destination y size */
    curx = gdk_pixbuf_get_width(pixbuf);
    cury = gdk_pixbuf_get_height(pixbuf);
    y = (int)(((float)x)/curx*cury);
    if (y <= 0) y = 1;

    scaledpixbuf = gdk_pixbuf_scale_simple(pixbuf, x, y, GDK_INTERP_NEAREST);
    if (!scaledpixbuf) {
	printf("error scaling pixbuf!\n");
	return -1;
    }

    gdk_pixbuf_render_pixmap_and_mask (scaledpixbuf, pixmap, bitmap, 127);
    if (!*pixmap) {
	printf("Error making pixmap\n");
	return -1;
    }

    gdk_window_get_size((GdkWindow  *)*pixmap, outsizex, outsizey);

    return 0;    
}
#endif /* 0 */

/*
 * Try to find a cached copy of this image
 */

static int
find_cached(tiles_t *tiles, char *filename, tile_t **tile)
{
    int i;

    for (i = 0; i < tiles->numtiles;i++) {
	int j;
	tilelist_t *tilel = &tiles->list[i];
	tile_t *t;

	/* look at dirs */
	for (j = NONE; j <= ALL_DIRS; j++) {
	    t = tilel->dir[j];
		
	    if ((t) && (t->filename) && (strcasecmp(filename, t->filename) == 0)) {
		*tile = t;
		return 0;
	    }
	}

	/* look at slopes */
	for (j = 0; j < NUMSLOPES; j++) {
	    t = tilel->slopes[j];

	    if ((t) && (t->filename) && (strcasecmp(filename, t->filename) == 0)) {
		*tile = t;
		return 0;
	    }	    
	}
    }

    return -1;
}

static int
load_color(tiles_t *tiles, tile_t **tile, int r, int g, int b)
{
    tile_t *ret;

    ret = calloc(1, sizeof(tile_t));
    if (!ret) return -1;

    ret->color_r = r;
    ret->color_g = g;
    ret->color_b = b;

    *tile = ret;

    return 0;
}

static int
slope_image_height(int slope)
{
    int tileheight = 16;
    int h[4];
    int i;
    int min = 999;
    int max = -999;

    h[0] = 0 -  ((slope >> 6) & 3) * tileheight;
    h[1] = 32 -   ((slope >> 4) & 3) * tileheight;
    h[2] = 32 - ((slope >> 2) & 3) * tileheight;
    h[3] = 64 - ((slope >> 0) & 3) * tileheight;

    for (i = 0; i < 4; i++) {
	if (h[i] < min) {
	    min = h[i];
	}
	if (h[i] > max) {
	    max = h[i];
	}
    }

    return abs(max - min);
}

static int
mypow(int x, int y)
{
    int i;
    int ret;

    if (y == 0) return 1;

    ret = x;
    for (i = 1; i < y; i++) {
	ret *= x;
    }

    return ret;
}

static int
actual_load_image(tiles_t *tiles, tile_t *tile, int rotation, int zoom)
{
    char fname[1024];
    char shortfnamebuf[1024];
    char *shortfname;
    int width;
    int height;
    int i;
    int hasdup = 1;

    /* advance to right image */
    snprintf(shortfnamebuf, sizeof(shortfnamebuf)-1,"%s", tile->filename);
    shortfname = shortfnamebuf;

    for (i = 0; i <= rotation; i++) {
	char *comma = strchr(shortfname, ',');
	if (comma) {
	    hasdup = 0;
	    *comma = '\0';	    
	    if (i < rotation) {
		shortfname = comma + 1;
	    }
	}
    }
   
    /* look to see if we've already loaded it */
    if (hasdup) {
	for (i = 0; i < 4; i++) {
	    if (tile->image[i][zoom] != NULL) {
		tile->image[rotation][zoom] = tile->image[i][zoom];
		tile->extray[rotation][zoom] = tile->extray[i][zoom];		
		return 0;
	    }
	}
    }

    width = tile->width / mypow(2, zoom);
    height = tile->height / mypow(2, zoom);

    snprintf(fname, sizeof(fname)-1,"%s/%s",tiles->prefix, shortfname);
    tile->image[rotation][zoom] = tiles->loadfunc(fname, width, height, 
					   &tile->extray[rotation][zoom], tiles->rock);
    if (!tile->image[rotation][zoom]) printf("Error loading image: %s\n", fname);

    return 0;
}



static int
load_image(tiles_t *tiles, tile_t **tile, char *filename, int ispattern, 
	   int sizex, int sizey, int slope)
{
    tile_t *ret;
    int width;
    int height;
    int i;
    int j;
    int issame = 0;

    /* Look to see if we've already loaded this filename */
    if (find_cached(tiles, filename, tile) == 0) {
	return 0;
    }

    ret = malloc(sizeof(tile_t));
    if (!ret) return -1;
    memset(ret, 0, sizeof(tile_t));

    ret->filename = strdup(filename);
    ret->ispattern = ispattern;
    ret->color_r = -1;
    ret->color_g = -1;
    ret->color_b = -1;

    
    ret->width = 64*sizex + 64*sizey;
    if (slope) {
	ret->height = slope_image_height(slope);
    } else {
	ret->height = (32*sizex + 32*sizey);
    }

#if 0
    for (j = 0; j < 4; j++) {
	for (i = 0; i < 7; i++) {	
	    actual_load_image(tiles, ret, j, i);
	}
    }
#endif /* 0 */

    *tile = ret;

    return 0;
}

static int
parse_slope(char *in)
{
    if (strlen(in) != 4) {
	printf("slope should be 4 long\n");
	return -1;
    }

    return get_slope(in[0] - '0',
		     in[1] - '0',
		     in[2] - '0',
		     in[3] - '0');
}

static int
parse_neighbor(char *str)
{
    if (strcasecmp(str,"NONE") == 0) return NONE;
    if (strcasecmp(str,"ABOVE") == 0) return ABOVE;
    if (strcasecmp(str,"BELOW") == 0) return BELOW;
    if (strcasecmp(str,"RIGHT") == 0) return RIGHT;
    if (strcasecmp(str,"LEFT") == 0) return LEFT;
    if (strcasecmp(str,"ALL_DIRS") == 0) return ALL_DIRS;
    
    return -1;
}

static int
parse_neighborstring(char *str)
{
    int ret = 0;

    while (str) {
	char *next;

	next = strchr(str,'|');
	if (next) {
	    *next = '\0';
	    next++;
	}

	ret |= parse_neighbor(str);

	str = next;
    }

    return ret;
}

static int
parsehex(char *str)
{
    int d1 = 0;
    int d2 = 0;
    
    if (isdigit((int)str[0])) {
	d1 = str[0] - '0';
    } else {
	d1 = str[0] - 'A'+10;
    }

    if (isdigit((int)str[1])) {
	d2 = str[1] - '0';
    } else {
	d2 = str[1] - 'A'+10;
    }

    return d1*16+d2;
}

static int
add_tile(tiles_t *tiles, int objnumber, char *type, char *name)
{
    char filename[1024];
    int sizex = tiles->list[objnumber].sizex;
    int sizey = tiles->list[objnumber].sizey;
    int color_r = -1;
    int color_g = -1;
    int color_b = -1;
    int ispattern = 0;

    if (name == NULL) {
	printf("NULL filaname for %d\n",objnumber);
	return -1;
    }

    if (*name == '#') {
	color_r = parsehex(name+1);
	color_g = parsehex(name+3);
	color_b = parsehex(name+5);
    } else if (*name == '%') {
	ispattern = 1;
	snprintf(filename, sizeof(filename)-1,"%s",name+1);
    } else {
	snprintf(filename, sizeof(filename)-1,"%s",name);
    }

    if (strncasecmp(type,"slope",5) == 0) {
	if (strcasecmp(type+5,"all") == 0) {
	    int i;

	    for (i = 0; i < NUMSLOPES; i++ ) {

		if (color_r > -1) {
		    load_color(tiles, &tiles->list[objnumber].slopes[i], 
			       color_r, color_g, color_b);
		} else {
		    load_image(tiles, &tiles->list[objnumber].slopes[i], 
			       filename, ispattern, sizex, sizey, i);
		}
	    }

	    return 0; /* xxx */
	} else {
	    int slope = parse_slope(type+5);
	    if (color_r > -1) {
		return load_color(tiles, &tiles->list[objnumber].slopes[slope], 
				  color_r, color_g, color_b);
	    } else {
		return load_image(tiles, &tiles->list[objnumber].slopes[slope], 
				  filename, ispattern, sizex, sizey, slope);
	    }
	}
    } else {
	int dirs = parse_neighborstring(type);
	if (color_r > -1) {
	    return load_color(tiles, &tiles->list[objnumber].dir[dirs], 
			      color_r, color_g, color_b);
	} else {
	    return load_image(tiles, &tiles->list[objnumber].dir[dirs], filename, 
			      ispattern, sizex, sizey, 0);
	}
    }
}
/*
 * Make into a args structure
 */
static int
make_args(char *str, char **argv, int maxargs, int *argc)
{
    int numargs = 0;

    while (str) {
	char *end;

	while ((*str) && (isspace((int)*str))) str++;
	if (*str == '\0') break;

	end = str;

	while ((*end) && (!isspace((int)*end))) end++;
	if (*end) {
	    *end = '\0';
	    end++;
	}

	argv[numargs++] = str;
	
	str = end;
    }

    *argc = numargs;
    argv[numargs] = NULL;

    return 0;
}

static int
parseline(char *line, char **name, char **val)
{
    char *start = line;
    char *colon;

    while ((*start) && (isspace((int)*start))) start++;

    if (*start == '\0') {
	return 0;
    }

    colon = strchr(start, ':');
    if (!colon) {
	printf("no colon found\n");
	return -1;
    }

    *colon = '\0';
    colon++;

    while ((*colon) && (isspace((int)*colon))) colon++;    
    if (*colon == '\0') {
	printf("no value found\n");
	return -1;
    }

    *name = start;
    *val = colon;

    return 0;    
}

static int
parse_labor(char **args, int argc, labor_t *labor)
{
    int i;
    
    for (i = 0; i < argc; i++) {
	int value;
	char *p;
	int j;

	value = strtoul(args[i], &p, 10);
	if ((value < 0) || (!p) || (p[1] != '\0')) {
	    printf("Invalid labor parameter %s\n", args[i]);
	    continue;
	}

	for (j = 0; j < LABOR_NUMGROUPS; j++) {
	    if (labor_table[j].id == *p) {
		enum labor_skill skill = labor_table[j].skill;
		labor->workers[skill] = value;
		break;
	    }
	}
	
	if (j == LABOR_NUMGROUPS) {
	    printf("Couldn't find labor type '%c' in %s\n", *p, args[i]);
	}
    }

    return 0;
}

static int
readspecfile(tiles_t *tiles, char *specfile)
{
    FILE *f;
    char line[1024];
    int objnum = 0;
    char *p;

    f = fopen(specfile,"r");
    if (!f) {
	return -1;
    }

    strcpy(tiles->prefix, specfile);
    p = strrchr(tiles->prefix, '/');
    if (p) {
	*(p+1) = '\0';
    }

    while (fgets(line, sizeof(line)-1, f)) {
	char *name = NULL;
	char *val = NULL;
	char *argv[10];
	int argc;

	if (parseline(line, &name, &val)) {
	    printf("Error parsing line: %s\n",line);
	    continue;
	}
	if (name == NULL) continue;

	/* comments */
	if (*name == '#') continue;

	if (make_args(val, argv, 10, &argc)) {
	    printf("Error making args: %s\n",val);
	    continue;
	}

	if (strcasecmp(name,"OBJECT") == 0) {
	    if (argc != 1) {
		printf("OBJECT must have 1 arguments\n");
		continue;
	    }

	    objnum = map_item_registerobj(argv[0]);
	    if (objnum == MAPOBJ_INVALID) {
		printf("Tile [%s] not supported\n", argv[0]);
	    }
	    if (objnum + 1 > tiles->numtiles) {
		tiles->numtiles = objnum + 1;
	    }

	    memset(&tiles->list[objnum], 0, sizeof(tilelist_t));
	    tiles->list[objnum].sizex = 1;
	    tiles->list[objnum].sizey = 1;
	} else if (strcasecmp(name,"TYPE") == 0) {
	    map_item_settype(objnum, argv[0]);

	} else if (strcasecmp(name,"TILE") == 0) {
	    add_tile(tiles, objnum, argv[0], argv[1]);

	} else if (strcasecmp(name,"COST") == 0) {
	    tiles->list[objnum].cost = atoi(argv[0]);

	} else if (strcasecmp(name,"UPKEEP") == 0) {
	    tiles->list[objnum].upkeep = function_parse(argv, argc, NULL);

	} else if (strcasecmp(name,"REVENUE") == 0) {
	    tiles->list[objnum].revenue = function_parse(argv, argc, NULL);

	} else if (strcasecmp(name,"RENT") == 0) {
	    tiles->list[objnum].rent = atoi(argv[0]);

	} else if (strcasecmp(name,"LIVE") == 0) {
	    tiles->list[objnum].live = atoi(argv[0]);

	} else if (strcasecmp(name,"LABOR") == 0) {
	    parse_labor(argv, argc, &tiles->list[objnum].labor);

	} else if (strcasecmp(name,"LANDVALUE") == 0) {
	    tiles->list[objnum].landvalue = function_parse(argv, argc, NULL);

	} else if (strcasecmp(name,"DEMOLISHCOST") == 0) {
	    tiles->list[objnum].demolish_cost = atoi(argv[0]);

	} else if (strcasecmp(name,"STUDENTS") == 0) {
	    tiles->list[objnum].maxstudents = atoi(argv[0]);

	} else if (strcasecmp(name,"MAXPATRONS") == 0) {
	    tiles->list[objnum].maxpatrons = atoi(argv[0]);

	} else if (strcasecmp(name,"PRODUCTION") == 0) {
	    tiles->list[objnum].production = function_parse(argv, argc, NULL);

	} else if (strcasecmp(name,"RANGE") == 0) {
	    tiles->list[objnum].range = atoi(argv[0]);

	} else if (strcasecmp(name,"PATRONPERC") == 0) {
	    tiles->list[objnum].patronperc = atoi(argv[0]);

	} else if (strcasecmp(name,"POWERUSE") == 0) {
	    tiles->list[objnum].poweruse = atoi(argv[0]);

	} else if (strcasecmp(name,"WATERUSE") == 0) {
	    tiles->list[objnum].wateruse = atoi(argv[0]);

	} else if (strcasecmp(name,"CAPACITY") == 0) {
	    tiles->list[objnum].capacity = atoi(argv[0]);

	} else if (strcasecmp(name,"SIZE") == 0) {
	    if ((isdigit((int)argv[0][0])) && (argv[0][1] == 'x') && 
		(isdigit((int)argv[0][2]))) {
		tiles->list[objnum].sizex = argv[0][0] - '0';
		tiles->list[objnum].sizey = argv[0][2] - '0';
	    } else {
		printf("Error parsing size argument\n");
	    }

	} else {
	    printf("Option %s being ignored\n", name);
	}
    }

    return 0;
}

/*
 * If 'window' is NULL don't load images
 */
int tiles_init(tiles_t **tiles, char *specfile, tiles_loadimage_func_t *loadfunc, void *rock)
{
    tiles_t *ret;

    ret = malloc(sizeof(tiles_t));
    if (!ret) return -1;
    memset(ret, 0, sizeof(tiles_t));
    ret->loadfunc = loadfunc;
    ret->rock = rock;

    *tiles = ret;

    return readspecfile(ret, specfile);
}

int tiles_get_flat(tiles_t *tiles, mapobj_t objtype, surrounding_t *surounding, tile_t **tile)
{
    int neighbormask = 0;

    if ((objtype > tiles->numtiles) || (objtype < 0)) {
	printf("invalid type\n");
	return -1;
    }

    if (surounding->dir.above == objtype) neighbormask |= ABOVE;
    if (surounding->dir.below == objtype) neighbormask |= BELOW;
    if (surounding->dir.right == objtype) neighbormask |= RIGHT;
    if (surounding->dir.left  == objtype) neighbormask |= LEFT;

    if (tiles->list[objtype].dir[neighbormask] != NULL) {
	*tile = tiles->list[objtype].dir[neighbormask];
	return 0;
    } else if (tiles->list[objtype].dir[0] != NULL) {
	/* if don't have that neighbor tile just use the generic one */
	*tile = tiles->list[objtype].dir[0];
	return 0;
    }

    printf("Can't find tile for objtype = %d\n", objtype);
    return -1;
}

static int
tiles_heightsquare_to_slope(heightsquare_t *square)
{
    int lowest = square->dir.topleft;

    /* find the lowest corner */
    if (square->dir.topright < lowest) lowest = square->dir.topright;
    if (square->dir.botleft < lowest) lowest = square->dir.botleft;
    if (square->dir.botright < lowest) lowest = square->dir.botright;

    /* return error if any of corners are >= 4 away from this one */
    if (square->dir.topleft - lowest >= 4) return -1;
    if (square->dir.topright - lowest >= 4) return -1;
    if (square->dir.botleft - lowest >= 4) return -1;
    if (square->dir.botright - lowest >= 4) return -1;

    return get_slope(square->dir.topleft - lowest, square->dir.topright - lowest,
		    square->dir.botleft - lowest, square->dir.botright - lowest);
}

int tiles_get_slope(tiles_t *tiles, mapobj_t objtype, int slope, tile_t **tile)
{
    /* xxx check input values */

    if (tiles->list[objtype].slopes[slope] != NULL) {
	*tile = tiles->list[objtype].slopes[slope];
	return 0;
    }

    return -1;
}

int tiles_get(tiles_t *tiles, mapobj_t objtype, surrounding_t *surounding, heightsquare_t *heightsquare, tile_t **tile)
{
    if (!heightmap_islevel(heightsquare)) {
	int slope = tiles_heightsquare_to_slope(heightsquare);
	
	return tiles_get_slope(tiles, objtype, slope, tile);
    } else {
	return tiles_get_flat(tiles, objtype, surounding, tile);
    }
}

void *
tile_image(tiles_t *tiles, tile_t *tile, int rotation, int zoom)
{
    if (tile->color_r > -1) return NULL;

    if (!tile->image[rotation][zoom]) {
	actual_load_image(tiles, tile, rotation, zoom);
    }

    return tile->image[rotation][zoom];
}

int tiles_cost(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].cost;
}

int
tiles_getrevenue(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock)
{
    return function_evaluate(tiles->list[objtype].revenue, cb, rock);
}

int
tiles_getrent(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].rent;
}

int
tiles_getupkeep(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock)
{
    return function_evaluate(tiles->list[objtype].upkeep, cb, rock);
}

int
tiles_gethouse(tiles_t *tiles, mapobj_t objtype)
{
    if ((objtype < 0) || (objtype >= tiles->numtiles)) {
	return 0;
    }

    return tiles->list[objtype].live;
}

labor_t *
tiles_getemploy(tiles_t *tiles, mapobj_t objtype)
{
    return &tiles->list[objtype].labor;
}

int 
tiles_getnumemploy(tiles_t *tiles, mapobj_t objtype)
{
    int i;
    int tot = 0;
    labor_t *labor = &tiles->list[objtype].labor;

    for (i = 0; i < LABOR_NUMGROUPS; i++) {
	tot += labor->workers[i];
    }

    return tot;
}

void
tiles_getsize(tiles_t *tiles, mapobj_t objtype, int *sizex, int *sizey)
{
    if ((objtype < 0) || (objtype >= tiles->numtiles)) {
	*sizex = 1;
	*sizey = 1;
	return;
    }

    *sizex = tiles->list[objtype].sizex;
    *sizey = tiles->list[objtype].sizey;
}

int
tiles_getlandvalue(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock)
{
    return function_evaluate(tiles->list[objtype].landvalue, cb, rock);
}

int tiles_get_demolish_cost(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].demolish_cost;
}

int tiles_get_maxstudents(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].maxstudents;
}

int tiles_get_maxpatrons(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].maxpatrons;
}

int tiles_getproduction(tiles_t *tiles, mapobj_t objtype, func_getvariable_cb *cb, void *rock)
{
    return function_evaluate(tiles->list[objtype].production, cb, rock);
}

int tiles_getrange(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].range;
}

float tiles_getpatronperc(tiles_t *tiles, mapobj_t objtype)
{
    return ((float)tiles->list[objtype].patronperc)/((float)100);
}

int
tiles_getpoweruse(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].poweruse;
}

int
tiles_getwateruse(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].wateruse;
}

int
tiles_getcapacity(tiles_t *tiles, mapobj_t objtype)
{
    return tiles->list[objtype].capacity;
}
