/*
 *  window_riscos.c -- window handling, RISC OS
 */

#include "gui.h"


/*
 *  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[]),
      wimp_menu *menu,
      const char *bbar,
      const char *help)
{
  wimp_WINDOW(1) window;
  wimp_w handle;
  wimp_window_state open;
  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();

  window.visible.x0 = 100;
  window.visible.y0 = 100;
  window.visible.x1 = window.visible.x0 + width;
  window.visible.y1 = window.visible.y0 + height;
  window.xscroll = window.yscroll = 0;
  window.next = wimp_TOP;
  window.flags = wimp_WINDOW_MOVEABLE | wimp_WINDOW_NEW_FORMAT |
                 wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON |
    (flags & gui_H_SCROLL_BAR ? wimp_WINDOW_HSCROLL | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_SIZE_ICON : 0) |
    (flags & gui_V_SCROLL_BAR ? wimp_WINDOW_VSCROLL | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_SIZE_ICON : 0);
  window.title_fg = wimp_COLOUR_BLACK;
  window.title_bg = wimp_COLOUR_LIGHT_GREY;
  window.work_fg = wimp_COLOUR_BLACK;
//  window.work_bg = wimp_COLOUR_WHITE;
  window.work_bg = wimp_COLOUR_TRANSPARENT;
  window.highlight_bg = wimp_COLOUR_CREAM;
  window.extent.x0 = window.extent.y1 = 0;
  window.extent.y0 = -height;
  window.extent.x1 = width;
  window.title_flags = wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | wimp_ICON_INDIRECTED;
  window.work_flags = wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT;
  window.sprite_area = (osspriteop_area *) 1;
  window.xmin = window.ymin = 1;
  window.title_data.indirected_text.text = title;
  window.title_data.indirected_text.validation = 0;
  window.icon_count = 0;

  gui_error = xwimp_create_window((wimp_window const *) &window, &handle);
  gui_check_error();

  open.w = handle;
  gui_error = xwimp_get_window_state(&open);
  gui_check_error();

  gui_error = xwimp_open_window((wimp_open *) &open);
  gui_check_error();

  gui_window_list[id].used = 1;
  gui_window_list[id].handle = handle;
  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;

  #ifdef DEBUG
  printf("gui_create_window: id = 0x%x, handle = 0x%x\n", id, (unsigned int) handle);
  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;
  unsigned 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)
      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 < 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(wimp_w handle)
{
  unsigned 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 < 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 < 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, 0x10000000, 0x10000000);
}


/*
 *  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)
{
  unsigned int invalid[4];
  bool more;
  wimp_window_state state;
  wimp_draw update;

  assert(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)
{
  os_box box = {0, 0, 0, 0};
  box.y0 = (height == 0 ? 0x80000000 : -height);
  box.x1 = (width == 0 ? 0x7fffffff : width);

  assert(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

  gui_error = xwimp_set_extent(gui_window_list[window_id].handle, &box);
  gui_check_error();

  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)
{
  gui_error = xcolourtrans_set_gcol((os_colour) colour << 8, 0, os_ACTION_OVERWRITE, NULL, NULL);
  gui_check_error();

  gui_error = xos_plot(os_MOVE_TO, (signed int) (gui_window_origin_x + x0), (signed int) (gui_window_origin_y - y0));
  gui_check_error();

  gui_error = xos_plot(os_PLOT_RECTANGLE | os_PLOT_TO,
                       (signed int) (gui_window_origin_x + x1), (signed int) (gui_window_origin_y - y1));
  gui_check_error();
}


/*
 *  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 < 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, (signed int) x0, (signed int) -y1,
        (signed int) x1, (signed int) -y0, (signed int) x2, (signed int) -(y1 + (y2 - y0)));
  gui_check_error();
}
