/*
 *  window_gtk.c -- window handling, GTK
 */

#include "gui.h"

signed int gui_window_origin_x, gui_window_origin_y;
GtkWidget *gui_redraw_widget;
gui_window_id menu_window_id;


gboolean gui_window_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
  gui_window_id window_id;

  printf("gui_window_press: widget 0x%x, type %i, win 0x%x, %f %f, %i, data 0x%x\n",
        (unsigned int) widget, event->type, (unsigned int) event->window, event->x, event->y, event->button, (unsigned int) data);

  window_id = gui_handle_to_window_id(data);

  if (window_id != gui_WINDOW_UNKNOWN)
  {
    assert(window_id < gui_window_list_items && gui_window_list[window_id].used);
    if (gui_window_list[window_id].menu && event->button == 2)
    {
      gtk_menu_popup(gui_window_list[window_id].menu->menu, NULL, NULL, NULL,
                     NULL, event->button, event->time);
      menu_window_id = window_id;
    }
    else if (gui_window_list[window_id].click_fn)
    {
      unsigned int z;

      z = (event->button == 1 ? gui_CLICK :
          (event->button == 3 ? gui_CLICK_RIGHT : 0));
      gui_window_list[window_id].click_fn(window_id, event->x * 2, event->y * 2, z);
    }
  }

}



gboolean gui_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
  gui_window_id window_id;
  signed int invalid[4];

  window_id = gui_handle_to_window_id(data);

  gui_window_origin_x = 0;
  gui_window_origin_y = 0;
  gui_redraw_widget = widget;
  invalid[0] = event->area.x * 2;
  invalid[1] = event->area.y * 2;
  invalid[2] = (event->area.x + event->area.width) * 2;
  invalid[3] = (event->area.y + event->area.height) * 2;

  gdk_window_clear_area(widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
  gdk_gc_set_clip_rectangle(widget->style->white_gc, &event->area);

  printf("gui_expose_event: id %i, rect %i %i %i %i\n", window_id,
        invalid[0], invalid[1], invalid[2], invalid[3]);

  gui_window_list[window_id].redraw_fn(window_id, invalid);

  gdk_gc_set_clip_rectangle(widget->style->white_gc, NULL);

  return TRUE;
}


void gui_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
{
  gui_window_id window_id;

  window_id = gui_handle_to_window_id(data);
  printf("gui_size_allocate: id %i, %i %i %u %u\n", window_id, allocation->x, allocation->y, allocation->width, allocation->height);
  gui_window_list[window_id].resize_fn(window_id, allocation->width * 2, allocation->height * 2);
}



/*
 *  gui_create_window -- create and display a window
 *
 *  => flags -- window details
 *     title -- window title
 *     width -- width of window
 *     height -- height of window
 *     close_fn -- function called when an attempt is made to close the window,
 *                 NULL for default of closing and destroying window
 *     redraw_fn -- function called when the window needs redrawing
 *     click_fn -- function called when the mouse is clicked in the window or equivalent,
 *                 NULL for default of no action
 *
 *  <= window id
 */

gui_window_id gui_create_window(const unsigned int flags,
      char *title,
      const unsigned int width, const unsigned int height,
      void (*close_fn)(gui_window_id window_id),
      void (*redraw_fn)(gui_window_id window_id, unsigned int invalid[]),
      void (*click_fn)(gui_window_id window_id, unsigned int x, unsigned int y,
            unsigned int z),
      void (*resize_fn)(gui_window_id window_id, unsigned int width, unsigned int height),
      bool (*key_fn)(gui_window_id window_id, unsigned int key),
      void (*input_fn)(gui_window_id window_id, wchar_t key),
      void (*menu_fn)(gui_window_id window_id, unsigned int items[]),
      struct gui_menu_data *menu,
      const char *bbar,
      const char *help)
{
  GtkWidget *window;
  GtkWidget *scrolled_window;
  GtkWidget *event_box;
  GtkWidget *drawing_area;
  gui_window_id id;

  #ifdef DEBUG
  printf("gui_create_window: title = '%s'\n", title);
  fflush(stdout);
  #endif

  assert(redraw_fn != NULL);

  id = gui_get_free_window_id();

  drawing_area = gtk_drawing_area_new();
  event_box = gtk_event_box_new();
  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_drawing_area_size(drawing_area, flags & gui_H_SCROLL_BAR ? width / 2 : 0, flags & gui_V_SCROLL_BAR ? height / 2 : 0);

  gtk_container_set_border_width(GTK_CONTAINER (scrolled_window), 0);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  gtk_window_set_title(GTK_WINDOW (window), title);
  gtk_container_set_border_width(GTK_CONTAINER (window), 0);

  gtk_container_add(GTK_CONTAINER (event_box), drawing_area);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (scrolled_window), event_box);
  gtk_container_add(GTK_CONTAINER (window), scrolled_window);

  gtk_widget_show(drawing_area);
  gtk_widget_show(event_box);
  gtk_widget_show(scrolled_window);
  gtk_widget_show(window);

  gui_window_list[id].used = 1;
  gui_window_list[id].handle = window;
  gui_window_list[id].close_fn = (close_fn == NULL ? gui_remove_window : close_fn);
  gui_window_list[id].redraw_fn = redraw_fn;
  gui_window_list[id].click_fn = click_fn;
  gui_window_list[id].resize_fn = resize_fn;
  gui_window_list[id].key_fn = key_fn;
  gui_window_list[id].input_fn = input_fn;
  gui_window_list[id].menu_fn = menu_fn;
  gui_window_list[id].width = width;
  gui_window_list[id].height = height;
  gui_window_list[id].menu = menu;
  gui_window_list[id].help = help;
  gui_window_list[id].drawing_area = drawing_area;

  gtk_signal_connect(event_box, "button-press-event", gui_window_press, window);
  gtk_signal_connect(drawing_area, "expose-event", gui_expose_event, window);
  gtk_signal_connect(drawing_area, "size-allocate", gui_size_allocate, window);

  #ifdef DEBUG
  printf("gui_create_window: id = 0x%x, handle = 0x%x\n", id, (unsigned int) window);
  fflush(stdout);
  #endif

  return id;
}


/*
 *  gui_get_free_window_id -- allocate space and return a window id
 *
 *  => nothing
 *
 *  <= window id (gui_window_list index)
 */

gui_window_id gui_get_free_window_id()
{
  static struct gui_window_data *new_gui_window_list;
  int i;

  #ifdef DEBUG
  printf("gui_get_free_window_id: gui_window_list_items = %i\n", gui_window_list_items);
  fflush(stdout);
  #endif

  for (i = 0; i < gui_window_list_items; i++)
  {
    if (!gui_window_list[i].used)
    {
      assert(i >= 0 && i < gui_window_list_items);
      return i;
    }
  }

  new_gui_window_list = realloc(gui_window_list,
                            sizeof(struct gui_window_data) * (gui_window_list_items + WINDOW_LIST_CHUNK));
  if (!new_gui_window_list) gui_raise_error("memory");

  gui_window_list = new_gui_window_list;
  for (i = gui_window_list_items; i < gui_window_list_items + WINDOW_LIST_CHUNK; i++)
    gui_window_list[i].used = 0;

  i = gui_window_list_items;
  gui_window_list_items += WINDOW_LIST_CHUNK;

  #ifdef DEBUG
  printf("gui_get_free_window_id: increased gui_window_list_items = %i\n", gui_window_list_items);
  fflush(stdout);
  #endif

  assert(i >= 0 && i < gui_window_list_items);
  return i;
}


/*
 *  gui_handle_to_window_id -- convert a window handle to a window id
 *
 *  => handle -- wimp window handle
 *
 *  <= window id
 */

gui_window_id gui_handle_to_window_id(GtkWidget *handle)
{
  int i;

  for (i = 0; i < gui_window_list_items; i++)
  {
    if ((gui_window_list[i].used) && (gui_window_list[i].handle == handle))
    {

      #ifdef DEBUG
      printf("gui_handle_to_window_id: handle = 0x%x, id = 0x%x\n", (unsigned int) handle, i);
      fflush(stdout);
      #endif
      return i;
    }
  }

  return gui_WINDOW_UNKNOWN;
}


/*
 *  gui_remove_window -- close and destroy a window
 *
 *  => window_id -- window id from gui_create_window
 *
 *  <= nothing
 */

void gui_remove_window(const gui_window_id window_id)
{
  assert(window_id >= 0 && window_id < gui_window_list_items && gui_window_list[window_id].used);

  #ifdef DEBUG
  printf("gui_remove_window: window_id = 0x%x\n", window_id);
  fflush(stdout);
  #endif

/*   gui_error = xwimp_delete_window(gui_window_list[window_id].handle);
 *   gui_check_error();
 *   gui_window_list[window_id].used = 0;
 */
}


/*
 *  gui_refresh_window -- update a window's contents
 *
 *  => window_id -- window id from gui_create_window
 *
 *  <= nothing
 */

void gui_refresh_window(const gui_window_id window_id)
{
  assert(window_id >= 0 && window_id < gui_window_list_items && gui_window_list[window_id].used);

  #ifdef DEBUG
  printf("gui_refresh_window: window_id = 0x%x\n", window_id);
  fflush(stdout);
  #endif

/*   gui_refresh_box(window_id, 0, 0, gui_window_list[window_id].width, gui_window_list[window_id].height);  */
}


/*
 *  gui_refresh_box -- update part of a window's contents
 *
 *  => window_id -- window id from gui_create_window
 *     x0, y0, x1, y1 -- area to update
 *
 *  <= nothing
 */

void gui_refresh_box(const gui_window_id window_id, unsigned int x0, unsigned int y0,
                     unsigned int x1, unsigned int y1)
{
  signed int invalid[4];
  bool more;
/*   wimp_window_state state;
 *   wimp_draw update;
 */

  assert(window_id >= 0 && window_id < gui_window_list_items && gui_window_list[window_id].used);

  #ifdef DEBUG
  printf("gui_refresh_box: window_id = 0x%x, box %i %i %i %i\n", window_id, x0, y0, x1, y1);
  fflush(stdout);
  #endif

/*   state.w = gui_window_list[window_id].handle;
 *
 *   gui_error = xwimp_get_window_state(&state);
 *   gui_check_error();
 *
 *   gui_window_origin_x = state.visible.x0 - state.xscroll;
 *   gui_window_origin_y = state.visible.y1 - state.yscroll;
 *
 *   #ifdef DEBUG
 *   printf("gui_refresh_box: redraw origin %i %i\n", gui_window_origin_x, gui_window_origin_y);
 *   fflush(stdout);
 *   #endif
 *
 *   update.w = state.w;
 *   update.box.x0 = x0;
 *   update.box.y0 = -y1;
 *   update.box.x1 = x1;
 *   update.box.y1 = -y0;
 *   gui_error = xwimp_update_window(&update, &more);
 *   gui_check_error();
 *
 *   while (more)
 *   {
 *     invalid[0] = update.clip.x0 - gui_window_origin_x;
 *     invalid[1] = gui_window_origin_y - update.clip.y1;
 *     invalid[2] = update.clip.x1 - gui_window_origin_x;
 *     invalid[3] = gui_window_origin_y - update.clip.y0;
 *
 *     #ifdef DEBUG
 *     printf("gui_refresh_box: redraw invalid %i %i %i %i\n",
 *             invalid[0], invalid[1], invalid[2], invalid[3]);
 *     fflush(stdout);
 *     #endif
 *
 *     gui_window_list[window_id].redraw_fn(window_id, invalid);
 *
 *     gui_error = xwimp_get_rectangle(&update, &more);
 *     gui_check_error();
 *   }
 */
}


/*
 *  gui_window_extent -- set window size
 *
 *  => window_id -- window id from gui_create_window
 *     width, height -- new dimensions of window
 *
 *  <= nothing
 */

void gui_window_extent(const gui_window_id window_id, unsigned int width, unsigned int height)
{
  assert(window_id >= 0 && window_id < gui_window_list_items && gui_window_list[window_id].used);

  #ifdef DEBUG
  printf("gui_window_extent: window_id = 0x%x, extent = %i x %i\n", window_id, width, height);
  fflush(stdout);
  #endif

  gtk_drawing_area_size(gui_window_list[window_id].drawing_area, width / 2, height / 2);

  gui_window_list[window_id].width = width;
  gui_window_list[window_id].height = height;
}


/*
 *  gui_fill -- fill a rectangular area during window redraw
 *
 *  => colour -- 0xbbggrr format colour
 *     x0, y0, x1, y1 -- coordinates of rectangle
 */

void gui_fill(unsigned int colour, unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1)
{
  GdkColor gdk_colour;

  printf("gui_fill: box %i %i %i %i\n", x0, y0, x1, y1);

  BGR_TO_GDK(colour, gdk_colour);
  gdk_colormap_alloc_color(gdk_window_get_colormap(gui_redraw_widget->window), &gdk_colour, FALSE, TRUE);
  gdk_gc_set_foreground(gui_redraw_widget->style->fg_gc[gui_redraw_widget->state], &gdk_colour);

  gdk_draw_rectangle(gui_redraw_widget->window, gui_redraw_widget->style->fg_gc[gui_redraw_widget->state],
        TRUE, x0 / 2, y0 / 2, (x1-x0) / 2, (y1-y0) / 2);
}


/*
 *  gui_move_box -- copy a rectangular area within a window
 *
 *  => window_id -- window id from gui_create_window
 *     x0, y0, x1, y1 -- source area
 *     x2, y2 -- destination
 */

void gui_move_box(const gui_window_id window_id, unsigned int x0, unsigned int y0,
                  unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
{

  assert(window_id >= 0 && window_id < gui_window_list_items && gui_window_list[window_id].used);

  #ifdef DEBUG
  printf("gui_move_box: window_id = 0x%x, box = %i %i %i %i, dest = %i %i\n",
         window_id, x0, y0, x1, y1, x2, y2);
  fflush(stdout);
  #endif

/*   gui_error = xwimp_block_copy(gui_window_list[window_id].handle, x0, -y1, x1, -y0, x2, -(y1 + (y2 - y0)));
 *   gui_check_error();
 */
}
