/*	SCCS Id: @(#)gnmap.c	3.4	2000/07/16	*/
/* Copyright (C) 1998 by Erik Andersen <andersee@debian.org> */
/* Copyright (C) 1998 by Anthony Taylor <tonyt@ptialaska.net> */
/* NetHack may be freely redistributed.  See license for details. */

#include "gnmap.h"
#include "gnglyph.h"
#include "gnsignal.h"
#include "hack.h"

#ifndef ROWNO
#define ROWNO 21
#define COLNO 80
#endif

/* globals static to this file go here */
struct {
  GnomeCanvas       *canvas;
  GnomeCanvasImage  *map[(ROWNO + 1) * COLNO];
  GnomeCanvasImage  *overlay[(ROWNO + 1) * COLNO];
  double            zoom;
  GtkWidget         *frame;
} ghack_map;
  
static GdkImlibImage *background;
static GdkImlibImage *petmark;
static GnomeCanvasGroup *myCanvasGroup;

/* static function declarations -- local to this file go here */
void ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data); 
void ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data);
void ghack_map_print_glyph( GtkObject *win, guint x, guint y, GdkImlibImage *im, gpointer data);
void ghack_map_clear( GtkWidget *win, gpointer data);
static void ghack_map_display( GtkWidget *win, boolean block, gpointer data);
static void ghack_map_cliparound( GtkWidget *win, int x, int y, gpointer data);
static void ghack_map_window_zoom( GtkAdjustment *adj, gpointer data);


/* The following XPM is the artwork of Warwick Allison
 * <warwick@troll.no>.  It has been borrowed from 
 * the most excellent NetHackQt, until such time as
 * we can come up with something better.
 *
 * More information about NetHackQt can be had from:
 * http://www.troll.no/~warwick/nethack/
 */

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


/* NAME: 
 *     ghack_init_map_window( )
 *
 * ARGUMENTS:
 *     NONE
 *
 * RETURNS:
 *     GtkWidget*
 *
 * PURPOSE:
 *     Create the basic map necessities.  Create a canvas;
 *     give it a background.  Attach all the right signals
 *     to all the right places.  Generally prepare the map
 *     to behave properly.
*/

GtkWidget*
ghack_init_map_window ( )
{
  GtkWidget        *vbox;
  GtkWidget        *hbox;
  GtkWidget        *table;
  GtkWidget        *frame;
  GtkWidget        *w;
  GtkWidget	   *hSeparator;
  GtkAdjustment    *adj;
  GnomeCanvasImage  *bg;
  double width, height, x, y;
  int i;

  width = COLNO * ghack_glyph_width();
  height = ROWNO * ghack_glyph_height();

  vbox = gtk_vbox_new (FALSE, 4);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  gtk_widget_show (vbox);
  
  /* Add in a horiz seperator */
  hSeparator = gtk_hseparator_new ();
  gtk_box_pack_start (GTK_BOX (vbox), hSeparator, FALSE, FALSE, 2);
  gtk_widget_show ( hSeparator);
  
  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  /* Create the Zoom spinbutton.
  */
  ghack_map.zoom = 1.0;
  w = gtk_label_new ("Zoom:");
  gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
  gtk_widget_show (w);
  adj = GTK_ADJUSTMENT (gtk_adjustment_new (1.00, 0.5, 3.00, 0.05, 0.50, 0.50));
  w = gtk_spin_button_new (adj, 0.5, 2);
  gtk_widget_set_usize (w, 50, 0);
  gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
  gtk_widget_show (w);

  /* Canvas and scrollbars
  */
  gtk_widget_push_visual (gdk_imlib_get_visual ());
  gtk_widget_push_colormap (gdk_imlib_get_colormap ());
  ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new());
  //gtk_widget_push_visual(gdk_rgb_get_visual());
  //gtk_widget_push_colormap(gdk_rgb_get_cmap());
  //ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new_aa());

  gtk_widget_pop_colormap();
  gtk_widget_pop_visual();
  gtk_widget_show (GTK_WIDGET(ghack_map.canvas));

  table = gtk_table_new (2, 2, FALSE);
  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
  gtk_widget_show (table);
  
  frame = gtk_frame_new (NULL);
  ghack_map.frame = frame;
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_table_attach (GTK_TABLE (table), frame,
		    0, 1, 0, 1,
		    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
		    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
		    0, 0);
  gtk_widget_show (frame);

  gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET(ghack_map.canvas));
  gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0, 
		    width+2*ghack_glyph_width(), height+2*ghack_glyph_height());

  gnome_canvas_set_pixels_per_unit (GNOME_CANVAS(ghack_map.canvas), 1.0);

  w = gtk_hscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->hadjustment);
  gtk_table_attach (GTK_TABLE (table), w,
		    0, 1, 1, 2,
		    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
		    GTK_FILL,
		    0, 0);
  gtk_widget_show (w);

  w = gtk_vscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->vadjustment);
  gtk_table_attach (GTK_TABLE (table), w,
		    1, 2, 0, 1, GTK_FILL,
		    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
		    0, 0);
  gtk_widget_show (w); 
  
  myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new ( 
		    gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)), 
		    gnome_canvas_group_get_type (), 
		    "x", 0.0, 
		    "y", 0.0, 
		    NULL) );

  /* Tile the map background with a pretty image */ 
  background = gdk_imlib_load_image((char *) "mapbg.xpm");
  if (background == NULL) {
      g_warning("Bummer! Failed to load the map background image (mapbg.xpm)!");
  }
  else {
    gdk_imlib_render(background, background->rgb_width,
	  background->rgb_height);

    /* Tile the map background */
    for (y = 0; y < height+background->rgb_height; y+=background->rgb_height)
    {
      for (x = 0; x < width+background->rgb_width; x+=background->rgb_width)
	{
	  bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new (
		      myCanvasGroup, gnome_canvas_image_get_type (),
		      "x",      (double) x,
		      "y",      (double) y,
		      "width",  (double) background->rgb_width,
		      "height", (double) background->rgb_height,
		      "image",  background,
		      "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER,
		      NULL) );
	  gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg));
	}
    }
  }

  /* ghack_map.map is an array of canvas images.  Each cell of
   * the array will contain one tile.  Here, we create the
   * space for the cells and then create the cells for easy
   * access later.
  */
  for (i=0, y = 0; y < height; y+=ghack_glyph_height())
    {
      for (x = 0; x < width; x+=ghack_glyph_width())
	{
	  ghack_map.map[i++] = GNOME_CANVAS_IMAGE( 
		  gnome_canvas_item_new (
		      myCanvasGroup,
		      gnome_canvas_image_get_type (),
		      "x",      (double) x,
		      "y",      (double) y,
		      "width",  (double) ghack_glyph_height(),
		      "height", (double) ghack_glyph_width(),
		      "anchor", GTK_ANCHOR_NORTH_WEST,
		      NULL) );
	}
    }

   /* Set up the pet mark image */
  petmark = gdk_imlib_create_image_from_xpm_data( pet_mark_xpm);
  if (petmark == NULL) {
    g_warning("Bummer! Failed to load the pet_mark image!");
  }
  else {
      gdk_imlib_render(petmark, petmark->rgb_width,
	  petmark->rgb_height);

      /* ghack_map.overlay is an array of canvas images used to
       * overlay tile images...
       */
      for (i=0, y = 0; y < height; y+=ghack_glyph_height())
	{
	  for (x = 0; x < width; x+=ghack_glyph_width())
	    {
	      ghack_map.overlay[i] = GNOME_CANVAS_IMAGE( 
		      gnome_canvas_item_new (
			  myCanvasGroup,
			  gnome_canvas_image_get_type (),
			  "x",      (double) x,
			  "y",      (double) y,
			  "width",  (double) petmark->rgb_width,
			  "height", (double) petmark->rgb_height,
			  "image",  petmark,
			  "anchor", GTK_ANCHOR_NORTH_WEST,
			  NULL) );
	      gnome_canvas_item_lower_to_bottom (
		      GNOME_CANVAS_ITEM( ghack_map.overlay[i++]));
	    }
	}
  }

  /* Resize the canvas when the spinbutton changes
  */
  gtk_signal_connect (GTK_OBJECT (adj),
		      "value_changed",
		      (GtkSignalFunc) ghack_map_window_zoom,
		      ghack_map.canvas);

  /* Game signals
  */
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_curs",
		      GTK_SIGNAL_FUNC (ghack_map_cursor_to),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_putstr",
		      GTK_SIGNAL_FUNC (ghack_map_putstr),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_print_glyph",
		      GTK_SIGNAL_FUNC (ghack_map_print_glyph),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_clear",
		      GTK_SIGNAL_FUNC (ghack_map_clear),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_display",
		      GTK_SIGNAL_FUNC (ghack_map_display),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (vbox),
		      "ghack_cliparound",
		      GTK_SIGNAL_FUNC (ghack_map_cliparound),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (ghack_map.canvas),
		      "button_press_event",
		      GTK_SIGNAL_FUNC (ghack_handle_button_press),
		      NULL);
  gtk_signal_connect(GTK_OBJECT (ghack_map.canvas), 
	              "gnome_delay_output",
                      GTK_SIGNAL_FUNC(ghack_delay), 
		      NULL);
  
  return GTK_WIDGET(vbox);
}


/* NAME: 
 *     ghack_map_window_zoom
 *
 * ARGUMENTS:
 *     double     zoom -- The zoom factor
 *
 * RETURNS:
 *     Nothing.
 *
 * PURPOSE:
 *     Zoom the map image in and out.  This should allow the user to
 *     dynamically scale the map.  Ideally, the background should
 *     *NOT* scale, but this may be impractical.
*/

static void
ghack_map_window_zoom( GtkAdjustment *adj, gpointer data)
{
  if ( adj->value > 3.0 ) 
      adj->value = 3.0;
  if ( adj->value < 0.5 )
      adj->value = 0.5;
  ghack_map.zoom = adj->value;
  gnome_canvas_set_pixels_per_unit (data, adj->value);
}



void
ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data)
{
  GnomeCanvasGroup *group;
  static GnomeCanvasRE *cursor = NULL;

  double x1, y1, x2, y2;

  x1 = x * ghack_glyph_width() - 1;
  y1 = y * ghack_glyph_height() - 1;
  x2 = x1 + ghack_glyph_width() + 2;
  y2 = y1 + ghack_glyph_height() + 2;


  group = gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas));

  if (!cursor) {
    cursor = GNOME_CANVAS_RE (gnome_canvas_item_new (group, 
		gnome_canvas_rect_get_type (), 
		"outline_color", "antiquewhite2", 
		"width_units", 1.0, NULL));
  }
  gnome_canvas_item_set (GNOME_CANVAS_ITEM (cursor),
			 "x1", x1,
			 "y1", y1,
			 "x2", x2,
			 "y2", y2,
			 NULL);

  gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( cursor));
  gnome_canvas_item_show( GNOME_CANVAS_ITEM(cursor));
}


void
ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data)
{
    g_warning("Fixme!!! ghack_map_putstr is not implemented");
}

/* NAME: 
 *     ghack_map_print_glyph( )
 *
 * ARGUMENTS:
 *     XCHAR_P x, y  -- The coordinates where which to print the glyph
 *     GdkImlibImage*   glyph -- The glyph image to print
 *
 * RETURNS:
 *     Nothing.
 *
 * PURPOSE:
 *     Draw the glyph-tile at the specified coordinates.
*/

void
ghack_map_print_glyph( GtkObject *win, 
		       guint x, 
		       guint y, 
		       GdkImlibImage *im,
		       gpointer data)
{
  GnomeCanvasGroup *group;
  int i = y * COLNO + x;
  int glyph = glyph_at(x,y);
  GnomeCanvasImage *canvas_image = GNOME_CANVAS_IMAGE( ghack_map.map[i]);

  group = gnome_canvas_root (GNOME_CANVAS (ghack_map.canvas));
  
  gnome_canvas_item_set (GNOME_CANVAS_ITEM ( canvas_image),
			 "image",  im, NULL);
  gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image));

  canvas_image = GNOME_CANVAS_IMAGE( ghack_map.overlay[i]);

  if (x==u.ux && y==u.uy)
      ghack_map_cliparound(NULL, x, y, NULL);

  if (glyph_is_pet(glyph)
#ifdef TEXTCOLOR
		  && iflags.hilite_pet
#endif
		) {
      gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( canvas_image));
      gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image));
  }
  else {
      gnome_canvas_item_hide( GNOME_CANVAS_ITEM( canvas_image));
  }
}


/* NAME: 
 *     ghack_map_clear( )
 *
 * ARGUMENTS:
 *     NONE
 *
 * RETURNS:
 *     Nothing.
 *
 * PURPOSE:
 *     Clear the map by hiding all the map tiles.
*/

void
ghack_map_clear( GtkWidget *win, gpointer data)
{
  int i;

  for (i = 0; i < ROWNO * COLNO; i++)
    {
      if (GNOME_IS_CANVAS_IMAGE(ghack_map.map[i]))
	{
	  gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.map[i]));
	}
      if (GNOME_IS_CANVAS_IMAGE(ghack_map.overlay[i]))
	{
	  gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.overlay[i]));
	}
    }
  gnome_canvas_update_now ( GNOME_CANVAS(ghack_map.canvas));
}


void
ghack_map_display( GtkWidget *win, boolean block, gpointer data)
{
  gtk_widget_show_all( GTK_WIDGET(win));
}


void
ghack_map_cliparound( GtkWidget *win,
		      int x,
		      int y,
		      gpointer data)
{
  int  map_width, map_height;
  int  to_x, to_y;
  int  cur_x, cur_y;
  int  width, height, half_width, half_height;

  x *= ghack_glyph_width() * ghack_map.zoom;
  y *= ghack_glyph_height() * ghack_map.zoom;
  map_width = COLNO * ghack_glyph_width() * ghack_map.zoom;
  map_height = ROWNO * ghack_glyph_height() * ghack_map.zoom;

  gdk_window_get_size( GTK_LAYOUT (ghack_map.canvas)->bin_window, 
	  &width, &height);
  gnome_canvas_get_scroll_offsets( ghack_map.canvas, &cur_x, &cur_y);

  half_width  = width * 0.5;
  half_height = height * 0.5;

  if ( ((x - cur_x) < (width * 0.25) ) || ( (x - cur_x) > (width * 0.75) ) ) {
    to_x = ((x-half_width) > 0)? x - half_width : 0;
    to_x = ((x+half_width) > map_width)? map_width - 2 * half_width : to_x;
  }                                                                             
  else {
    to_x = cur_x;
  }                                                                             

  if ( ((y - cur_y) < (height * 0.25) ) || ( (y - cur_y) > (height * 0.75) ) ) {
    to_y = ((y-half_height) > 0)? y - half_height : 0;
    to_y = ((y+half_height) > map_height)? map_height - 2 * half_height : to_y;
  }                                                                             
  else {
    to_y = cur_y;
  }                                                                             

  if (to_x != cur_x || to_y != cur_y)
    gnome_canvas_scroll_to( ghack_map.canvas, to_x, to_y);
  //gnome_canvas_update_now ( ghack_map.canvas);
  
}



void
ghack_reinit_map_window ( )
{
  GnomeCanvasImage  *bg;
  double width, height, x, y;
  int i;

  /* ghack_map_clear(NULL, NULL); */

  width = COLNO * ghack_glyph_width();
  height = ROWNO * ghack_glyph_height();

  gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0, 
		    width+2*ghack_glyph_width(), height+2*ghack_glyph_height());

  /* remove everything currently in the canvas map */
  gtk_object_destroy( GTK_OBJECT (myCanvasGroup));

  /* Put some groups back */
  myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new ( 
		    gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)), 
		    gnome_canvas_group_get_type (), 
		    "x", 0.0, 
		    "y", 0.0, 
		    NULL) );

  /* Tile the map background with a pretty image */ 
  if (background != NULL) {
    /* Tile the map background */
    for (y = 0; y < height+background->rgb_height; y+=background->rgb_height)
    {
      for (x = 0; x < width+background->rgb_width; x+=background->rgb_width)
	{
	  bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new (
		      myCanvasGroup, gnome_canvas_image_get_type (),
		      "x",      (double) x,
		      "y",      (double) y,
		      "width",  (double) background->rgb_width,
		      "height", (double) background->rgb_height,
		      "image",  background,
		      "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER,
		      NULL) );
	  gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg));
	}
    }
  }

  /* ghack_map.map is an array of canvas images.  Each cell of
   * the array will contain one tile.  Here, we create the
   * space for the cells and then create the cells for easy
   * access later.
  */
  for (i=0, y = 0; y < height; y+=ghack_glyph_height()) {
      for (x = 0; x < width; x+=ghack_glyph_width()) {
	  ghack_map.map[i++] = GNOME_CANVAS_IMAGE( 
		  gnome_canvas_item_new (
		      myCanvasGroup,
		      gnome_canvas_image_get_type (),
		      "x",      (double) x,
		      "y",      (double) y,
		      "width",  (double) ghack_glyph_height(),
		      "height", (double) ghack_glyph_width(),
		      "anchor", GTK_ANCHOR_NORTH_WEST,
		      NULL) );
      }
  }
  
  if (petmark != NULL) {
      /* ghack_map.overlay is an array of canvas images used to
       * overlay tile images...
      */
      for (i=0, y = 0; y < height; y+=ghack_glyph_height()) {
	  for (x = 0; x < width; x+=ghack_glyph_width()) {
	      ghack_map.overlay[i] = GNOME_CANVAS_IMAGE( 
		      gnome_canvas_item_new (
			  myCanvasGroup,
			  gnome_canvas_image_get_type (),
			  "x",      (double) x,
			  "y",      (double) y,
			  "width",  (double) petmark->rgb_width,
			  "height", (double) petmark->rgb_height,
			  "image",  petmark,
			  "anchor", GTK_ANCHOR_NORTH_WEST,
			  NULL) );
	      gnome_canvas_item_lower_to_bottom (
		      GNOME_CANVAS_ITEM( ghack_map.overlay[i++]));
	    }
	}
  }

  ghack_map_cliparound(NULL, u.ux, u.uy, NULL);
  ghack_map_cursor_to(NULL, u.ux, u.uy, NULL);
  gnome_canvas_update_now ( ghack_map.canvas);
  doredraw();
}


