/* 
 * 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 "sdlwin.h"
#include "sdl.h"

#include "landvalue.h"
#include "map.h"
#include "utils.h"

#include <SDL_image.h>
#include "SDL_rotozoom.h"

SDL_Surface *sdlscreen = NULL;
SDLFont *myfont = NULL;
static int sdlwin_inited = 0;

int ownercolors[][3] =
    {
	{255, 255, 255},
	{60,  175,  80},
	{255, 255, 0},
	{255, 75,  125},
	{125, 100, 255},
	{255, 0,   255},
    };

static SDL_Surface *
resize_image(SDL_Surface *src, int request_width)
{
	double zoom = (double)request_width/src->w;
	return zoomSurface(src, zoom, zoom, 0);
}

/* Integer math version of SDL_ScaleBlit().
 * Where necessary, a number uses the 16 high bits for the integer
 * and the 16 low bits for the decimal portion.
 *
 * eg:
 * float a = (float) (b >> 16) + (b & 0xFFFF)/65536.0;
 */

static inline Uint32 ifloor(Uint32 i)
{
	return i & 0xFFFF0000;
}

static inline Uint32 iceil(Uint32 i)
{
	return (i & 0xFFFF) ? i : ifloor(i) + (1<<16);
}

static int
set_alpha(int specials, SDL_Surface *image)
{
    int alpha = 255;

    if ((specials & MAPSPECIAL_HILITE) == MAPSPECIAL_HILITE) {
	alpha -= 50;
    }
    if ((specials & MAPSPECIAL_HILITE_INVALID) == MAPSPECIAL_HILITE_INVALID) {
	alpha -= 100;
    }
    if ((specials & MAPSPECIAL_CURSOR) == MAPSPECIAL_CURSOR) {
	alpha -= 100;
    }
    if ((specials & MAPSPECIAL_SELECTED) == MAPSPECIAL_SELECTED) {
	alpha -= 60;
    }

    if (alpha < 100) alpha = 100;

    if (alpha < 255) {
	SDL_SetAlpha(image, SDL_SRCALPHA, alpha);
    }

    return alpha;
}

static void
reset_alpha(int alpha, SDL_Surface *image)
{
    if (alpha < 255) {
	SDL_SetAlpha(image, SDL_SRCALPHA, 255);
    }
}


static void
ShowImg(SDL_Surface *screen, SDL_Surface *image, int x, int y, int specials)
{
    SDL_Rect dest;
    int alpha;

    if (!image) return;

    /* Blit onto the screen surface.
       The surfaces should not be locked at this point.
     */
    dest.x = x;
    dest.y = y;
    dest.w = image->w;
    dest.h = image->h;

    alpha = set_alpha(specials, image);

    SDL_BlitSurface(image, NULL, screen, &dest);

    reset_alpha(alpha, image);
}

void sdlwin_clearscreen(void)
{
    SDL_Rect rect;

    rect.x = 0;
    rect.y = 0;
    rect.w = sdlscreen->w-1;
    rect.h = sdlscreen->h-1;
    SDL_FillRect(sdlscreen, &rect, SDL_MapRGB(sdlscreen->format, 0, 0, 0));    
}

void sdlwin_updatescreen(void)
{
    SDL_Rect dest;
    dest.x = 0;
    dest.y = 0;
    dest.w = sdlscreen->w-1;
    dest.h = sdlscreen->h-1;

    SDL_UpdateRects(sdlscreen, 1, &dest);
}

int sdlwin_init(GtkWidget *widget)
{
    /* Hack to get SDL to use GTK window */
    {
	char SDL_windowhack[32];
	sprintf(SDL_windowhack,"SDL_WINDOWID=%ld",
		GDK_WINDOW_XWINDOW(widget->window));
	putenv(SDL_windowhack);
    }

    /* Initialize SDL */
    if (!sdlwin_inited) {
	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
	    fprintf(stderr,_("Couldn't initialize SDL: %s\n"),SDL_GetError());
	    gtk_main_quit();
	}
    }    

    if (sdlscreen) SDL_FreeSurface(sdlscreen); 
    sdlscreen = SDL_SetVideoMode(widget->allocation.width,
				 widget->allocation.height, 
				 0, SDL_RESIZABLE);
    sdlwin_updatescreen();

    if (!myfont) {
	myfont = load_fixed_font(IMG_DIRECTORY "f_small_green.bmp", 32, 96, 8 );
	if (!myfont) {
	    myfont = load_fixed_font( "../img/f_small_green.bmp", 32, 96, 8 );
	    if (!myfont) {
		printf(_("Unable to load font!\n"));
	    }
	}
    }

    sdlwin_inited = 1;

    return 0;
}

static void
draw_grid_lines(display_t *display, 
		square_t *drawsquare, int owner, map_t *map, int mapx, int mapy)
{
    Uint32 gridlinecolor = -1;

    
    gridlinecolor = SDL_MapRGB(sdlscreen->format,
			       ownercolors[owner][0],
			       ownercolors[owner][1],
			       ownercolors[owner][2]);

    /* draw grid lines */
    DoLine(sdlscreen,
	   drawsquare->parts.leftx, drawsquare->parts.lefty, 
	   drawsquare->parts.topx, drawsquare->parts.topy,
	   gridlinecolor, 255);
    DoLine(sdlscreen,
	   drawsquare->parts.topx, drawsquare->parts.topy, 
	   drawsquare->parts.rightx, drawsquare->parts.righty,
	   gridlinecolor, 255);
    DoLine(sdlscreen,
	   drawsquare->parts.rightx, drawsquare->parts.righty, 
	   drawsquare->parts.botx, drawsquare->parts.boty,
	   gridlinecolor, 255);
    DoLine(sdlscreen,
	   drawsquare->parts.botx, drawsquare->parts.boty, 
	   drawsquare->parts.leftx, drawsquare->parts.lefty,
	   gridlinecolor, 255);

    /*
     * If in owner view display another set of lines closer in
     */
    if ((display->view == GAMEVIEW_OWNERS) && (owner > 0)) {
	if ((mapx == 0) || (map_get_owner(map, mapx-1, mapy) != owner)) {
	    DoLine(sdlscreen,
		   drawsquare->parts.leftx+3, drawsquare->parts.lefty, 
		   drawsquare->parts.topx, drawsquare->parts.topy+2,
		   gridlinecolor, 255);
	    DoLine(sdlscreen,
		   drawsquare->parts.leftx+6, drawsquare->parts.lefty, 
		   drawsquare->parts.topx, drawsquare->parts.topy+4,
		   gridlinecolor, 255);
	}

	if ((mapy == 0) || (map_get_owner(map, mapx, mapy-1) != owner)) {
	    DoLine(sdlscreen,
		   drawsquare->parts.topx, drawsquare->parts.topy+2, 
		   drawsquare->parts.rightx-3, drawsquare->parts.righty,
		   gridlinecolor, 255);
	    DoLine(sdlscreen,
		   drawsquare->parts.topx, drawsquare->parts.topy+4, 
		   drawsquare->parts.rightx-6, drawsquare->parts.righty,
		   gridlinecolor, 255);
	}

	if ((mapx == map_get_sizex(map)-1) || (map_get_owner(map, mapx+1, mapy) != owner)) {
	    DoLine(sdlscreen,
		   drawsquare->parts.rightx-3, drawsquare->parts.righty, 
		   drawsquare->parts.botx, drawsquare->parts.boty-2,
		   gridlinecolor, 255);
	    DoLine(sdlscreen,
		   drawsquare->parts.rightx-6, drawsquare->parts.righty, 
		   drawsquare->parts.botx, drawsquare->parts.boty-4,
		   gridlinecolor, 255);
	}

	if ((mapy == map_get_sizey(map)-1) || (map_get_owner(map, mapx, mapy+1) != owner)) {
	    DoLine(sdlscreen,
		   drawsquare->parts.botx, drawsquare->parts.boty-2, 
		   drawsquare->parts.leftx+3, drawsquare->parts.lefty,
		   gridlinecolor, 255);
	    DoLine(sdlscreen,
		   drawsquare->parts.botx, drawsquare->parts.boty-4, 
		   drawsquare->parts.leftx+6, drawsquare->parts.lefty,
		   gridlinecolor, 255);
	}
    }
}

static void
fillsquare(int r, int g, int b, int a, square_t *drawsquare)
{
    Uint32 color;
    
    color = SDL_MapRGBA(sdlscreen->format,
		       r, g, b, a);
    
    DoFilledPolygon (sdlscreen,
		     drawsquare->parts.leftx,  drawsquare->parts.lefty,
		     drawsquare->parts.topx,   drawsquare->parts.topy,
		     drawsquare->parts.rightx, drawsquare->parts.righty,
		     drawsquare->parts.botx,   drawsquare->parts.boty,
		     color, a);
}

static void
ShowPattern(SDL_Surface *screen, SDL_Surface *pattern, square_t *drawsquare,
	    int specials)
{
    int alpha;

    alpha = set_alpha(specials, pattern);

    DoPattern (sdlscreen,
	       drawsquare->parts.leftx,  drawsquare->parts.lefty,
	       drawsquare->parts.topx,   drawsquare->parts.topy,
	       drawsquare->parts.rightx, drawsquare->parts.righty,
	       drawsquare->parts.botx,   drawsquare->parts.boty,
	       pattern);

    reset_alpha(alpha, pattern);
}

#if 0
static void
ShowSolid(SDL_Surface *screen, int r, int g, int b,
	  square_t *square, int specials)
{
    int alpha = set_alpha(specials, sdlscreen);
    
    fillsquare(r, g, b, alpha, square);	
    
    reset_alpha(alpha, sdlscreen);
}
#endif /* 0 */

static void
ShowSolid_minus(SDL_Surface *screen, int r, int g, int b,
		square_t *square, int minus, int specials)
{
    int alpha = set_alpha(specials, sdlscreen);
    square_t sq2;

    sq2.parts.topx   = square->parts.topx;
    sq2.parts.rightx = square->parts.rightx - minus;
    sq2.parts.leftx  = square->parts.leftx + minus;
    sq2.parts.botx   = square->parts.botx;

    sq2.parts.topy   = square->parts.topy + minus;
    sq2.parts.righty = square->parts.righty;
    sq2.parts.lefty  = square->parts.lefty;
    sq2.parts.boty   = square->parts.boty - minus;

    fillsquare(r, g, b, alpha, &sq2);	
    
    reset_alpha(alpha, sdlscreen);
}

static void
show_tile(display_t *display, tiles_t *tiles, screen_t *screen, mapobj_t mapobj,
	  int x, int y, int sx, int sy,
	  surrounding_t *surrounding,
	  heightsquare_t *heightsquare,
	  square_t *drawsquare,
	  int dirview, int zoom, int specials)
{
    tile_t *tile;
    int tilesizex;
    int tilesizey;
    int tileheight;

    screen_tilesizes(screen, &tilesizex, &tilesizey, &tileheight);

    if (!display->showbuildings) {

#if 0 /* xxx */
	switch (map_item_gettype(mapobj)) 
	    {
	    case MAPTYPE_GOVERNMENT:
	    case MAPTYPE_EMPTY:
	    case MAPTYPE_ENTERTAINMENT:
		/* don't change */
		break;
	    case MAPTYPE_HOUSE:
		mapobj = MAPOBJ_ZONE_HOUSE;
		break;
	    case MAPTYPE_COMMERCIAL:
		mapobj = MAPOBJ_ZONE_COMMERCIAL;
		break;
	    case MAPTYPE_OFFICE:
		mapobj = MAPOBJ_ZONE_OFFICE;
		break;
	    case MAPTYPE_INDUSTRIAL:
		mapobj = MAPOBJ_ZONE_INDUSTRIAL;
		break;
	    case MAPTYPE_FARM:
		mapobj = MAPOBJ_ZONE_FARM;
		break;
	    }
#endif /* 0 */
    }


    if (tiles_get(tiles, mapobj, surrounding, heightsquare, &tile) == 0) {
	void *image = tile_image(tiles, tile, dirview, zoom);
	
	if (image) {
	    if (tile->ispattern) {
		ShowPattern(sdlscreen, image, drawsquare, specials);
	    } else {
		ShowImg(sdlscreen, image,
			x - ((sy-1)*tilesizex/2), y - heightsquare->dir.topleft*tileheight - tile->extray[dirview][zoom] - (sx-1+sy-1)*tilesizey/2,
			specials);
	    }
	} else {
	    ShowSolid_minus(sdlscreen, tile->color_r, tile->color_g, tile->color_b, 
			    drawsquare, 1, specials);
	}
    } else {
	printf(_("unable to get tile! obj = %d\n"), mapobj);
    }
}

static void
display_land_value(map_t *map, tiles_t *tiles, int mapx, int mapy, square_t *drawsquare,
		   int zoom, int owner, int activity)
{
    int value;
    char str[10];
    int strheight = 0;
    int strwidth;

    if (zoom >= 2) return;

    switch (activity) 
	{
	case MAPOBJ_ACTION_BUYLAND:
	    if (!map_empty_land(map, mapx, mapy)) return;
	    /* fallthru */
	case MAPOBJ_ACTION_BUYALL:
	    if (owner != NO_OWNER) return;
	    break;
	case MAPOBJ_ACTION_SELLLAND:
	    if (!map_empty_land(map, mapx, mapy)) return;
	    /* fallthru */
	case MAPOBJ_ACTION_SELLALL:
	    if (owner == NO_OWNER) return;
	    break;

	default:
	    return;	    
    }

    value = government_calculate_onelandvalue(map, tiles, mapx, mapy);

    snprintf(str, sizeof(str),"$%d",value);

    strwidth = text_width(myfont, str);
    strheight = 12; /* xxx */
	    
    write_text(myfont, sdlscreen, 
	       (drawsquare->parts.leftx + drawsquare->parts.rightx)/2 - strwidth/2,
	       (drawsquare->parts.lefty + drawsquare->parts.righty)/2 - strheight/2,
	       str, OPAQUE);    
}

void
sdlwin_draw_spot(display_t *display,
		 map_t *map, player_t *me, screen_t *screen, tiles_t *tiles, 
		 int screen_moved, int mapx, int mapy, int x, int y)
{
    square_t drawsquare;
    mapobj_t mapobj;
    surrounding_t surrounding;
    heightsquare_t heightsquare;
    int specials;
    int owner = -1;
    int zoom = screen_getzoom(screen);
    int screen_dirview = screen_getdirview(screen);
    int tilesizex;
    int tilesizey;
    int tileheight;
    int sx;
    int sy;
    int i;
    int j;
    int topx;
    int topy;
    int xoffset;
    
    screen_tilesizes(screen, &tilesizex, &tilesizey, &tileheight);

    /*
     * Get info on this spot
     */
    mapobj = map_get_info(map, mapx, mapy, 
			  screen_dirview, &surrounding, &heightsquare, 
			  &specials, me, &owner);

    /*
     * If tile is bigger than 1x1 do the right thing
     */
    tiles_getsize(tiles, mapobj, &sx, &sy);

    topx = mapx;
    topy = mapy;
    map_find_tiletop(map, &topx, &topy);

    switch(screen_dirview) 
	{
	case 0:
	    if ((mapx != topx+sx-1) ||
		(mapy != topy+sy-1)) {
		return;
	    }
	    break;
	case 1:
	    if ((mapx != topx+sx-1) ||
		(mapy != topy)) {
		return;
	    }
	    break;
	case 2:
	    if ((mapx != topx) ||
		(mapy != topy)) {
		return;
	    }
	    break;
	case 3:
	    if ((mapx != topx) ||
		(mapy != topy+sy-1)) {
		return;
	    }
	    break;
    }
    
    /*
     * Figure out where we're drawing
     */
    xoffset = (sy - sx)*tilesizex;    
    drawsquare.parts.topx   = x + xoffset + tilesizex/2;
    drawsquare.parts.rightx = x + xoffset + tilesizex*(1+sx)/2;
    drawsquare.parts.leftx  = x + xoffset - tilesizex*(sy-1)/2;
    drawsquare.parts.botx   = x + xoffset + tilesizex*(1+sx-sy)/2;

    drawsquare.parts.topy   = y +  0 - heightsquare.dir.topleft*tileheight;

    drawsquare.parts.righty = y + tilesizey*sx/2 - heightsquare.dir.topright*tileheight;

    drawsquare.parts.lefty  = y + tilesizey*sy/2 - heightsquare.dir.botleft*tileheight;

    drawsquare.parts.boty   = y + tilesizey*(sx+sy)/2 - heightsquare.dir.botright*tileheight;

    /*
     * xxx
     */
    if (screen_moved) {
	screen_viewarea_set(screen, mapx, mapy, &drawsquare);
    }

    /*
     * If highlighted check if can really place that there
     */
    if (display->activity == MAPOBJ_ACTION_FLATTEN) {
	if (specials & MAPSPECIAL_HILITE_INVALID) {
	    fillsquare(255, 0, 0, 255,
		       &drawsquare);	    
	} else if ((specials) ||
	    (mapspot_is_in_list(display->height_change_list, mapx, mapy))) {
	    fillsquare(0, 0, 0, 255,
		       &drawsquare);
	}

    } else if (specials) {

	if (specials & MAPSPECIAL_HILITE_INVALID) {
	    fillsquare(255, 0, 0, 255,
		       &drawsquare);
	} else {
	    fillsquare(0, 0, 0, 255,
		       &drawsquare);
	}
    }

    if ((display->grid) && (zoom < 3)) {
	draw_grid_lines(display, &drawsquare, owner, map, mapx, mapy);
    }

    switch (display->view) 
	{
	case GAMEVIEW_OWNERS:
	    ShowSolid_minus(sdlscreen,
		      ownercolors[owner][0],
		      ownercolors[owner][1],
		      ownercolors[owner][2],
		      &drawsquare, 1, specials);
	    break;
	case GAMEVIEW_MINE:
	    if (owner == player_getnum(me)) {
		show_tile(display, tiles, screen, mapobj, x, y, sx, sy, 
			  &surrounding, &heightsquare, &drawsquare, 
			  screen_dirview, zoom, specials);
	    } else {
		ShowSolid_minus(sdlscreen,
				170, 170, 170, &drawsquare, 1, specials);
	    }
	    break;
	case GAMEVIEW_NORMAL:
	    show_tile(display, tiles, screen, mapobj, x, y, sx, sy, 
	      &surrounding, &heightsquare, &drawsquare, 
		      screen_dirview, zoom, specials);
	    break;
	case GAMEVIEW_POLICE_COVER:
	case GAMEVIEW_HOSPITAL_COVER:
	case GAMEVIEW_FIRE_COVER:
	    if (map_item_gettype(mapobj) == display->viewtype) {
		ShowSolid_minus(sdlscreen,
				10, 10, 10, &drawsquare, 1, specials);
	    } else if (mapspot_within_range(display->objlist, mapx, mapy, map, tiles)) {
		ShowSolid_minus(sdlscreen,
				150, 150, 150, &drawsquare, 1, specials);
	    } else {
		show_tile(display, tiles, screen, mapobj, x, y, sx, sy, 
			  &surrounding, &heightsquare, &drawsquare, 
			  screen_dirview, zoom, specials);
	    }
	    break;
	}


    /*
     * Draw land value if buying land
     */
    display_land_value(map, tiles, mapx, mapy, &drawsquare, zoom, owner, display->activity);

}

void *sdlwin_loadimage(char *filename, 
		       int request_width, int request_height,
		       int *extra_height, 
		       void *rock)
{
    SDL_Surface *buf;
    SDL_Surface *image;

    buf = IMG_Load(filename);
    if (!buf) {
	printf(_("Error loading %s\n"), filename);
	return NULL;
    }

    if (request_width != buf->w) {
	image = resize_image(buf, request_width);
	SDL_FreeSurface(buf);
    } else {
	image = buf;
    }

    *extra_height = image->h - request_height;

/*
    image = SDL_DisplayFormat(image);
*/

    /* Set colorkey for non-opaque tiles */
    if (strstr(filename, "png")) {
/*
    SDL_SetColorKey(image, 
		    SDL_SRCCOLORKEY|SDL_RLEACCEL,
		    SDL_MapRGBA(image->format, 0, 0, 0, 0));
*/		    
    } else {
    SDL_SetColorKey(image, 
		    SDL_SRCCOLORKEY|SDL_RLEACCEL,
		    SDL_MapRGBA(image->format, 255, 255, 255, 255));
    }

    return image;
}

