/*   GPDriver source gpd_gui.c
 *   $Id: gpd_gui.c 1.21 2004/11/26 17:01:57 root Exp root $
 *
 *   GPDriver - gimp-print based printer spooler for RISC OS
 *   Copyright (C) 2003 Martin Wuerthner
 *
 *   This program is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU General Public License as published by the Free
 *   Software Foundation; either version 2 of the License, or (at your option)
 *   any later version.
 *
 *   This program 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 General Public License
 *   for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *   Martin Wuerthner <ro-printing@mw-software.com>
 *   Mannheimer Str. 18, 67655 Kaiserslautern, Germany
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "oslib/os.h"
#include "oslib/syslog.h"
#include "oslib/messagetrans.h"
#include "oslib/wimp.h"
#include "gpd_gui.h"

/* the externally visible version number */
#define APPDIR "<GPDriver$Dir>"
#define GPDRIVER_VERSION "1.16 (26-Nov-04)"

messagetrans_control_block global_msgs;

#define DEBUG 0

#include "debug.h"

#ifdef GPDRIVER_GUI

#define APPNAME "GPDriver"

/* we want a GUI-based driver */
#include "oslib/toolbox.h"
#include "oslib/wimp.h"
#include "oslib/displayfield.h"
#include "oslib/slider.h"
#include "oslib/window.h"
#include "oslib/actionbutton.h"
#include "oslib/button.h"
#include "oslib/proginfo.h"

static toolbox_block tbx_block;
static osspriteop_area* spr_area;
static wimp_t task_h;
static toolbox_o main_w;
static wimp_block wblock;

static int gui_initialised = 0;
static int timeslice = 20;   /* centiseconds */
static int ended = 0;
static int paused = 0;
static int aborted = 0;
static int page = 0, pages = 0, pages_printed = 0;
static int last_perc = -1, last_perc2 = -1;

static os_t last_poll;       /* monotonic time */

/* ToolBox component IDs */
#define MAIN_ICON  0x0
#define MAIN_SLIDER  0x1
#define MAIN_FIELD_1 0x2
#define MAIN_FIELD_2 0x3
#define MAIN_PAUSE 0x4
#define MAIN_ABORT 0x5

#define MAIN_FIELD_LEN 40    /* excluding terminator */

#define SLIDER_MAX 320

/* ToolBox events */
#define MAIN_PAUSE_CLICKED 0x1
#define MAIN_ABORT_CLICKED 0x2
#define MAIN_QUIT_CHOSEN 0x3

static void gui_set_suspended(void);
static void gui_set_ended(void);
static void do_poll(wimp_poll_flags flags);
void gui_wimp_error_abort_continue(const char* msg);
static void wait_for_quit(void);

/* initialise the user interface, return 1 for success, 0 for failure */
int gui_init(void)
{
  os_error* err;
  int wimp_version;
  int* msgs = 0;   /* no messages at all */
  int toolbox_events[] = { 0 };   /* all ToolBox event codes */
  err = xtoolbox_initialise(0,310,(wimp_message_list*)msgs,(toolbox_action_list*)toolbox_events,
                            APPDIR,&global_msgs,&tbx_block,
                            &wimp_version, &task_h, &spr_area);
  if (err) {
    gui_wimp_error(err); return 0;
  }
  if (gui_complain(xtoolbox_create_object(0, (toolbox_id)"MainWindow", &main_w))) return 0;
  if (gui_complain(xtoolbox_show_object(0, main_w, toolbox_POSITION_DEFAULT, 0,
                                        toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT))) return 0;
  last_poll = os_read_monotonic_time();
  gui_initialised = 1;
  return 1;
}

void gui_inform(const char* msg, int field)
{
  toolbox_c comp;
  REPORT_C("gui_inform %s",msg)
  if (strlen(msg) > MAIN_FIELD_LEN) {
    msg = "*** FIELD LENGTH EXCEEDED";
  }
  if (field == 1) comp = MAIN_FIELD_1;
  else if (field == 2) comp = MAIN_FIELD_2;
  else return;
  displayfield_set_value(0, main_w, comp, msg);
}

void gui_inform_d(const char* tmsg, int n, int field)
{
  char msg[256];
  sprintf(msg,tmsg,n);
  gui_inform(msg,field);
}

void gui_inform_dd(const char* tmsg, int n1, int n2, int field)
{
  char msg[256];
  sprintf(msg,tmsg,n1,n2);
  gui_inform(msg,field);
}

void gui_inform_ddd(const char* tmsg, int n1, int n2, int n3, int field)
{
  char msg[256];
  sprintf(msg,tmsg,n1,n2,n3);
  gui_inform(msg,field);
}

void gui_window_to_front(toolbox_o window_object)
{
  wimp_window_state state;
  if (gui_complain(xwindow_get_wimp_handle(0,window_object,&state.w))) return;
  if (gui_complain(xwimp_get_window_state(&state))) return;
  state.next=(wimp_w)-1;
  xwimp_open_window((wimp_open*)&state);
}

/* report an error and prompt the user to click on Continue or Abort */
void gui_error3(const char* msg,int fatal)
{
  const char* msg1 = msg;
  const char* msg2 = "";
  const char* p;
  char msgb[MAIN_FIELD_LEN + 1];

  REPORT("error3")
  if (strlen(msg1) > MAIN_FIELD_LEN) {
    /* we need to split the message into two lines */
    int msg1_len;
    p = msg1 + MAIN_FIELD_LEN;
    /* split at a space or a hyphen */
    while(p > msg1 && *p != ' ' && *p != '-') --p;
    /* p points to the space or hyphen or the start of the string */
    if (p > msg1) {
      /* we could split it somehow */
      msg2 = p + 1;
      msg1_len = p - msg1;
      if (*p == '-') ++msg1_len;    /* we can forget about the space but we keep the hyphen */
      strncpy(msgb, msg1, msg1_len);
      msgb[msg1_len] = '\0';
      msg1 = msgb;
    }
  }

  if (strlen(msg1) <= MAIN_FIELD_LEN && strlen(msg2) <= MAIN_FIELD_LEN) {
    /* fully multi-tasking error handling via our main window */
    os_box box;
    gui_inform(msg1, 1); gui_inform(msg2, 2);
    if (fatal) gui_set_ended();
    else gui_set_suspended();
    REPORT("bell")
    xos_bell();              /* beep */
    gui_window_to_front(main_w);
    REPORT("change sprite")
    gui_complain(xbutton_set_value(0, main_w, MAIN_ICON, "gimp_e"));
    REPORT("sprite changed")

    /* we poll and return after the user has clicked on a button */
    if (fatal) wait_for_quit();
    else {
      REPORT("calling poll")
      do {
        do_poll(wimp_MASK_NULL);
      } while(paused && !aborted);
    }
    gui_complain(xbutton_set_value(0, main_w, MAIN_ICON, "gimpprint"));
    /* for some reason, the button is not redrawn properly, so let us do it manually */
    xgadget_get_bbox(0, main_w, MAIN_ICON, &box);
    xwindow_force_redraw(0, main_w, &box);
  }
  else {
    /* fall back - pop up an error box */
    if (fatal) {
      gui_wimp_error2(msg);
      aborted = 1;
      gui_progress_end(TRUE);
    }
    else {
      gui_wimp_error_abort_continue(msg);
    }
  }
  /* the caller can find out whether Abort was clicked by calling gui_aborted() */
}

/* report an error and prompt the user to click on Continue or Abort */
void gui_error2(const char* msg)
{
  REPORT("error2")
  if (gui_initialised) {
    gui_error3(msg,0);
  }
  else {
    gui_wimp_error2(msg);
  }
}

/* as above but substitute %d by the given integer parameter */
void gui_error2_d(const char* msg, int n)
{
  char msg2[256];
  sprintf(msg2, msg, n);
  gui_error2(msg2);
}

void gui_fatal_error2(const char* msg)
{
  if (gui_initialised) {
    gui_error3(msg,1);     /* does not return */
  }
  else {
    gui_wimp_error2(msg);
    aborted = 1;
    gui_progress_end(TRUE);
  }
}

void gui_closedown(int code)
{
  REPORT("gui_closedown")
  exit(code);
}

void gui_progress_start(void)
{
  slider_set_value(0, main_w, MAIN_SLIDER, 0);
}

static void gui_quit_gpdriver(void)
{
  gui_closedown(EXIT_SUCCESS);
}

static void gui_set_suspended(void)
{
  REPORT("gui_set_suspended")
  gui_complain(xactionbutton_set_text(0,main_w,MAIN_PAUSE,gui_lookup("CONT")));
  paused = 1;
}

void dispatch_toolbox_event(void)
{
  toolbox_action* e = (toolbox_action*)&wblock;
  REPORT_D("toolbox event %d",e->action_no)
  switch(e->action_no) {
    case MAIN_QUIT_CHOSEN:
      gui_quit_gpdriver();
      break;
    case MAIN_PAUSE_CLICKED:
      if (ended) {
        /* we have re-labelled the button "Quit", so do just that */
        gui_quit_gpdriver();
      }
      if (paused) {
        /* already paused, so continue */
        gui_inform_dd(gui_lookup("PRINTdd"),page,pages,1);
        gui_inform("",2);  /* clear second line */
        gui_complain(xactionbutton_set_text(0,main_w,MAIN_PAUSE,gui_lookup("PAUSE")));
        last_perc = 0;       /* force redisplay of the percentage */
        paused = 0;
      }
      else {
        /* not paused, so pause */
        gui_inform_dd(gui_lookup("PAUSEDdd"),page,pages,1);
        gui_set_suspended();
      }
      break;
    case MAIN_ABORT_CLICKED:
      aborted = 1;
      break;
    case action_PROG_INFO_ABOUT_TO_BE_SHOWN:
      gui_complain(xproginfo_set_version(0, tbx_block.this_obj, GPDRIVER_VERSION));
      break;
  }
}

void do_poll(wimp_poll_flags flags)
{
  wimp_event_no e = wimp_poll(flags, &wblock, 0);
  switch(e) {
    case wimp_KEY_PRESSED:
      /* pass all keypresses on */
      xwimp_process_key(wblock.key.c);
      break;
    case wimp_USER_MESSAGE: case wimp_USER_MESSAGE_RECORDED:
      if (wblock.message.action == message_QUIT) {
        gui_quit_gpdriver();
      }
      break;
    case toolbox_EVENT:
      dispatch_toolbox_event();
      break;
  }
}

void maybe_poll(int row, int rows)
{
  os_t now = os_read_monotonic_time();
  /* REPORT_D("elapsed time: %d",now-last_poll); */
  if (now - last_poll > timeslice) {
    int perc = 100*row/rows;
    int perc2 = SLIDER_MAX*row/rows;
    /* we have taken our share of processing time */
    if (perc != last_perc) {
      gui_inform_ddd(gui_lookup("PRINTddd"),page,pages,perc,1);
      last_perc = perc;
    }
    if (perc2 != last_perc2) {
      gui_complain(xslider_set_value(0, main_w, MAIN_SLIDER, perc2));
      last_perc2 = perc2;
    }
    do {
      do_poll(0);   /* do not mask any events */
    } while(paused && !aborted);
    last_poll = os_read_monotonic_time();
  }
}

void gui_progress_row(int row, int rows)
{
  /* REPORT_D("row %d",row); */
  maybe_poll(row, rows);
}

static void gui_set_ended(void)
{
  ended = 1;
  gui_complain(xwindow_remove_gadget(0, main_w, MAIN_ABORT));
  gui_complain(xactionbutton_set_text(0, main_w, MAIN_PAUSE, gui_lookup("QUIT")));
}

void gui_progress_end(int keep_open)
{
  REPORT("gui_progress_end")
  gui_set_ended();

  if (aborted) {
    gui_inform_dd(gui_lookup("ABORTEDdd"),pages_printed,pages,1);
    gui_complain(xslider_set_value(0, main_w, MAIN_SLIDER, 0));
  }
  else {
    gui_inform_d(gui_lookup("PRINTEDd"),pages_printed,1);
    gui_complain(xslider_set_value(0, main_w, MAIN_SLIDER, SLIDER_MAX));
  }
  gui_inform("",2);
  if (keep_open) wait_for_quit(); else gui_quit_gpdriver();
}

static void wait_for_quit(void)
{
  /* the program will quit when the Quit button is clicked */
  while(1) {
    do_poll(wimp_MASK_NULL);
  }
}

/* display a Wimp error box */
void gui_wimp_error(os_error* err)
{
  xwimp_report_error(err, 0, APPNAME, NULL);
}

void gui_wimp_error2(const char* msg)
{
  os_error err;
  err.errnum = 0;
  strcpy(err.errmess, msg);
  gui_wimp_error(&err);
}

void gui_wimp_error_abort_continue(const char* msg)
{
  wimp_error_box_selection button;
  os_error err;
  err.errnum = 0;
  strcpy(err.errmess, msg);
  xwimp_report_error_by_category(&err, wimp_ERROR_BOX_OK_ICON + wimp_ERROR_BOX_GIVEN_CATEGORY
                                     + (wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT),
                                APPNAME, "!gpdriver", (osspriteop_area const *)1, gui_lookup("ABORT"), &button);
  if (button == 3) aborted = 1;
}

void gui_error(os_error* err)
{
  if (gui_initialised) {
    gui_error2(err->errmess);
  }
  else gui_wimp_error(err);
}

os_error* gui_complain(os_error* err)
{
  if (err) gui_wimp_error(err);
  return err;
}

int gui_aborted(void)
{
  return aborted;
}

#else  /* !defined(GPDRIVER_GUI) */

/* we want a command-line based driver - can be started in a TaskWindow */

/* initialise the user interface, return 1 for success, 0 for failure */
/* command-line */
int gui_init(void)
{
  int size;
  messagetrans_file_flags flags;
  char* mtrans_buffer;
  if (gui_complain(xmessagetrans_file_info(APPDIR".Messages",&flags, &size))) return 0;
  if ((mtrans_buffer = malloc(size)) == NULL) return 0;
  if (gui_complain(xmessagetrans_open_file(&global_msgs, APPDIR".Messages", mtrans_buffer))) return 0;

  /* OK, we got our messages */
  printf("GPDriver "GPDRIVER_VERSION"\n\n");
  return 1;
}

/* command-line */
void gui_inform(const char* msg, int field)
{
  fputs(msg, stdout); fputc('\n', stdout);
}

/* command-line */
void gui_inform_d(const char* msg, int n, int field)
{
  fprintf(stdout, msg, n); fputc('\n', stdout);
}

/* command-line */
void gui_inform_dd(const char* msg, int n1, int n2, int field)
{
  fprintf(stdout, msg, n1, n2); fputc('\n', stdout);
}

/* command-line */
void gui_closedown(int code)
{
  exit(code);
}

/* command-line */
void gui_progress_start(void)
{
  /*##*/
}

/* command-line */
void gui_progress_row(int row, int rows)
{
  /*##*/
  printf("Processing row %d of %d\n",row,rows);
}

/* command-line */
void gui_progress_end(int pages_printed, int pages)
{
  gui_inform_d("PRINTEDd",pages_printed);
}

/* command-line */
os_error* gui_complain(os_error* err)
{
  if (err) {
    error(err->errmess);
  }
  return err;
}

/* command-line */
void gui_error2(const char* msg)
{
  fprintf(stderr,"%s\n",msg);
  gui_log_msg(msg, 10);
}

/* command-line */
void gui_wimp_error(os_error* err)
{
  gui_error(err);
}

/* command-line */
void gui_wimp_error2(const char* msg)
{
  gui_error2(msg);
}

int gui_aborted(void)
{
  return 0;
}

/* command-line */
void gui_fatal_error2(const char* msg)
{
  fprintf(stderr,"%s\n",msg);
  gui_log_msg(msg,10);
  exit(EXIT_FAILURE);
}

#endif /* GPDRIVER_GUI */

/* shared functions for the Toolbox and command-line implementations */
void gui_error_c(const char* msg, const char* s)
{
  char msg2[256];
  sprintf(msg2,msg,s);
  gui_error2(msg2);
}

void gui_wimp_error_d(const char* tmsg, int n)
{
  char msg[256];
  sprintf(msg,tmsg,n);
  gui_wimp_error2(msg);
}

void gui_fatal_error(os_error* err)
{
  gui_fatal_error2(err->errmess);
}

void gui_log_msg(const char* msg, int priority)
{
  xsyslog_log_message("GPDriver",msg,priority);
}

const char* gui_lookup(const char* token)
{
  static char buffer[256];
  if (gui_complain(xmessagetrans_lookup(&global_msgs, token, buffer, 256, 0, 0, 0, 0, NULL, NULL))) {
    strcpy(buffer,"???");
  }
  return buffer;
}

void gui_set_page(int cp)
{
  page = cp;
}

void gui_set_pages(int pp)
{
  pages = pp;
}

void gui_set_pages_printed(int pp)
{
  REPORT_D("set_pages_printed %d",pp)
  pages_printed = pp;
}
