#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gtk/gtkdrawingarea.h>
#include "chartable.h"

enum {
  PROP_0,
  PROP_RANGE_BEGIN,
  PROP_RANGE_END
};


static void chartable_class_init (CharTableClass *klass);
static void chartable_init (CharTable *table);
static void
chartable_get_property (GObject      *object,
			guint         property_id,
			GValue       *value,
			GParamSpec   *pspec);
static void
chartable_set_property (GObject      *object,
			guint         property_id,
			const GValue *value,
			GParamSpec   *pspec);
static gint
event_handler_expose (GtkWidget *widget,
		      GdkEventExpose *event,
		      CharTable *chartable);
static gint get_cell_vector (CharTable *table);
static void draw_chartable (CharTable *table);
static GtkWidget *make_scrollbar (CharTable *table);
static void scroll_chartable (GtkAdjustment *adjustment, CharTable *table);




GType
chartable_get_type (void)
{
  static GType chartable_type = 0;

  if (!chartable_type)
    {
      static const GTypeInfo chartable_info =
	{
	  sizeof (CharTableClass),
	  NULL,                /* base_init */
	  NULL,                /* base_finalize */
	  (GClassInitFunc) chartable_class_init,
	  NULL,                /* class_finalize */
	  NULL,                /* class_data */
	  sizeof (CharTable),
	  0,
	  (GInstanceInitFunc) chartable_init,
	};
      chartable_type = g_type_register_static (GTK_TYPE_HBOX,
					       "CharTable",
					       &chartable_info, 0);
    }
  return chartable_type;
}

static void
chartable_class_init (CharTableClass *klass)
{
  GObjectClass *gobject_class;
  gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->get_property = chartable_get_property;
  gobject_class->set_property = chartable_set_property;
  g_object_class_install_property (gobject_class,
				   PROP_RANGE_BEGIN,
				   g_param_spec_uint ("code_begin",
						      "code_begin",
						      "begin value of code range",
						      0, 0xffff, 0,
						      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
  g_object_class_install_property (gobject_class,
				   PROP_RANGE_END,
				   g_param_spec_uint ("code_end",
						      "code_end",
						      "end value of code range",
						      0, 0xffff, 0,
						      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));  

  
  return;
}

static void
chartable_init (CharTable *table)
{
  PangoContext *context;
  int n_cols, n_rows;
  n_cols = 16, n_rows = 16;
  table->drawingarea = gtk_drawing_area_new ();
  table->pixmap = NULL;
  table->ncols = n_cols;
  table->nrows = n_rows;
  gtk_widget_set_events (table->drawingarea,
			 GDK_EXPOSURE_MASK
			 );

  g_signal_connect (G_OBJECT (table->drawingarea),
		    "expose-event",
		    G_CALLBACK (event_handler_expose),
		    table);

  gtk_box_pack_start (GTK_BOX (table), table->drawingarea,
		      TRUE, TRUE, 0);
		      


  context = gtk_widget_get_pango_context (table->drawingarea);
  
  table->font_name = NULL;
  table->font_metrics =
    pango_context_get_metrics (context,
			       table->drawingarea->style->font_desc, NULL);
  table->pango_layout = pango_layout_new (context);
  pango_layout_set_font_description (table->pango_layout,
				     table->drawingarea->style->font_desc);
#if 0
  g_printf ("cell width should be %d\n", get_cell_vector (table));
#endif
  gtk_widget_set_size_request (GTK_WIDGET (table->drawingarea),
			       get_cell_vector (table) * n_cols + (n_cols + 1),
			       get_cell_vector (table) * n_rows + (n_rows + 1));
  gtk_box_pack_start (GTK_BOX (table),
		      make_scrollbar (table),
		      FALSE, FALSE, 0);
  gtk_widget_show_all (GTK_WIDGET (table));
    
}

static GtkWidget *
make_scrollbar (CharTable *table)
{
  int n_rows;
  n_rows = table->nrows;
  
  table->adjustment =
    gtk_adjustment_new (0.0, 0.0, 100.0,
			1.0 * (get_cell_vector (table) + 1),
			1.0 * ((get_cell_vector (table) * n_rows + n_rows + 1)),
			0.0);
  table->adjustment_changed_handler_id =
    g_signal_connect (G_OBJECT (table->adjustment), "value-changed",
		      G_CALLBACK (scroll_chartable), table);
  return gtk_vscrollbar_new (GTK_ADJUSTMENT (table->adjustment));
}

static void
scroll_chartable (GtkAdjustment *adjustment, CharTable *table)
{
  g_printf ("scrollbar is being changed...\n");
}

static void
draw_borders (CharTable *table)
{
  int n_cols, n_rows;
  int ic, ir;
  int x, y;
  n_cols = table->ncols;
  n_rows = table->nrows;

  gdk_draw_line (table->pixmap,
		 table->drawingarea->style->dark_gc[GTK_STATE_NORMAL],
		 0,0,0, table->drawingarea->allocation.height - 1);

  for (ic = 0, x = 0; ic < table->ncols; ic++){
    x += get_cell_vector (table) + 1 ;
    gdk_draw_line (table->pixmap,
		   table->drawingarea->style->dark_gc[GTK_STATE_NORMAL],
		   x, 0, x, table->drawingarea->allocation.height - 1);
  }
  
  gdk_draw_line (table->pixmap,
		 table->drawingarea->style->dark_gc[GTK_STATE_NORMAL],
		 0,0,0, table->drawingarea->allocation.width - 1);
  for (ir = 0, y = 0; ir < table->nrows; ir++){
    y += get_cell_vector (table) + 1;
    gdk_draw_line (table->pixmap,
		   table->drawingarea->style->dark_gc[GTK_STATE_NORMAL],
		   0, y, table->drawingarea->allocation.width - 1, y);
  }
}

static void
chartable_get_property (GObject      *object,
			guint         property_id,
			GValue       *value,
			GParamSpec   *pspec)
{
  CharTable *table;
  table = CHARTABLE (object);
  switch (property_id)
    {
    case PROP_RANGE_BEGIN:
      g_value_set_uint (value, table->_begin);
      break;
    case PROP_RANGE_END:
      g_value_set_uint (value, table->_end);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;      
    }
}

static void
chartable_set_property (GObject      *object,
			guint         property_id,
			const GValue *value,
			GParamSpec   *pspec)
{
  CharTable *table;
  table = CHARTABLE (object);
  g_printf ("set_property seems to be being called\n");
  
  switch (property_id)
    {
    case PROP_RANGE_BEGIN:
      chartable_set_begin (table, g_value_get_uint (value));
      break;
    case PROP_RANGE_END:
      chartable_set_end (table, g_value_get_uint (value));
      break;
    }
}

static gint
event_handler_expose (GtkWidget *widget,
		      GdkEventExpose *event,
		      CharTable *chartable)
{
  if (chartable->pixmap == NULL){
    draw_chartable (chartable);
  }
  gdk_draw_drawable (chartable->drawingarea->window,
		     widget->style->fg_gc[GTK_STATE_NORMAL],
		     chartable->pixmap,
		     event->area.x, event->area.y,
                     event->area.x, event->area.y,
                     event->area.width, event->area.height);
  return FALSE;
}

static void
draw_chartable (CharTable *chartable)
{
  if (chartable->pixmap == NULL)
    chartable->pixmap =
      gdk_pixmap_new (chartable->drawingarea->window,
		      chartable->drawingarea->allocation.width,
		      chartable->drawingarea->allocation.height,
		      -1);
  gdk_draw_rectangle (chartable->pixmap,
		      chartable->drawingarea->style->base_gc[GTK_STATE_NORMAL],
		      TRUE, 0, 0,
		      chartable->drawingarea->allocation.width,
		      chartable->drawingarea->allocation.height);
  gdk_draw_line (chartable->pixmap,
		 chartable->drawingarea->style->base_gc[GTK_STATE_NORMAL],
		 0,0,
		 chartable->drawingarea->allocation.width,
		 chartable->drawingarea->allocation.height);
  draw_borders (chartable);

}

static gint
get_font_height (PangoFontMetrics *font_metrics)
{
  gint height;
  height = pango_font_metrics_get_ascent (font_metrics) +
    pango_font_metrics_get_descent (font_metrics);
  return PANGO_PIXELS (height);
}

static gint
get_cell_vector (CharTable *table)
{
  return get_font_height (table->font_metrics) + 8;
}

GtkWidget *
chartable_new (void)
{
  return g_object_new (CHARTABLE_TYPE, NULL);
}

GtkWidget *
chartable_new_with_range (guint begin, guint end)
{
  guint real_begin, real_end;

  real_begin = (begin & 0xff00);
  real_end = (end | 0x00ff);
  
  return g_object_new (CHARTABLE_TYPE,
		       "code_begin", real_begin,
		       "code_end", real_end,
		       NULL);
}

void
chartable_set_begin (CharTable *table, guint begin)
{
  table->_begin = (begin & 0xff00);
  g_object_notify (G_OBJECT (table), "code_begin");
}

void
chartable_set_end (CharTable *table, guint end)
{
  table->_end = (end | 0x00ff);
  g_object_notify (G_OBJECT (table), "code_end");
}

void
chartable_set_range (CharTable *table, guint begin, guint end)
{
  table->_begin = (begin & 0xff00);
  table->_end = (end | 0x00ff);
  
  g_object_notify (G_OBJECT (table), "code_begin");
  g_object_notify (G_OBJECT (table), "code_end");  
}
