#include <gtk/gtk.h>
#include "unicode_char_info_marshal.h"
#include "unicode_char_info.h"

static void unicharinfo_table_class_init (UniCharInfoClass *class);
static void unicharinfo_init (UniCharInfo *obj);
static gint expose_event_handler (GtkWidget *, GdkEventExpose *, UniCharInfo *);
static void draw_unichar_table (UniCharInfo *obj);
static void draw_trellis (UniCharInfo *);

static void size_allocate_handler (GtkWidget *, GtkAllocation *, UniCharInfo *);

static gint minimal_column_width (UniCharInfo *obj);
static gint minimal_row_height (UniCharInfo *obj);

static gint bare_minimal_char_width (UniCharInfo *obj);
static gint bare_minimal_char_height (UniCharInfo *obj);
static gint __font_height (PangoFontMetrics *fm);

static gunichar rowcol_to_unichar (UniCharInfo *, gint , gint);
static void charinfo_table_draw_character (UniCharInfo *, gint, gint);
static gboolean __unichar_is_valid (gunichar uc);
static GtkWidget *get_scrollbar (UniCharInfo *obj);
static void charinfo_table_redraw (UniCharInfo *obj);

static void
debug_print_width (UniCharInfo *obj);
enum {
  ACTIVATE = 0,
  SET_ACTIVE_CHAR,
  NUM_SIGNALS
};

static guint unicharinfo_table_signals[NUM_SIGNALS] = { 0, 0};

GType
unicharinfo_get_type (void)
{
  static GType unicharinfo_type = 0;
  if (!unicharinfo_type){
    static const GTypeInfo unicharinfo_info = {
      sizeof (UniCharInfoClass),
      NULL, /* base init */
      NULL, /* base finalize */
      (GClassInitFunc) unicharinfo_table_class_init, /* class init */
      NULL, /* class finalize */
      NULL, /* class data */
      sizeof (UniCharInfo),
      0,
      (GInstanceInitFunc) unicharinfo_init, /* obj init */
      NULL /* value table */
    };
    unicharinfo_type =
      g_type_register_static (GTK_TYPE_HBOX, "unicharinfo",
			      &unicharinfo_info, 0);
  }
  return unicharinfo_type;
}

static GtkWidget *
get_scrollbar (UniCharInfo *obj)
{
  obj->adjustment =
    gtk_adjustment_new ( 0.0,
			 0.0, 1.0 * 0xFFFF / obj->n_cols,
			 2.0, 3.0 * obj->n_rows,
			 0.0);
  return gtk_vscrollbar_new (GTK_ADJUSTMENT (obj->adjustment));
}
static void
unicharinfo_table_class_init (UniCharInfoClass *class)
{
  class->set_active_char = NULL;
  
  unicharinfo_table_signals[SET_ACTIVE_CHAR] =
    g_signal_new ("set_active_char", unicharinfo_get_type(),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (UniCharInfoClass, set_active_char),
		  NULL, NULL, unicharinfo_table_marshal_VOID__UINT, G_TYPE_NONE,
		  1, G_TYPE_UINT);

}

static void
unicharinfo_init (UniCharInfo *obj)
{
  PangoContext *pango_ctx;
#if 0
  obj->n_rows = 10;
  obj->n_cols = 0x10;
#endif
  obj->begin_char = 0xac00;
  obj->end_char = 0xac30;
  obj->first_char = 0xac00;
  
  obj->drawing_area = gtk_drawing_area_new ();

  gtk_widget_set_events
    (obj->drawing_area,
     GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
     GDK_SCROLL_MASK);
  g_signal_connect (G_OBJECT (obj->drawing_area), "expose-event",
		    G_CALLBACK (expose_event_handler), obj);
  g_signal_connect (G_OBJECT (obj->drawing_area), "size-allocate",
		    G_CALLBACK (size_allocate_handler), obj);

  pango_ctx = gtk_widget_get_pango_context (obj->drawing_area);
  obj->pango_fm =
    pango_context_get_metrics (
			       pango_ctx,
			       obj->drawing_area->style->font_desc, NULL);
  obj->pango_layout = pango_layout_new (pango_ctx);
  pango_layout_set_font_description (obj->pango_layout,
				     obj->drawing_area->style->font_desc);
			       
  gtk_box_pack_start (GTK_BOX (obj), obj->drawing_area, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (obj), get_scrollbar (obj), FALSE, FALSE, 0);

  gtk_widget_show (obj->drawing_area);
  
#if 0
  {
    int i,j;
    int n_families, n_faces;
    PangoFontFamily **families = NULL;
    PangoFontFace **faces = NULL;
    pango_context_list_families (pango_ctx ,&families, &n_families);
    for (i = 0 ; i < n_families; i++){
      g_printf ("******** family %d ******\n", i);
      faces = NULL;
      pango_font_family_list_faces (families[i], &faces, &n_faces);
      for (j = 0 ; j < n_faces; j++){
	g_printf
	  ("face name : %s\n", pango_font_face_get_face_name (faces[j]));
      }
    }
  }
#endif
}

GtkWidget *
unicharinfo_new (void)
{
  return GTK_WIDGET (g_object_new (unicharinfo_get_type (), NULL));
}

static gint
expose_event_handler (GtkWidget *widget,
		      GdkEventExpose *event,
		      UniCharInfo *obj)
{
  gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
  
  if (obj->pixmap == NULL){
    draw_unichar_table (obj);
  }
  gdk_draw_pixmap (obj->drawing_area->window,
		   widget->style->fg_gc[GTK_STATE_NORMAL],
		   obj->pixmap,
		   event->area.x, event->area.y,
		   event->area.x, event->area.y,
		   event->area.width, event->area.height);
  debug_print_width (obj);
#if 0
  g_printf ("I was called\n");
#endif
}

static void
size_allocate_handler (GtkWidget *widget, 
		       GtkAllocation *allocation, 
		       UniCharInfo *obj)
{
  gint old_rows, old_cols;
  old_rows = obj->n_rows;
  old_cols = obj->n_cols;

  obj->n_cols = (allocation->width - 1) / bare_minimal_char_width (obj);
  obj->n_rows = (allocation->height - 1) / bare_minimal_char_height (obj);

  if (obj->n_cols < 1)
    obj->n_cols = 1;
  if (obj->n_rows < 1)
    obj->n_rows = 1;
  
  if (obj->pixmap != NULL)
    g_object_unref (obj->pixmap);
  obj->pixmap = NULL;
  
  if (obj->n_rows == old_rows && obj->n_cols == old_cols)
    return;
#if 0
  g_printf ("size_allocate_handler was called(%d col, %d rows)\n",
	    obj->n_cols, obj->n_rows);
#endif
}

static void
debug_print_width (UniCharInfo *obj)
{
  int i;
  int colwidth;
  int offset;
  int local_offset = 0;
  
  for (i = 0; i < obj->n_cols; i++){
    colwidth = charinfo_table_column_width (obj, i);
    offset = charinfo_table_x_offset (obj, i);

    
    g_printf ("col %d: width : %d, offset: %d: loffset: %d(%d)\n",
	      i, colwidth, offset, local_offset, offset - local_offset);
    local_offset += offset;

  }
}
static void
draw_unichar_table (UniCharInfo *obj)
{
  gint row, col;
  if (obj->pixmap == NULL){
    obj->pixmap =
      gdk_pixmap_new (obj->drawing_area->window,
		      obj->drawing_area->allocation.width,
		      obj->drawing_area->allocation.height,
		      -1);
  }
  gdk_draw_rectangle (obj->pixmap,
		      obj->drawing_area->style->base_gc[GTK_STATE_NORMAL],
		      TRUE,
		      0, 0,
		      obj->drawing_area->allocation.width,
		      obj->drawing_area->allocation.height);
  draw_trellis (obj);

  for (row = 0; row < obj->n_rows; row++)
    for (col = 0; col < obj->n_cols; col++){
      charinfo_table_draw_character (obj, row, col);
    }
}

static void
set_current_char (UniCharInfo *obj, gunichar uc)
{
  gint offset;

  g_return_if_fail (uc >= 0 && uc <= UNICHAR_MAX);

  obj->previous_selected_char = obj->selected_char;
  obj->previous_first_char = obj->first_char;

  obj->selected_char = uc;

  if (uc - obj->first_char >= obj->n_rows * obj->n_cols){
    offset = (gint) obj->selected_char - (gint) obj->previous_selected_char;
    if ((gint) obj->previous_first_char + offset >= 0)
      obj->first_char = obj->first_char + offset;
    else
      obj->first_char = 0;
    obj->first_char -= (obj->first_char % obj->n_cols);

    if (obj->selected_char - obj->first_char >= obj->n_rows * obj->n_cols)
      obj->first_char += obj->n_cols;
  }

  g_signal_emit (obj,
		 unicharinfo_table_signals[SET_ACTIVE_CHAR],
		 0 , obj->selected_char);
  
    
}


static void
charinfo_table_draw_character (UniCharInfo *obj, gint row, gint col)
{
  gint padding_x, padding_y;
  gint char_width, char_height;
  gint square_width, square_height; 
  gunichar uc;
  GdkGC *gc;
  gchar buf[10];
  gint n;

  uc = rowcol_to_unichar (obj, row, col);

  /* need to review later... */
#if 0  
  if (uc < 0 || uc > UNICHAR_MAX || ! __unichar_is_valid (uc) )
    return;


  if (GTK_WIDGET_HAS_FOCUS (obj->drawing_area) 
      && uc == obj->selected_char)
    gc = obj->drawing_area->style->text_gc[GTK_STATE_SELECTED];
  else if (uc == obj->selected_char)
    gc = obj->drawing_area->style->text_gc[GTK_STATE_ACTIVE];
  else
    gc = obj->drawing_area->style->text_gc[GTK_STATE_NORMAL];
#endif
  
  gc = obj->drawing_area->style->text_gc[GTK_STATE_NORMAL];
  
  square_width = charinfo_table_column_width (obj, col) - 1;
  square_height = charinfo_table_row_height (obj, row) - 1;
  
  g_printf ("(%d, %d)-width(%d), height(%d)\n",
	    row, col, square_width, square_height);

  n = charinfo_unichar_to_printable_utf8 (uc, buf);
  pango_layout_set_text (obj->pango_layout, buf, n);

  pango_layout_get_pixel_size (obj->pango_layout, 
                               &char_width, &char_height);

  /* (square_width - char_width)/2 is the smaller half */
  padding_x = (square_width - char_width) - (square_width - char_width)/2;
  padding_y = (square_height - char_height) - (square_height - char_height)/2;

  gdk_draw_layout (obj->pixmap, gc,
                   charinfo_table_x_offset (obj, col) + padding_x,
                   charinfo_table_y_offset (obj, row) + padding_y,
                   obj->pango_layout);
}

gint
charinfo_table_x_offset (UniCharInfo *obj, gint col)
{
  gint i, offset = 0;
  for (i = 0; i < col; i++){
    offset +=  charinfo_table_column_width (obj, col);
    
  }
  return offset;
}

gint
charinfo_table_y_offset (UniCharInfo *obj, gint row)
{
  gint r, offset = 0;

  for (r = 0 ;  r < row;  r++)
    offset += charinfo_table_row_height (obj, r);

  return offset;
}

static gunichar
rowcol_to_unichar (UniCharInfo *obj, gint row, gint col)
{
  if (gtk_widget_get_direction (obj->drawing_area) == GTK_TEXT_DIR_RTL)
    return obj->first_char + row * obj->n_cols 
           + (obj->n_cols - col - 1);
  else
    return obj->first_char + row * obj->n_cols + col;
}


gint
charinfo_unichar_to_printable_utf8 (gunichar uc, gchar *outbuf)
{
#if 0
  if (! gucharmap_unichar_validate (uc) || (! gucharmap_unichar_isgraph (uc) 
      && gucharmap_unichar_type (uc) != G_UNICODE_PRIVATE_USE))
    return 0;
  else if (gucharmap_unichar_type (uc) == G_UNICODE_COMBINING_MARK
      || gucharmap_unichar_type (uc) == G_UNICODE_ENCLOSING_MARK
      || gucharmap_unichar_type (uc) == G_UNICODE_NON_SPACING_MARK)
    {
      gint x;

      outbuf[0] = ' ';
      outbuf[1] = '\xe2'; /* ZERO */ 
      outbuf[2] = '\x80'; /* WIDTH */
      outbuf[3] = '\x8d'; /* JOINER (0x200D) */

      x = g_unichar_to_utf8 (uc, outbuf + 4);

      return x + 4;
    }
  else
    return g_unichar_to_utf8 (uc, outbuf);
#endif
  return g_unichar_to_utf8 (uc, outbuf);
}

static void
draw_trellis (UniCharInfo *obj)
{
  gint x, y;
  gint i_col, i_row;
  gdk_draw_line (obj->pixmap,
		 obj->drawing_area->style->dark_gc[GTK_STATE_NORMAL],
		 0, 0, 0, obj->drawing_area->allocation.height - 1);

  for (x = 0, i_col = 0; i_col < obj->n_cols; i_col++){
    x += charinfo_table_column_width (obj, i_col);
    gdk_draw_line (obj->pixmap,
		   obj->drawing_area->style->dark_gc[GTK_STATE_NORMAL],
		   x,0,x, obj->drawing_area->allocation.height - 1);
  }

  gdk_draw_line (obj->pixmap,
		 obj->drawing_area->style->dark_gc[GTK_STATE_NORMAL],
		 0, 0, obj->drawing_area->allocation.width - 1, 0);
  for (y = 0, i_row = 0; i_row < obj->n_rows; i_row++){
    y += charinfo_table_row_height (obj, i_row);
    gdk_draw_line (obj->pixmap,
		   obj->drawing_area->style->dark_gc[GTK_STATE_NORMAL],
		   0, y, obj->drawing_area->allocation.width - 1, y);
  }
}

static gint
minimal_column_width (UniCharInfo *obj)
{
  gint padding;
  gint char_glyph_width;
  char_glyph_width = bare_minimal_char_width (obj);
  padding =
    (obj->drawing_area->allocation.width -
     (obj->n_cols * char_glyph_width + 1)) / obj->n_cols;
#if 0
  g_printf ("minimal_column_width : %d\n", padding + char_glyph_width);
#endif
  return padding + char_glyph_width;
}

static gint
minimal_row_height (UniCharInfo *obj)
{
  gint padding;
  gint char_glyph_height;
  char_glyph_height = bare_minimal_char_height (obj);
  padding =
    (obj->drawing_area->allocation.height -
     (obj->n_rows * char_glyph_height + 1)) / obj->n_rows;
#if 0
  g_printf ("minimal_row_height : %d\n", padding + char_glyph_height);
#endif
  return padding + char_glyph_height;
}

static gint
bare_minimal_char_width (UniCharInfo *obj)
{
  return __font_height (obj->pango_fm) + 7;
}

static gint
bare_minimal_char_height (UniCharInfo *obj)
{
  return __font_height (obj->pango_fm) + 7;
}

static gint
__font_height (PangoFontMetrics *fm)
{
  gint font_height;
  font_height = pango_font_metrics_get_ascent (fm) +
    pango_font_metrics_get_descent (fm);
  return PANGO_PIXELS (font_height);
}


static gboolean
__unichar_is_valid (gunichar uc)
{
#if 0
  return gucharmap_unichar_type (uc) != G_UNICODE_UNASSIGNED;
#endif
}

gint
charinfo_table_column_width (UniCharInfo *obj, gint col)
{
  gint num_padded_columns;
  gint min_col_w = minimal_column_width (obj);

  num_padded_columns = obj->drawing_area->allocation.width
    - ( min_col_w * obj->n_cols + 1);
  if (obj->n_cols - col <= num_padded_columns)
    return min_col_w + 1;
  else
    return min_col_w;
}

gint
charinfo_table_row_height (UniCharInfo *obj, gint row)
{
  gint num_padded_rows;
  gint min_row_h = minimal_row_height (obj);

  num_padded_rows = obj->drawing_area->allocation.height
    - ( min_row_h * obj->n_rows + 1);
  if (obj->n_rows - row <= num_padded_rows)
    return min_row_h + 1;
  else
    return min_row_h;
}

static void
charinfo_table_redraw (UniCharInfo *obj)
{
  gint row_offset;
  row_offset = ((gint) obj->first_char - (gint) obj->begin_char) /
    obj->n_cols;
  

}

void
charinfo_table_set_current_character (UniCharInfo *obj, gunichar uc)
{
  set_current_char (obj, uc);
#if 0
  charinfo_table_redraw (obj);
#endif
}

#ifdef TEST_RUN
int
main (int argc, char *argv[])
{
  GtkWidget *main_window;
  GtkWidget *charinfo;
  gtk_init (&argc, &argv);

  main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (main_window), "destroy",
		    G_CALLBACK (gtk_main_quit), NULL);
  charinfo = unicharinfo_new ();
  gtk_container_add (GTK_CONTAINER (main_window), charinfo);
  gtk_widget_show (charinfo);
  gtk_widget_show (main_window);
  
  
  gtk_main ();
  return 0;
  
  
}

#endif
