/* 
 * 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 "heightmap.h"

#define MAXHEIGHTDIFFERENCE 1

#define MAPPOS(mapstruct, x, y) mapstruct->map[ ((y) * (mapstruct->sizex+1)) + (x)]

struct heightmap_s {
    int sizex;
    int sizey;

    char *map;
};

int heightmap_init(int sizex, int sizey, heightmap_t **map)
{
    heightmap_t *ret;

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

    ret->sizex = sizex;
    ret->sizey = sizey;

    ret->map = malloc(sizeof(char)*(sizex+1)*(sizey+1));
    if (!ret->map) return -1;
    memset(ret->map, DEFAULT_HEIGHT, sizeof(char)*(sizex+1)*(sizey+1));    

    *map = ret;

    return 0;			 
}

int heightmap_getsize_x(heightmap_t *map)
{
    return map->sizex;
}

int heightmap_getsize_y(heightmap_t *map)
{
    return map->sizey;
}

int heightmap_getheight(heightmap_t *map, int x, int y, int dirview, heightsquare_t *square)
{
    if ((x < 0) || (x > map->sizex - 1) ||
	(y < 0) || (y > map->sizey - 1)) {
	return -1;
    }

    square->index[(0+dirview+4)%4]  = MAPPOS(map,x  ,y  );
    square->index[(1+dirview+4)%4] = MAPPOS(map,x+1,y  );
    square->index[(2+dirview+4)%4] = MAPPOS(map,x+1,y+1);
    square->index[(3+dirview+4)%4]  = MAPPOS(map,x  ,y+1);
    
    return 0;
}

int heightmap_get(heightmap_t *map, int x, int y)
{
    return MAPPOS(map, x, y);
}

int
heightmap_can_changeheight(heightmap_t *map, int x, int y, int change)
{
    int newheight;

    newheight = MAPPOS(map,x,y) + change;


    if (x > 0)
	if (abs(newheight - MAPPOS(map,x-1,y)) > MAXHEIGHTDIFFERENCE) {	    
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x-1,y);*/
	    return 0;
	}	
    if (x < map->sizex-1)
	if (abs(newheight - MAPPOS(map,x+1,y)) > MAXHEIGHTDIFFERENCE) {
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x+1,y);*/
	    return 0;
	}
    if (y > 0)
	if (abs(newheight - MAPPOS(map,x,y-1)) > MAXHEIGHTDIFFERENCE) {
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x,y-1);*/
	    return 0;
	}
    if (y < map->sizey-1)
	if (abs(newheight - MAPPOS(map,x,y+1)) > MAXHEIGHTDIFFERENCE) {
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x,y+1);*/
	    return 0;
	}
    if ((x > 0) && (y > 0))
	if (abs(newheight - MAPPOS(map,x-1,y-1)) > MAXHEIGHTDIFFERENCE) {	    
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x-1,y);*/
	    return 0;
	}	
    if ((x < map->sizex-1) && (y > 0))
	if (abs(newheight - MAPPOS(map,x+1,y-1)) > MAXHEIGHTDIFFERENCE) {	    
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x-1,y);*/
	    return 0;
	}	
    if ((x < map->sizex-1) && (y < map->sizey-1))
	if (abs(newheight - MAPPOS(map,x+1,y+1)) > MAXHEIGHTDIFFERENCE) {	    
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x-1,y);*/
	    return 0;
	}	
    if ((x > 0) && (y < map->sizey-1))
	if (abs(newheight - MAPPOS(map,x-1,y+1)) > MAXHEIGHTDIFFERENCE) {	    
	    /*printf("diff btwn %d,%d and %d,%d too big\n",x,y,x-1,y);*/
	    return 0;
	}	
        
    return 1;
}

static int
heightmap_changeheight(heightmap_t *map, int x, int y, int change, int checkdiffs)
{
    /* sanity check */
    if ((x < 0) || (x >= map->sizex)) return -1;
    if ((y < 0) || (y >= map->sizey)) return -1;

    /* can't be more than MAXHEIGHTDIFFERENCE away from any of it's neighbors */
    if (checkdiffs) {
	if (!heightmap_can_changeheight(map, x, y, change)) {
	    return -1;
	}
    }

    MAPPOS(map,x,y) += change;
    
    if (x == map->sizex-1) {
	MAPPOS(map,x+1,y) = MAPPOS(map,x,y);
    }
    if (y == map->sizey-1) {
	MAPPOS(map,x,y+1) = MAPPOS(map,x,y);
    }
    if ((x == map->sizex-1) && (y == map->sizey-1)) {
	MAPPOS(map,x+1,y+1) = MAPPOS(map,x,y);
    }

    return 0;
}

int heightmap_raise(heightmap_t *map, int x, int y)
{
    return heightmap_changeheight(map, x, y, 1, 1);
}

int heightmap_lower(heightmap_t *map, int x, int y)
{
    return heightmap_changeheight(map, x, y, -1, 1);    
}

int heightmap_setheight(heightmap_t *map, int x, int y, int isinit, int height)
{    
    return heightmap_changeheight(map, x, y, height - MAPPOS(map,x,y), !isinit);
}

int heightmap_islevel(heightsquare_t *square)
{
    if (square->dir.topleft != square->dir.topright) return 0;
    if (square->dir.topleft != square->dir.botleft) return 0;
    if (square->dir.topleft != square->dir.botright) return 0;

    return 1;
}

int heightmap_islevel_spot(heightmap_t *map, int x, int y)
{
    heightsquare_t square;
    int r;

    r = heightmap_getheight(map, x, y, 0, &square);
    if (r) return 0;

    return heightmap_islevel(&square);
}
