/*
 *  main_riscos.c -- portable gui library, RISC OS
 */

#include "gui.h"

wimp_t gui_wimp_task;                          /* wimp task handle */
os_error *gui_error;                           /* last error block pointer */
const char *gui_error_message;                 /* last error message */
int gui_error_line;                            /* last error line */
jmp_buf gui_error_jump;                        /* longjmp buffer for error handling */
bool quit = 0;

char **gui_message;

struct gui_window_data *gui_window_list;       /* array of window details */
unsigned int gui_window_list_items;            /* length of windows list */
signed int gui_window_origin_x, gui_window_origin_y;

static void redraw_window_request(wimp_draw *redraw);
static void open_window_request(wimp_open *open);
static void close_window_request(wimp_close *close);
static void mouse_click(wimp_pointer *pointer);
static void key_pressed(wimp_key *key);
static void menu_selection(wimp_selection *selection);
static void user_message(wimp_message *message);
static void help_request(wimp_message *message, help_message_request *request);


/****************************************************************************************/
/*  main()                                                                              */
/****************************************************************************************/

int main(int argc, char *argv[])
{
  gui_initialise();
  task_initialise();

  gui_event_loop();

  task_finalise();

  return 0;
}


/****************************************************************************************/
/*  Initialisation                                                                      */
/****************************************************************************************/

/*
 *  gui_initialise -- initialise the program with the os or window manager
 *
 *  => name -- program name to pass to the os if applicable
 *
 *  <= nothing
 */

void gui_initialise()
{
  char file_name[30];
  wimp_MESSAGE_LIST(2) messages;

  gui_log("gui_initialise: task_name = '%s'\n", task_name);

  if (setjmp(gui_error_jump))
  {
    gui_log("gui_initialise: *** error %i: %s ***\n", gui_error_line, gui_error_message);

    printf("%s (%i)\n", gui_error_message, gui_error_line);
    fflush(stdout);
    exit(1);
  }

  messages.messages[0] = message_HELP_REQUEST;
  messages.messages[1] = message_QUIT;

  gui_error = xwimp_initialise(wimp_VERSION_RO3, task_name,
        (wimp_message_list const *) &messages,
        NULL, &gui_wimp_task);
  gui_check_error();

  gui_window_list = NULL;
  gui_window_list_items = 0;

  sprintf(file_name, "<%s$Messages>", task_name);
  gui_message = gui_messages_read(file_name);

  gui_log("gui_initialise: gui_wimp_task = 0x%x\n", (unsigned int) gui_wimp_task);
}


/****************************************************************************************/
/*  Event handling                                                                      */
/****************************************************************************************/

/*
 *  gui_event_loop -- receive and handle os or window manager events
 *
 *  => nothing
 *
 *  <= does not return directly, except on exit
 */

#define MASK (wimp_MASK_NULL | wimp_MASK_LEAVING | wimp_MASK_ENTERING | wimp_MASK_LOSE | wimp_MASK_GAIN | wimp_MASK_POLLWORD)

static gui_window_id menu_window_id;

void gui_event_loop(void)
{
  wimp_block block;
  wimp_event_no event;
  os_error err;

  if (setjmp(gui_error_jump))
  {
    gui_log("gui_event_loop: *** error %i: %s ***\n", gui_error_line, gui_error_message);

    err.errnum = 1;
    sprintf(err.errmess, "%s (%i)\n", gui_error_message, gui_error_line);
    xwimp_report_error(&err, wimp_ERROR_BOX_OK_ICON, task_name, NULL);
  }

  while (!quit)
  {
    gui_error = xwimp_poll(MASK, &block, NULL, &event);
    gui_check_error();

    gui_log("gui_event_loop: event = %i\n", event);
    switch (event)
    {
      case wimp_REDRAW_WINDOW_REQUEST: redraw_window_request(&block.redraw); break;
      case wimp_OPEN_WINDOW_REQUEST:   open_window_request(&block.open);     break;
      case wimp_CLOSE_WINDOW_REQUEST:  close_window_request(&block.close);   break;
      case wimp_MOUSE_CLICK:           mouse_click(&block.pointer);          break;
      case wimp_KEY_PRESSED:           key_pressed(&block.key);              break;
      case wimp_MENU_SELECTION:        menu_selection(&block.selection);     break;
      case wimp_USER_MESSAGE:
      case wimp_USER_MESSAGE_RECORDED: user_message(&block.message);         break;
    }
  }
}


/*
 *  redraw window request
 */

void redraw_window_request(wimp_draw *redraw)
{
  bool more;
  unsigned int invalid[4];     /* why was this signed ?? */
  gui_window_id window_id = gui_handle_to_window_id(redraw->w);

  assert(window_id == gui_WINDOW_UNKNOWN ||
         (window_id < gui_window_list_items && gui_window_list[window_id].used));

  gui_error = xwimp_redraw_window(redraw, &more);
  gui_check_error();

  gui_window_origin_x = redraw->box.x0 - redraw->xscroll;
  gui_window_origin_y = redraw->box.y1 - redraw->yscroll;

  gui_log("gui_event_loop: redraw origin %i %i\n", gui_window_origin_x, gui_window_origin_y);

  while (more)
  {
    if (window_id != gui_WINDOW_UNKNOWN && gui_window_list[window_id].redraw_fn)
    {
      invalid[0] = redraw->clip.x0 - gui_window_origin_x;
      invalid[1] = gui_window_origin_y - redraw->clip.y1;
      invalid[2] = redraw->clip.x1 - gui_window_origin_x;
      invalid[3] = gui_window_origin_y - redraw->clip.y0;

      gui_log("gui_event_loop: redraw invalid %i %i %i %i\n",
          invalid[0], invalid[1], invalid[2], invalid[3]);

      gui_window_list[window_id].redraw_fn(window_id, invalid);
    }
    gui_error = xwimp_get_rectangle(redraw, &more);
    gui_check_error();
  }
}


/*
 *  open window request
 */

void open_window_request(wimp_open *open)
{
  gui_window_id window_id = gui_handle_to_window_id(open->w);

  gui_error = xwimp_open_window(open);
  gui_check_error();

  if (window_id != gui_WINDOW_UNKNOWN)
  {
    assert(window_id < gui_window_list_items && gui_window_list[window_id].used);
    if ((unsigned int) (open->visible.x1 - open->visible.x0) != gui_window_list[window_id].width ||
        (unsigned int) (open->visible.y1 - open->visible.y0) != gui_window_list[window_id].height)
    {
      gui_window_list[window_id].width = open->visible.x1 - open->visible.x0;
      gui_window_list[window_id].height = open->visible.y1 - open->visible.y0;
      gui_log("open_window_request: resized to %i %i\n",
            gui_window_list[window_id].width, gui_window_list[window_id].height);
      if (gui_window_list[window_id].resize_fn)
        gui_window_list[window_id].resize_fn(window_id,
              gui_window_list[window_id].width, gui_window_list[window_id].height);
    }
  }
}


/*
 *  close window request
 */

void close_window_request(wimp_close *close)
{
  gui_window_id window_id = gui_handle_to_window_id(close->w);

  gui_log("gui_event_loop: window_id = 0x%x\n", window_id);

  if (window_id != gui_WINDOW_UNKNOWN)
  {
    assert(window_id < gui_window_list_items && gui_window_list[window_id].used);
    gui_window_list[window_id].close_fn(window_id);
  }
}


/*
 *  mouse click
 */

void mouse_click(wimp_pointer *pointer)
{
  gui_window_id window_id = gui_handle_to_window_id(pointer->w);

  gui_log("gui_event_loop: window_id = 0x%x\n", window_id);

  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 && (pointer->buttons & wimp_CLICK_MENU))
    {
      gui_error = xwimp_create_menu(gui_window_list[window_id].menu,
                       pointer->pos.x - 64, pointer->pos.y);
      gui_check_error();
      menu_window_id = window_id;
    }
    else if (gui_window_list[window_id].click_fn)
    {
      unsigned int x, y, z;
      wimp_window_state state;

      state.w = pointer->w;
      gui_error = xwimp_get_window_state(&state);
      gui_check_error();

      x = pointer->pos.x - (state.visible.x0 - state.xscroll);
      y = (state.visible.y1 - state.yscroll) - pointer->pos.y;
      z = (pointer->buttons & wimp_CLICK_SELECT ? gui_CLICK :
          (pointer->buttons & wimp_CLICK_ADJUST ? gui_CLICK_RIGHT :
          (pointer->buttons & wimp_DRAG_SELECT  ? gui_DRAG :
          (pointer->buttons & wimp_DRAG_ADJUST  ? gui_DRAG_RIGHT : 0))));
      gui_window_list[window_id].click_fn(window_id, x, y, z);
    }
  }
}


/*
 *  key pressed
 */

void key_pressed(wimp_key *key)
{
  gui_window_id window_id = gui_handle_to_window_id(key->w);

  gui_log("gui_event_loop: window_id = 0x%x\n", window_id);

  if (window_id != gui_WINDOW_UNKNOWN)
  {
    assert(window_id < gui_window_list_items && gui_window_list[window_id].used);

    if ((key->c & (1<<31)) || (key->c < 256))
    {
      if (gui_window_list[window_id].input_fn)
        gui_window_list[window_id].input_fn(window_id, (wchar_t) (key->c & 0xffff));
    }
    else
    {
      if (gui_window_list[window_id].key_fn)
        gui_window_list[window_id].key_fn(window_id, (unsigned int) key->c);
    }
  }
}


/*
 *  menu selection
 */

void menu_selection(wimp_selection *selection)
{
  wimp_pointer pointer;

  gui_error = xwimp_get_pointer_info(&pointer);
  gui_check_error();

  gui_window_list[menu_window_id].menu_fn(menu_window_id, (unsigned int *) selection->items);

  if (pointer.buttons == wimp_CLICK_ADJUST)
  {
    gui_error = xwimp_create_menu(gui_window_list[menu_window_id].menu, 0, 0);
    gui_check_error();
  }
}


/*
 *  user message, user message recorded
 */

void user_message(wimp_message *message)
{
  gui_log("gui_event_loop: block.message.action = 0x%x\n", message->action);

  switch (message->action)
  {
    case message_QUIT:
      quit = 1;
      break;
    case message_HELP_REQUEST:
      help_request(message, (help_message_request *) &message->data);
      break;
  }
}

/*
 *  interactive help request
 */

void help_request(wimp_message *message, help_message_request *request)
{
  const char *help;
  wimp_message reply;
  gui_window_id window_id = gui_handle_to_window_id(request->w);

  if (window_id != gui_WINDOW_UNKNOWN)
  {
    if (gui_window_list[window_id].used == DIALOG && request->i >= 0)
      help = gui_window_list[window_id].icon[request->i].help;
    else
      help = gui_window_list[window_id].help;

    gui_log(help);
    reply.size = (20 + strlen(help) + 3) & ~3u;
    reply.your_ref = message->my_ref;
    reply.action = message_HELP_REPLY;
    strcpy((char *) &reply.data.reserved, help);
    gui_error = xwimp_send_message(wimp_USER_MESSAGE, &reply, message->sender);
    gui_check_error();
  }
}
