/*
 * Copyright 2003 Sun Microsystems Inc.
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Karl Park <karl.park@sun.com>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gtk/gtk.h>
#include <assert.h>
#include "virtual_keyboard.h"
#include "keyboard_config.h"

enum {
  V_INPUT,
  LAST_SIGNAL
};

static void virtual_keyboard_init (VirtualKeyboard *obj);

static gint expose_event_handler (GtkWidget *, GdkEventExpose *, VirtualKeyboard *);
static gint button_press_handler (GtkWidget *widget, GdkEventButton *event, VirtualKeyboard *obj);
static gint
button_release_handler (GtkWidget *widget, GdkEventButton *event, VirtualKeyboard *obj);
static void size_allocate_handler (GtkWidget *, GtkAllocation *, VirtualKeyboard *);
static void init_keyboard_dimension (VirtualKeyboard *obj, gint *width, gint *height);
static void draw_virtual_keyboard (VirtualKeyboard *obj);
static void draw_frame (VirtualKeyboard *obj);
static void draw_all_buttons (VirtualKeyboard *obj);
static void draw_button (VirtualKeyboard *, int, gboolean);
static void draw_button_frame (VirtualKeyboard *obj,
			       GdkRectangle* pRect, gboolean bPushed,
			       int nBorderWidth);
static gint get_button_id_from_pos (VirtualKeyboard *ob, int x, int y);
static gint get_char_height (PangoFontMetrics *font_metrics);
static gboolean rect_has_pos (GdkRectangle *rect, int x, int y);

static gboolean
menuitem_cb (GtkWidget *widget);

GType
virtual_keyboard_get_type (void)
{
  static GType vk_type = 0;
  if (!vk_type){
    static const GTypeInfo vk_type_info = {
      sizeof (VirtualKeyboardClass),
      NULL, /* base_init */
      NULL, /* base_finalize */
      (GClassInitFunc) NULL,
      NULL, /* class_finalize */
      NULL, /* class_data */
      sizeof (VirtualKeyboard),
      0,    /* n_preallocs */
      (GInstanceInitFunc) virtual_keyboard_init,
    };
    vk_type =
      g_type_register_static(GTK_TYPE_HBOX, "VirtualKeyboard",
			     &vk_type_info, 0);
  }
  return vk_type;
}

GtkWidget *
virtual_keyboard_new ()
{
  VirtualKeyboard *obj;
  obj = g_object_new (VIRTUAL_KEYBOARD_TYPE, NULL);
  
#if 0
  gtk_widget_set_size_request (obj->drawing_area, 1000, 50);
#endif
  return GTK_WIDGET (obj);
}


GtkWidget *
virtual_keyboard_new_with_keyboard_layout (int count, KeyboardLayout **li)
{
  VirtualKeyboard *obj;
  int i = count;
  
  obj = g_object_new (VIRTUAL_KEYBOARD_TYPE, NULL);
  for (i = 0; i < count; i++)
    virtual_keyboard_append_keyboard_layout (obj, li[i]);

  return GTK_WIDGET (obj);
}

void
virtual_keyboard_append_keyboard_layout (VirtualKeyboard *obj,
					 KeyboardLayout *li)
{
  KeyboardLayout **tmp;
  GtkWidget *popup_menu;
  
  assert (li != NULL);
  
  tmp =
    (KeyboardLayout **) calloc (obj->keyboard_layout_count + 1 ,
				sizeof (KeyboardLayout *));
  memcpy (tmp, obj->keyboard_layout,
	  obj->keyboard_layout_count * sizeof (KeyboardLayout *));
  tmp[obj->keyboard_layout_count] =
    (KeyboardLayout *) calloc (1, sizeof (KeyboardLayout));

  
  copy_keyboardlayout_struct (tmp[obj->keyboard_layout_count], li);
  obj->keyboard_layout_count++;
  free (obj->keyboard_layout);
  obj->keyboard_layout = tmp;

  popup_menu = obj->popup_menu;
  
}

void
virtual_keyboard_set_keyboard_layout (VirtualKeyboard *obj, gint i)
{
  if (i >= obj->keyboard_layout_count || i < 0)
    obj->nKeyboardID  = 0;
  else
    obj->nKeyboardID = i;
}

static void
virtual_keyboard_init (VirtualKeyboard *obj)
{
  int ret;
  PangoContext *context;
						 
  obj->drawing_area = gtk_drawing_area_new ();
  /* pango */
  context = gtk_widget_get_pango_context (obj->drawing_area);

  obj->n_pressed_button_id = -1;
  obj->font_metrics =
    pango_context_get_metrics (context, obj->drawing_area->style->font_desc, NULL);

  obj->pango_layout = pango_layout_new (context);
  pango_layout_set_font_description (obj->pango_layout,
				     obj->drawing_area->style->font_desc);

  init_keyboard_dimension (obj, &obj->width, &obj->height);
  
  gtk_widget_set_size_request (GTK_WIDGET (obj->drawing_area), obj->width, obj->height);
    
  gtk_widget_set_events (obj->drawing_area,
			 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
			 GDK_BUTTON_RELEASE_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);

  g_signal_connect (G_OBJECT (obj->drawing_area), "button_press_event",
		    G_CALLBACK (button_press_handler), obj);
  g_signal_connect (G_OBJECT (obj->drawing_area), "button_release_event",
		    G_CALLBACK (button_release_handler), obj);
  gtk_box_pack_start (GTK_BOX (obj), obj->drawing_area,
		      TRUE, TRUE, 0);
  gtk_widget_show_all (GTK_WIDGET (obj));
}

static gint
button_press_handler (GtkWidget *widget, 
                    GdkEventButton *event, 
                    VirtualKeyboard *obj)
{
  gint i_previous_button;
  gint i_keybutton;

  if (event->button == 1){
  i_keybutton = get_button_id_from_pos (obj, event->x, event->y);
  g_printf ("pressed button was %d\n", i_keybutton);

  i_previous_button = obj->n_pressed_button_id;
  obj->n_pressed_button_id = i_keybutton;

  draw_button (obj, i_keybutton, TRUE);

  gtk_widget_queue_draw (obj->drawing_area);
  } else if (event->button == 3){
    
    GtkWidget *menuitem;
    int i;
    
    if (obj->popup_menu)
      gtk_widget_destroy (obj->popup_menu);

    obj->popup_menu = gtk_menu_new ();
    for (i = 0; i < obj->keyboard_layout_count; i++){
      menuitem =
	gtk_menu_item_new_with_label (obj->keyboard_layout[i]->ename);

      gtk_menu_shell_append (GTK_MENU_SHELL (obj->popup_menu),
			     menuitem);
      gtk_widget_show (menuitem);
      g_signal_connect_swapped (G_OBJECT (menuitem), "activate",
			G_CALLBACK (menuitem_cb), i);
    }
    gtk_menu_popup (GTK_MENU (obj->popup_menu),
		    NULL, NULL, NULL, NULL, 0,0);

  }
}

static gint
button_release_handler (GtkWidget *widget, 
                      GdkEventButton *event, 
                      VirtualKeyboard *obj)
{
  draw_button (obj, obj->n_pressed_button_id, FALSE);
  obj->n_pressed_button_id = -1;
  gtk_widget_queue_draw (obj->drawing_area);
  return 0;
}

static gint
expose_event_handler (GtkWidget *widget, GdkEventExpose *event,
		      VirtualKeyboard *obj)
{
  g_printf ("expose_event called\n");
  
  draw_virtual_keyboard (obj);
  
  gdk_draw_drawable (obj->drawing_area->window,
		     widget->style->base_gc[GTK_STATE_NORMAL],
		     obj->pixmap,
		     event->area.x, event->area.y,
		     event->area.x, event->area.y,
		     event->area.width, event->area.height
		     );
  return 0;
}


static void
size_allocate_handler (GtkWidget *widget,
		       GtkAllocation *allocation,
		       VirtualKeyboard *obj)
{
  if (obj->pixmap != NULL)
    g_object_unref (obj->pixmap);
  obj->pixmap = NULL;
}


static void
draw_virtual_keyboard (VirtualKeyboard *obj)
{
  draw_frame (obj);
  draw_all_buttons (obj);
}

static void
draw_frame (VirtualKeyboard *obj)
{
  GdkPixmap *pixmap;
  GdkGC *gc;

  if (!obj->pixmap)
    obj->pixmap =
      gdk_pixmap_new (
		      obj->drawing_area->window,
		      obj->drawing_area->allocation.width,
		      obj->drawing_area->allocation.height, -1);

  gc = obj->drawing_area->style->bg_gc[GTK_STATE_NORMAL];
  gdk_draw_rectangle (
		      obj->pixmap,
		      gc,
		      TRUE, 0,0,
		      obj->drawing_area->allocation.width,
		      obj->drawing_area->allocation.height);
  
  gc = obj->drawing_area->style->fg_gc[GTK_STATE_NORMAL];
  gdk_draw_rectangle (
		      obj->pixmap,
		      gc,
		      FALSE, 0,0,
		      obj->drawing_area->allocation.width - 1,
		      obj->drawing_area->allocation.height - 1);
}

static void
draw_all_buttons (VirtualKeyboard *obj)
{
  int i_keybutton, i_ctrlbutton;
  gboolean is_pushed;

  for (i_keybutton=0; i_keybutton < MAX_VK_NUM; i_keybutton++) {
    is_pushed = FALSE;
    if (i_keybutton >= MAX_BASEKEY_NUM) {
      /* if is Control Keys */
      i_ctrlbutton = i_keybutton - MAX_BASEKEY_NUM;
      if (i_ctrlbutton == VK_Caps_Lock) {
	is_pushed = obj->nCapsLockKey_Status ? TRUE : FALSE;
      } else if (i_ctrlbutton == VK_Control_L) {
	is_pushed = obj->nCtrlKey_Status ? TRUE : FALSE;
      } else if (i_ctrlbutton == VK_Shift_L) {
	is_pushed = obj->nShiftKey_Status ? TRUE : FALSE;
      } else if (i_ctrlbutton == VK_Alt_L) {
	is_pushed = obj->nAltKey_Status ? TRUE : FALSE;
      }
    } else
      is_pushed = i_keybutton == obj->n_pressed_button_id ? TRUE : FALSE;
    
    draw_button (obj, i_keybutton, is_pushed);
  }
}

static void
draw_button (VirtualKeyboard *obj, int i_pressed_button, gboolean is_pushed)
{
  PangoLayout *pango_layout;
  KeyboardLayout *pVKB;
  GdkGC *bg_gc, *label_gc;
  GdkRectangle *rect;
  GdkRectangle *buttons = obj->buttons;
  char *keylist_label;
  gint nXPos, nYPos, nWidth, nHeight;


  gchar *buf;
  gchar *str;
  gchar mybuffer[20];

  if (i_pressed_button == -1)
    return;
  pVKB = obj->keyboard_layout[obj->nKeyboardID];
  rect = &buttons[i_pressed_button];

  /* graphic context */
  if (is_pushed == TRUE) {
    bg_gc = obj->drawing_area->style->bg_gc[GTK_STATE_SELECTED];
#if 0
    bg_gc = obj->drawing_area->style->black_gc;
#endif
  } else {
    bg_gc = obj->drawing_area->style->bg_gc[GTK_STATE_NORMAL];
  }
  
  label_gc = obj->drawing_area->style->text_gc[GTK_STATE_NORMAL];
  
  gdk_draw_rectangle (obj->pixmap, bg_gc, TRUE,
		      rect->x, rect->y,
		      rect->width, rect->height);
  
  gdk_draw_rectangle (obj->pixmap,
		      label_gc,
		      FALSE,
		      rect->x, rect->y,
		      rect->width, rect->height);

  pango_layout =
    pango_layout_new (pango_layout_get_context (obj->pango_layout));

  if (i_pressed_button >= MAX_BASEKEY_NUM) {
    /* if is Control Keys */

    str = pVKB->ctrlkey[i_pressed_button-MAX_BASEKEY_NUM].label_str;
    if (str == NULL)
      return;
    pango_layout_set_text (obj->pango_layout, str, strlen (str));
    gdk_draw_layout (obj->pixmap, label_gc,
		     rect->x + 3, rect->y + 3, 
		     obj->pango_layout);
  } else {
    
    str = pVKB->basekey[i_pressed_button].lower_str;
    if (!str || !*str)
      return;
    
    buf = g_strdup (pVKB->basekey[i_pressed_button].lower_str);

    str = pVKB->basekey[i_pressed_button].upper_str;
    
    if (str && *str)
      sprintf (mybuffer, "%s\n    %s", buf, str);
#if 0
    g_printf (buf);
#endif
    
    pango_layout_set_text (obj->pango_layout, mybuffer, strlen (mybuffer));
    gdk_draw_layout (obj->pixmap, label_gc,
		     rect->x + 3, rect->y + 3,
		     obj->pango_layout);
    g_free (buf);
      

/*     /\* if is Base Keys *\/ */

/*     str = pVKB->basekey[i_pressed_button].lower_str; */


    

/*     if (str && *str) { */
/*       pango_layout_set_text (obj->pango_layout, str, strlen (str)); */
/*       gdk_draw_layout (obj->pixmap, label_gc, */
/* 		       rect->x + 3, rect->y + 3,  */
/* 		       obj->pango_layout); */
/*     } */

/*     str = pVKB->basekey[i_pressed_button].upper_str; */
/*     if (str && *str) { */
/*       if (str[1]) { */
/* 	/\* if is not English Character *\/ */
/* 	nXPos = nXPos + nWidth - 13; */
	
/*       } else { */
/* 	/\* if is English character *\/ */
/* 	nXPos = nXPos + nWidth - 10; */
/*       } */
/*       pango_layout_set_text (obj->pango_layout, str, strlen (str)); */
/*       gdk_draw_layout (obj->pixmap, label_gc, */
/* 		       rect->x + 3, rect->x + rect->x + rect->width - 3, */
/* 		       obj->pango_layout); */
/*     } */

  }

  g_object_unref (pango_layout);

  draw_button_frame (obj, &(buttons[i_pressed_button]), is_pushed, 2);

}

static void
draw_button_frame (VirtualKeyboard *obj, GdkRectangle* p_rect,
		 gboolean is_pushed, int n_border_width)
     
{
  GdkPoint arrPt[3];
  GdkGC *defGC = obj->drawing_area->style->bg_gc[GTK_STATE_NORMAL];
  GdkGC *dark_gc = obj->drawing_area->style->dark_gc[GTK_STATE_NORMAL];
  GdkGC *white_gc = obj->drawing_area->style->white_gc;
  GdkGC *test_gc;
  GdkColor color;
  color.red = 0;
  color.green = 0;
  color.blue = 256;
  
  

 
  int nXPosStart = p_rect->x;
  int nYPosStart = p_rect->y;
  int nXPosEnd   = p_rect->x + p_rect->width - 1;
  int nYPosEnd   = p_rect->y + p_rect->height - 1;
  int i;

  for (i = 0; i < n_border_width; i++) {
    arrPt[0].x = nXPosStart + i; arrPt[0].y = nYPosEnd - i;
    arrPt[1].x = nXPosStart + i; arrPt[1].y = nYPosStart + i;
    arrPt[2].x = nXPosEnd;   arrPt[2].y = nYPosStart + i;
    gdk_draw_lines (obj->pixmap, white_gc, arrPt, 3);
  }
  
  for (i = 0; i < n_border_width; i++) {
    arrPt[0].x = nXPosEnd - i;   arrPt[0].y = nYPosStart + i;
    arrPt[1].x = nXPosEnd - i;   arrPt[1].y = nYPosEnd - i;
    arrPt[2].x = nXPosStart + i; arrPt[2].y = nYPosEnd - i;
  /*   if (is_pushed) */
      gdk_draw_lines (obj->pixmap, dark_gc, arrPt, 3);
/*     else */
/*       gdk_draw_lines (obj->pixmap, white_gc, arrPt, 3); */
  }
}

static gint
get_button_id_from_pos (VirtualKeyboard *obj, int x, int y)
{
  gint i;
  for (i = 0 ; i < MAX_VK_NUM; i++){
    if (rect_has_pos (&obj->buttons[i], x, y))
      return i;
  }
  return VK_movearea;
}

static gboolean
rect_has_pos (GdkRectangle *rect, int x, int y)
{
  return ((rect->x <= x) && (rect->x + rect->width >= x) &&
	  (rect->y <= y) && (rect->y + rect->height >= y));
}


static void
init_keyboard_dimension (VirtualKeyboard *obj, gint *width_return, gint *height_return)
{
  GdkRectangle *buttons;
  int nXPos, nYPos, nXMargin, nYMargin;
  int nHeight, nWidth, i_button_height, i_button_width;
  int nFrameHeight, nFrameWidth;
  int nYDelta = 1;
  int i, j;

  buttons = obj->buttons;

  g_printf ("height of the character is %d\n",
	    get_char_height (obj->font_metrics));
  


  i_button_height =  2 * get_char_height (obj->font_metrics) + 4;
  i_button_width  = i_button_height + 4;
  
  nXMargin = 3;
  nYMargin = 3;

  /* layout for the first line */
  nXPos = nXMargin;
  nYPos = nYMargin;

  for (i=VK_quotedbl; i<=VK_equal; i++) {
    nWidth = i_button_width;
    buttons[i].x = nXPos;
    buttons[i].y = nYPos;
    buttons[i].width = nWidth;
    buttons[i].height = i_button_height;
    nXPos += nWidth;
  } 

  i = VK_BackSpace + MAX_BASEKEY_NUM;
  nWidth = i_button_width * 2;
  buttons[i].x = nXPos;
  buttons[i].y = nYPos;
  buttons[i].width = nWidth;
  buttons[i].height = i_button_height;

  /* layout for the second line */
  nXPos = nXMargin;
  nYPos += i_button_height + nYDelta;

  i = VK_Tab + MAX_BASEKEY_NUM;
  nWidth = i_button_width * 3 / 2;
  buttons[i].x = nXPos;
  buttons[i].y = nYPos;
  buttons[i].width = nWidth;
  buttons[i].height = i_button_height;
  nXPos += nWidth;

  for (i=VK_q; i<=VK_backslash; i++) {
    nWidth = i_button_width;
    if (i == VK_backslash) nWidth = i_button_width * 3 / 2;

    buttons[i].x = nXPos;
    buttons[i].y = nYPos;
    buttons[i].width = nWidth;
    buttons[i].height = i_button_height;
    nXPos += nWidth;
  } 

  /* layout for the third line */
  nXPos = nXMargin;
  nYPos += i_button_height + nYDelta;

  i = VK_Caps_Lock + MAX_BASEKEY_NUM;
  nWidth = i_button_width * 2;
  buttons[i].x = nXPos;
  buttons[i].y = nYPos;
  buttons[i].width = nWidth;
  buttons[i].height = i_button_height;
  nXPos += nWidth;

  for (i=VK_a; i<=VK_apostrophe; i++) {
    nWidth = i_button_width;
    buttons[i].x = nXPos;
    buttons[i].y = nYPos;
    buttons[i].width = nWidth;
    buttons[i].height = i_button_height;
    nXPos += nWidth;
  } 

  i = VK_Enter + MAX_BASEKEY_NUM;
  nWidth = i_button_width * 2;
  buttons[i].x = nXPos;
  buttons[i].y = nYPos;
  buttons[i].width = nWidth;
  buttons[i].height = i_button_height;
  nXPos += nWidth;

  /* layout for the fourth line */
  nXPos = nXMargin;
  nYPos += i_button_height + nYDelta;

  i = VK_Shift_L + MAX_BASEKEY_NUM;
  nWidth = i_button_width * 5 / 2;
  buttons[i].x = nXPos;
  buttons[i].y = nYPos;
  buttons[i].width = nWidth;
  buttons[i].height = i_button_height;
  nXPos += nWidth;

  for (i=VK_z; i<=VK_slash; i++) {
    nWidth = i_button_width;
    buttons[i].x = nXPos;
    buttons[i].y = nYPos;
    buttons[i].width = nWidth;
    buttons[i].height = i_button_height;
    nXPos += nWidth;
  } 

  /* layout for the fifth line */
  nXPos = nXMargin;
  nYPos += i_button_height + nYDelta;

  for (i=VK_Control_L; i<=VK_escape; i++) {
    nWidth = 2 * i_button_width;
    if (i == VK_space)  nWidth = 7 * i_button_width;
    if (i == VK_escape) nXPos += 2 * i_button_width;
    j = i + MAX_BASEKEY_NUM;
    buttons[j].x = nXPos;
    buttons[j].y = nYPos;
    buttons[j].width = nWidth;
    buttons[j].height = i_button_height;
    nXPos += nWidth;
  } 

  /* Calculate the frame dimension of the Virtual keyboard */
  nWidth = 15 * i_button_width + 2 * nXMargin;
  nHeight = 5 * i_button_height + 2 * nYMargin + 4 * nYDelta;

  obj->width = nWidth;
  obj->height = nHeight;
  if (width_return && height_return){
    *width_return = nWidth;
    *height_return = nHeight;
  }

}


static gint
get_char_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 gboolean
menuitem_cb (GtkWidget	       *widget)
{
  g_printf ("menuitem_cb being called\n");
  return TRUE;
}
