/* the map window
 *
 * $Id: g2map.c,v 1.1.1.1 2004/06/23 02:01:44 miq Exp $
 *
 */
#include <stdio.h>
#include <math.h>

#include "g2map.h"
#include "g2main.h"
#include "g2marsh.h"
#include "tile2x11.h"
#include "hack.h"


#ifndef ROWNO
#define ROWNO 21
#endif

#ifndef COLNO
#define COLNO 80
#endif

/* XPM */
const char *pet_mark_xpm[] = {
/* width height ncolors chars_per_pixel */
"8 7 2 1",
/* colors */
". c None",
"  c #FF0000",
/* pixels */
"........",
"..  .  .",
".       ",
".       ",
"..     .",
"...   ..",
".... ..."
};

static GdkColor cursColor = {
	200 * 255,
	200 * 255,
	200 * 255,
};

GdkPixbuf **tiles;
GdkPixbuf *petMark;
static GtkVBoxClass *parent_class;
static gint g2_map_signals[5];

extern short glyph2tile[];      /* from tile.c */
static void draw_background(G2Map *map);
static void g2_map_class_init(G2MapClass * class);
static void g2_map_init(G2Map * map);
static void init_tiles(G2Map * map);
static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event,
                             gpointer data);

static const GTypeInfo g2_map_info = {
    sizeof(G2MapClass),
    NULL,                       /* base_init */
    NULL,                       /* base_finalize */
    (GClassInitFunc) g2_map_class_init,
    NULL,                       /* class_finalize */
    NULL,                       /* class_data */
    sizeof(G2Map),
    0,                          /* n_preallocs */
    (GInstanceInitFunc) g2_map_init
};

guint g2_map_get_type()
{
    static GType g2_map_type = 0;

    if (g2_map_type == 0) {
        g2_map_type = g_type_register_static(GTK_TYPE_VBOX,
                                             "G2Map", &g2_map_info, 0);
    }
    return g2_map_type;
}

static void g2_map_class_init(G2MapClass * class)
{
    parent_class = gtk_type_class(gtk_vbox_get_type());

    g2_map_signals[0] =
        g_signal_new("print_glyph",
                     G_OBJECT_CLASS_TYPE(class),
                     G_SIGNAL_RUN_FIRST,
                     G_STRUCT_OFFSET(G2MapClass, g2_map_print_glyph),
                     NULL, NULL,
                     g2_marshal_VOID__INT_INT_INT, G_TYPE_NONE, 3, G_TYPE_INT,
                     G_TYPE_INT, G_TYPE_INT);
    g2_map_signals[1] =
        g_signal_new("clear",
                     G_OBJECT_CLASS_TYPE(class),
                     G_SIGNAL_RUN_FIRST,
                     G_STRUCT_OFFSET(G2MapClass, g2_map_clear),
                     NULL, NULL, gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
    g2_map_signals[2] =
        g_signal_new("cliparound",
                     G_OBJECT_CLASS_TYPE(class),
                     G_SIGNAL_RUN_FIRST,
                     G_STRUCT_OFFSET(G2MapClass, g2_map_cliparound),
                     NULL, NULL, gtk_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
                     G_TYPE_INT, G_TYPE_INT);
    g2_map_signals[3] =
        g_signal_new("curs",
                     G_OBJECT_CLASS_TYPE(class),
                     G_SIGNAL_RUN_FIRST,
                     G_STRUCT_OFFSET(G2MapClass, g2_map_curs),
                     NULL, NULL, gtk_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
                     G_TYPE_INT, G_TYPE_INT);
    g2_map_signals[4] =
        g_signal_new("display",
                     G_OBJECT_CLASS_TYPE(class),
                     G_SIGNAL_RUN_FIRST,
                     G_STRUCT_OFFSET(G2MapClass, g2_map_display),
                     NULL, NULL, gtk_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1,
                     G_TYPE_BOOLEAN);
}


static gboolean expose_event(GtkWidget * canvas, GdkEventExpose * event,
                             gpointer data)
{
    G2Map * map = G2_MAP(data);
    
    /* XXX: when the visible area becomes bigger than the backbuffer, copy the
     * complete backbuffer to the screen. We should optimize this...
     */
    if (!map->gc) {
        map->gc = gdk_gc_new(map->canvas->window);
        gdk_gc_set_rgb_fg_color(map->gc, &cursColor);
    }
    if (event->area.x + event->area.width > map->mapWidth ||
        event->area.y + event->area.height > map->mapHeight) {
		gdk_draw_drawable(canvas->window, canvas->style->white_gc,
                          map->backBuffer, 0, 0, 0, 0, map->mapWidth, map->mapHeight);
    } else {
        gdk_draw_drawable(canvas->window, canvas->style->white_gc,
                          map->backBuffer, event->area.x, event->area.y,
                                      event->area.x, event->area.y,
                                      event->area.width, event->area.height);
    }
/*    gdk_draw_rectangle(canvas->window, map->gc, FALSE, map->cursorX * tileSize,
                       map->cursorY * tileSize, tileSize - 1, tileSize - 1);*/
    gdk_draw_rectangle(canvas->window, canvas->style->white_gc, FALSE, map->cursorX * tileSize,
                       map->cursorY * tileSize, tileSize - 1, tileSize - 1);
    return TRUE;
}

static void g2_map_display(GtkWidget * win, gboolean block, gpointer gp)
{
    gtk_widget_show_all(win);
    gtk_widget_grab_focus(win);
}

static void g2_map_clear(GtkWidget * win, gpointer gp)
{
    draw_background(G2_MAP(win));
    gtk_widget_queue_draw_area(G2_MAP(win)->canvas, 0, 0, G2_MAP(win)->mapWidth,
                               G2_MAP(win)->mapHeight);
}

static void g2_map_print_glyph(GtkWidget * win, int x, int y, int glyph,
                               gpointer gp)
{
	G2Map *map = G2_MAP(win);
	
    gint tile = glyph2tile[glyph];

    gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,tiles[tile],
			0, 0, x * tileSize, y * tileSize, tileSize, tileSize,
			GDK_RGB_DITHER_NONE, 0, 0);
	if (glyph_is_pet(glyph) && iflags.hilite_pet) {
		gint width;
		gint height;
		width = gdk_pixbuf_get_width (petMark);
  		height = gdk_pixbuf_get_height (petMark);
    	gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,	petMark,
			0, 0, x * tileSize, y * tileSize, width, height,
			GDK_RGB_DITHER_NONE, 0, 0);
	}
    gtk_widget_queue_draw_area(map->canvas, x * tileSize,
                               y * tileSize, tileSize, tileSize);
}

static void g2_map_cliparound(GtkWidget * win, int x, int y, gpointer gp)
{
    G2Map * map = G2_MAP(win);
    
    GtkAdjustment *vAdj =
        gtk_viewport_get_vadjustment(GTK_VIEWPORT(map->mapWin));
    GtkAdjustment *hAdj =
        gtk_viewport_get_hadjustment(GTK_VIEWPORT(map->mapWin));
    gint height = map->mapWin->allocation.height;
	gint width = map->mapWin->allocation.width;
	
    gint requestedCenterX = x * tileSize + tileSize / 2;
    gint requestedCenterY = y * tileSize + tileSize / 2;

    if (requestedCenterX <= width / 2) {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(hAdj), 0.0f);
    } else if (requestedCenterX >= map->mapWidth - width / 2) {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(hAdj),
                                 hAdj->upper - hAdj->page_size);
    } else {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(hAdj),
                                 requestedCenterX - width / 2);
    }
    if (requestedCenterY <= height / 2) {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(vAdj), 0.0f);
    } else if (requestedCenterY >= map->mapHeight - height / 2) {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(vAdj),
                                 vAdj->upper - vAdj->page_size);
    } else {
        gtk_adjustment_set_value(GTK_ADJUSTMENT(vAdj),
                                 requestedCenterY - height / 2);
    }
}

static void g2_map_curs(GtkWidget * win, int x, int y, gpointer gp)
{
    G2Map * map = G2_MAP(win);
    
    /*g_print("Map-curs: x=%d, y=%d\n", x, y);*/
    /* refresh last cursor position from backbuffer */
    gtk_widget_queue_draw_area(map->canvas, map->cursorX * tileSize,
                               map->cursorY * tileSize, tileSize, tileSize);
    map->cursorX = x;
    map->cursorY = y;
    /* schedule new position for refresh */
    gtk_widget_queue_draw_area(map->canvas, map->cursorX * tileSize,
                           map->cursorY * tileSize, tileSize, tileSize);
}

static gboolean g2_map_button_press_event(GtkWidget *widget, GdkEventButton *event,
                                          gpointer data)
{
    G2Map *map = G2_MAP(widget);

    int tilePosX;
    int tilePosY;
    GtkAdjustment *vAdj =
        gtk_viewport_get_vadjustment(GTK_VIEWPORT(map->mapWin));
    GtkAdjustment *hAdj =
        gtk_viewport_get_hadjustment(GTK_VIEWPORT(map->mapWin));
    gdouble xOffset = gtk_adjustment_get_value(hAdj);
    gdouble yOffset = gtk_adjustment_get_value(vAdj);
    
    tilePosX = (int) ((event->x + xOffset) / (gdouble) tileSize);
    tilePosY = (int) ((event->y + yOffset) / (gdouble) tileSize);
    if (event->button == 1) {
        g_print("Button 1 pressed: x=%d, y=%d\n", tilePosX, tilePosY);
    }

    return TRUE;
}

static void draw_background(G2Map *map)
{
    gint x;
    gint y;
    gint backWidth;
    gint backHeight;

    backWidth = gdk_pixbuf_get_width(map->bg);
    backHeight = gdk_pixbuf_get_height(map->bg);
    /* tile the background images on the back buffer */
    for (x = 0; x < map->mapWidth - backWidth; x += backWidth) {
        for (y = 0; y < map->mapHeight - backHeight; y += backHeight) {
    		gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,map->bg,
					0, 0, x, y, backWidth, backHeight, GDK_RGB_DITHER_NONE, 0, 0);
        }
		gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,map->bg,
				0, 0, x, y, backWidth, map->mapHeight - y, GDK_RGB_DITHER_NONE, 0, 0);
    }
    /* the last column */
    for (y = 0; y < map->mapHeight - backHeight; y += backHeight) {
		gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,map->bg,
				0, 0, x, y, map->mapWidth -x, backHeight, GDK_RGB_DITHER_NONE, 0, 0);
    }
	gdk_draw_pixbuf(map->backBuffer, map->canvas->style->white_gc,map->bg,
			0, 0, x, y, map->mapWidth - x, map->mapHeight - y, GDK_RGB_DITHER_NONE, 0, 0);
}

static void init_tiles(G2Map *map)
{
    gint i;
    gint srcX;
    gint srcY;
	GError *err = NULL;
	GdkColor white;
	white.red = 65535;
	white.green = 65535;
	white.blue = 65535;

    map->tileSet = gdk_pixbuf_new_from_file("x11tiles", &err);
    if (!map->tileSet) {
        fprintf(stderr, "Error loading tile set:%s\n", err->message);
    }
    g_print("Tiles per row %d\n", TILES_PER_ROW);
    tileSize = gdk_pixbuf_get_width(map->tileSet) / TILES_PER_ROW;
    g_print("Tilesize: %d\n", tileSize);
    tileCount = TILES_PER_ROW * (gdk_pixbuf_get_height(map->tileSet) / tileSize);
    g_print("Tilecount: %d\n", tileCount);
    tiles = g_new0(GdkPixbuf *, tileCount);
    for (i = 0; i < tileCount; i++) {
        srcX = (i % TILES_PER_ROW) * tileSize;
        srcY = (i / TILES_PER_ROW) * tileSize;
        tiles[i] =
            gdk_pixbuf_new_subpixbuf(map->tileSet, srcX, srcY, tileSize, tileSize);
    }
}

GdkPixbuf *g2_get_tile(gint glyph)
{
    return tiles[glyph2tile[glyph]];
}

static void g2_map_init(G2Map * map)
{
	GError *err = NULL;
	GtkWidget *hscroll;
	GtkWidget *vscroll;
	GdkVisual *visual;

    map->bg = gdk_pixbuf_new_from_file("mapbg.xpm", &err);
    if (!map->bg) {
        fprintf(stderr, "Error loading bg:%s\n", err->message);
    }

    init_tiles(map);
    map->canvas = gtk_drawing_area_new();
	gtk_widget_set_double_buffered(map->canvas, FALSE);
    map->mapWidth = COLNO * tileSize;
    map->mapHeight = ROWNO * tileSize;
    gtk_widget_set_size_request(map->canvas, map->mapWidth, map->mapHeight);
    map->mapWin = gtk_viewport_new(NULL, NULL);
	gtk_viewport_set_shadow_type(GTK_VIEWPORT(map->mapWin), GTK_SHADOW_NONE);
	gtk_widget_set_size_request(map->mapWin, 500, ROWNO * tileSize);
	hscroll = gtk_hscrollbar_new(gtk_viewport_get_hadjustment(GTK_VIEWPORT(map->mapWin)));
    /* XXX: determine depth somehow differently */
	visual = gdk_visual_get_system();

	map->backBuffer = gdk_pixmap_new(NULL, map->mapWidth, map->mapHeight, visual->depth);
    gtk_container_add(GTK_CONTAINER(map->mapWin), map->canvas);
    gtk_box_pack_start(GTK_BOX(map), map->mapWin, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(map), hscroll, FALSE, FALSE, 0);
    g_signal_connect(G_OBJECT(map->canvas), "expose_event",
                     G_CALLBACK(expose_event), (gpointer) map);
}

GtkWidget *g2_map_new()
{
    G2Map *g2Map;

    g2Map = G2_MAP(g_object_new(TYPE_G2_MAP, NULL));
    draw_background(g2Map);
	petMark = gdk_pixbuf_new_from_xpm_data(pet_mark_xpm);
    g_signal_connect(G_OBJECT(g2Map), "print_glyph",
                     G_CALLBACK(g2_map_print_glyph), NULL);
    g_signal_connect(G_OBJECT(g2Map), "clear", G_CALLBACK(g2_map_clear), NULL);
    g_signal_connect(G_OBJECT(g2Map), "cliparound",
                     G_CALLBACK(g2_map_cliparound), NULL);
    g_signal_connect(G_OBJECT(g2Map), "curs", G_CALLBACK(g2_map_curs), NULL);
    g_signal_connect(G_OBJECT(g2Map), "display", G_CALLBACK(g2_map_display), NULL);
    g_signal_connect(G_OBJECT(g2Map), "button_press_event",
                     G_CALLBACK(g2_map_button_press_event), NULL);
    gtk_widget_set_events(GTK_WIDGET(g2Map), gtk_widget_get_events(GTK_WIDGET(g2Map)) |
                    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
    return GTK_WIDGET(g2Map);
}
