#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <setjmp.h>

#include "kernel.h"
#include "swis.h"

#include "osmodule.h"
#include "osfile.h"
#include "wimpspriteop.h"
#include "taskwindow.h"
#include "toolbox.h"
#include "window.h"
#include "menu.h"
#include "proginfo.h"
#include "stringset.h"
#include "writablefield.h"
#include "radiobutton.h"
#include "optionbutton.h"
#include "actionbutton.h"
#include "draggable.h"
#include "hourglass.h"
#include "help.h"

#include "event.h"

#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include "socklib.h"
#include "inetlib.h"

#include "rc5wimp.h"
#include "task.h"
#include "display.h"
#include "iniread.h"
#include "config.h"
#include "graph.h"
#include "sound.h"
#include "power.h"

#define VERSION "2.7104.428"
#define DATE "13-Jan-99"

#define action_Quit 1
#define action_Stop 2
#define action_Start 3
#define action_Benchmark 4
#define action_Test 5
#define action_Flush 6
#define action_Fetch 7
#define action_Update 8
#define action_OpenURL 9
#define action_Configure 10
#define action_DESStats 11
#define action_RC5Stats 12
#define action_Graph 13
#define action_Hide 14
#define action_Help 15
#define action_About 16
#define action_Unpause 17
#define action_Pause 18
#define action_NewVersion 19
#define action_ForceFlush 20
#define action_ForceFetch 21

#define action_MooTest 0x2400
#define action_SetPassword 0x2410
#define action_ChoicesSet 0x2600
#define action_ChoicesTab 0x27fd

#define cmp_Start 0
#define cmp_Stop 1
#define cmp_Test 2
#define cmp_Benchmark 3
#define cmp_Configure 4
#define cmp_Pause 6
#define cmp_Unpause 7

#define message_RC5_CHECK 0xF7040
#define message_RC5_HELLO 0xF7041

#define toolbox_POSITION_CENTRED ((toolbox_position_tag)3)

static messagetrans_control_block mfd;
static toolbox_block id_block;
static int wimpver;
static char msgs_buffer[256];

client_mode stopping_for;

bool shutting_down, autolaunching, first_config;

bool done_setjmp;

wimp_t taskhandle, quitsender;
toolbox_o displayhandle, clientmenuhandle, ibarmenuhandle;

osspriteop_area *spritearea;

char our_dir[256];

static bool hiding;

static jmp_buf restart;

char *msgs_lookup(const char *token)
{
    int used;

    messagetrans_lookup(&mfd, token, msgs_buffer, sizeof msgs_buffer,
                        NULL, NULL, NULL, NULL, &used);

    msgs_buffer[used]='\0';

    return msgs_buffer;
}

static void sighandler(int sig)
{
    char Quit[32];
    messagetrans_control_block cb;
    wimp_error_box_selection s;
    wimp_error_box_flags ok;

    NOT_USED(sig);

    if (sig == SIGILL || sig == SIGSEGV || sig == SIGFPE || sig == SIGOSERROR)
    {
        unsigned int *regdump, *os_regdump;

        os_change_environment(os_HANDLER_CALL_BACK, 0, 0, 0,
                              0, (byte **) &regdump);
        os_regdump = (unsigned int *)
                     os_change_environment(os_HANDLER_EXCEPTION_REGISTERS, 0, 0, 0,
                                           0, 0);
        memcpy(os_regdump, regdump, 4*16);
    }

    messagetrans_open_file(&cb, "WindowManager:Messages", 0);
    messagetrans_lookup(&cb, "Quit:Quit", Quit, sizeof Quit, 0, 0, 0, 0, 0);
    messagetrans_close_file(&cb);

    ok = done_setjmp ? wimp_ERROR_BOX_OK_ICON : 0;

    if (wimpver >= 322)
    	s = wimp_report_error_by_category((os_error *) _kernel_last_oserror(),
    	        ok | (wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT),
                msgs_lookup("_TaskName"), "!rc5des", wimpspriteop_AREA, Quit);
    else
    	s = wimp_report_error((os_error *) _kernel_last_oserror(),
    	    	    	      ok | wimp_ERROR_BOX_CANCEL_ICON,
    	    	    	      msgs_lookup("_TaskName"));

    if (s == wimp_ERROR_BOX_SELECTED_OK && ok)
    {
        signal(sig, sighandler);
        longjmp(restart, 1);
    }

    client_kill();

    exit(EXIT_FAILURE);
}

static enum ibstate { no_icon, DNet_icon, Bovine_icon, Monarch_icon, Paused_icon } iconbar_state;
wimp_i iconbar_icon;
char spritename[12];

void show_iconbar()
{
    enum ibstate wanted = DNet_icon;

    if (mode == Run)
    {
        if (contest_type == DES_being_processed)
            wanted = Monarch_icon;
        else if (contest_type == RC5_being_processed)
            wanted = Bovine_icon;
    }

    if (paused)
        wanted = Paused_icon;

    if (hiding)
        return;

    if (wanted != iconbar_state)
    {
        wimp_icon_create ic;

        strcpy(spritename, wanted == Bovine_icon  ? "bovine"  :
                           wanted == Monarch_icon ? "monarch" :
                           wanted == Paused_icon  ? "paused"  :
                                                    "!rc5des");

        if (iconbar_state == no_icon)
        {
            ic.w = wimp_ICON_BAR_RIGHT_LOW_PRIORITY;
            ic.icon.extent.x0 = 0;
            ic.icon.extent.y0 = 0;
            ic.icon.extent.x1 = 68;
            ic.icon.extent.y1 = 68;
            ic.icon.flags = wimp_ICON_SPRITE |
                            wimp_ICON_HCENTRED |
                            wimp_ICON_VCENTRED |
                            wimp_ICON_INDIRECTED |
                            wimp_ICON_NEEDS_HELP |
                            (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT) |
                            (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT) |
                            (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);
        	ic.icon.data.indirected_sprite.id = (osspriteop_id) spritename;
        	ic.icon.data.indirected_sprite.area = spritearea;
        	ic.icon.data.indirected_sprite.size = sizeof spritename;
            iconbar_icon = wimp_create_icon_prioritised(1, &ic);
        }
        else
            wimp_set_icon_state(wimp_ICON_BAR, iconbar_icon, 0, 0);

        iconbar_state = wanted;
    }
}

void hide_iconbar()
{
    if (iconbar_state != no_icon)
    {
        wimp_delete_icon(wimp_ICON_BAR, iconbar_icon);
        iconbar_state = no_icon;
    }
}

static int quit_handler(wimp_message *message, void *handle)
{
    client_kill();

    exit(EXIT_SUCCESS);

    return 1;
}

bool key_pressed_handler(wimp_event_no event_code, wimp_block *block,
                         toolbox_block *id_block, void *handle)
{
    if (block->key.c >= 0x100)
        wimp_process_key(block->key.c);

    return 0;
}

bool iconbar_click_handler(wimp_event_no event_code, wimp_block *block,
                           toolbox_block *id_block, void *handle)
{
    if (block->pointer.w != wimp_ICON_BAR)
        return FALSE;

    if (block->pointer.buttons == wimp_CLICK_SELECT)
        toolbox_show_object(0, displayhandle, toolbox_POSITION_DEFAULT,
                            NULL, toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
    else if (block->pointer.buttons == wimp_CLICK_MENU)
    {
        toolbox_position pos;

        pos.top_left.x = block->pointer.pos.x - 64;
        pos.top_left.y = 96 + menu_get_height(0, ibarmenuhandle);

        toolbox_show_object(0, ibarmenuhandle, toolbox_POSITION_TOP_LEFT,
                            &pos, toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
    }
    else if (block->pointer.buttons == wimp_CLICK_ADJUST)
    {
        if (paused)
            client_unpause();
        else
            client_pause();
    }
    return TRUE;
}

static int prequit_handler(wimp_message *message, void *handle)
{
    if (client_running() && !(message->data.prequit.flags & wimp_PRE_QUIT_TASK_ONLY))
    {
        /* Desktop shutdown in progress. We want to give the child time to die */
        quitsender = message->sender;
        message->your_ref = message->my_ref;
        wimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE, message, quitsender);
        stopping_for = Shutdown;
        client_break(TRUE);
    }

    return 1;
}

static int data_open_handler(wimp_message *message, void *handle)
{
    if (hiding &&
        message->data.data_xfer.file_type == osfile_TYPE_APPLICATION &&
        strcasecmp(our_dir, message->data.data_xfer.file_name) == 0)
    {
        hiding = FALSE;

        toolbox_show_object(0, displayhandle, toolbox_POSITION_DEFAULT, NULL,
            	            toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
        show_iconbar();

        message->action = message_DATA_LOAD_ACK;
        message->your_ref = message->my_ref;
        wimp_send_message(wimp_USER_MESSAGE, message, message->sender);
        return 1;
    }

    return 0;
}

static int help_request_handler(wimp_message *message, void *handle)
{
    wimp_message m;
    help_message_request *help = (help_message_request *) &message->data;
    const char *p;

    if (help->w != wimp_ICON_BAR)
    	return 0;

    p = msgs_lookup("IbarHelp");
    m.size = (20 + strlen(p) + 1 + 3) &~ 3;
    m.your_ref = message->my_ref;
    m.action = message_HELP_REPLY;
    strcpy((char *) &m.data, p);

    wimp_send_message(wimp_USER_MESSAGE, &m, message->sender);

    return 1;
}

static int stop_handler(bits event_code, toolbox_action *event,
                       toolbox_block *id, void *handle)
{
    stopping_for = Stop;
    client_break(FALSE);

    return 1;
}

void fade_client_menu()
{
    menu_set_fade(0, clientmenuhandle, cmp_Start, mode == Run);
    menu_set_fade(0, clientmenuhandle, cmp_Stop, mode == Stop);
    menu_set_fade(0, clientmenuhandle, cmp_Test, mode == Test);
    menu_set_fade(0, clientmenuhandle, cmp_Benchmark, mode == Benchmark);
    menu_set_fade(0, clientmenuhandle, cmp_Pause, mode == Stop || paused);
    menu_set_fade(0, clientmenuhandle, cmp_Unpause, !paused);
}

static int start_handler(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    if (client_running())
    {
        stopping_for = Run;
        client_break(FALSE);
    }
    else
        client_start(Run);

    return 1;
}

static int choices_close_handler(bits event_code, toolbox_action *event,
                                 toolbox_block *id, void *handle)
{
    if (first_config)
    {
        first_config = FALSE;
        if (ReadConfig())
            exit(0);

        client_start(Run);

        toolbox_show_object(0, displayhandle, toolbox_POSITION_DEFAULT, NULL,
                            toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
    }

    return 1;
}

static int clientmenu_show_handler(bits event_code, toolbox_action *event,
                                   toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    fade_client_menu();

    return 1;
}

static int operation_handler(bits event_code, toolbox_action *event,
                             toolbox_block *id, void *handle)
{
    client_mode op;

    NOT_USED(event); NOT_USED(id); NOT_USED(handle);

    switch (event_code)
    {
        case action_Benchmark: op = Benchmark; break;
        case action_Test:      op = Test; break;
        case action_Fetch:     op = Fetch; break;
        case action_Flush:     op = Flush; break;
        case action_Update:    op = Update; break;
        case action_Configure: op = Configure; break;
        case action_ForceFlush:op = ForceFlush; break;
        case action_ForceFetch:op = ForceFetch; break;
        default:               op = Stop; break;
    }

    if (op == Configure)
    {
        /* No need to stop */
        configure();
    }
    else if (client_running())
    {
        stopping_for = op;
        client_break(FALSE);
    }
    else
    {
        client_start(op);
    }

    return 1;
}

static int pause_handler(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)
{
    if (event_code == action_Pause)
        client_pause();
    else
        client_unpause();

    return 1;
}

static int quitmenu_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    if (client_running())
    {
        stopping_for = Quit;
        client_break(TRUE);
    }
    else
    	exit(EXIT_SUCCESS);

    return 1;
}

static int hide_handler(bits event_code, toolbox_action *event,
                        toolbox_block *id, void *handle)
{
    hiding=TRUE;

    //toolbox_hide_object(0, choices_handle);
    hide_iconbar();
    //toolbox_hide_object(0, displayhandle);

    return 1;
}

#ifndef URI_Dispatch
#define URI_Dispatch 0x4E381
#endif

#ifndef message_URI_RETURN_RESULT
#define message_URI_RETURN_RESULT 0x4E383
#endif

void report_error(os_error *e)
{
    if (e)
        wimp_report_error_by_category(e,
                wimp_ERROR_BOX_OK_ICON |
                wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
                msgs_lookup("_TaskName"), "!rc5des", wimpspriteop_AREA, 0);

}

static int uri_result_handler(wimp_message *message, void *handle)
{
    NOT_USED(handle);

    if (message->data.reserved[0] & 1)
    {
        os_error err;
        err.errnum = 1;
        strcpy(err.errmess, msgs_lookup("NoBrowse"));

        report_error(&err);
    }

    return 1;
}

void dispatch_uri(const char *uri)
{
    os_error *e;
    os_error err;

    e = (os_error *) _swix(URI_Dispatch, _INR(0,2), 1, uri, taskhandle);

    if (e->errnum == 0x1E6)
    {
        if (xosmodule_load("System:Modules.Network.URI") == NULL)
        {
            xwimp_start_task("Desktop", NULL);
            e = (os_error *) _swix(URI_Dispatch, _INR(0,2), 1, uri, taskhandle);
        }
    }

    if (e->errnum == 0x1E6)
    {
        err.errnum = 1;
        strcpy(err.errmess, msgs_lookup("NoURIhandler"));
        e=&err;
    }

    if (e)
        report_error(e);

}

static int open_url_handler(bits event_code, toolbox_action *event,
                            toolbox_block *id, void *handle)
{
    char uri[256];

    menu_get_entry_text(0, id->this_obj, id->this_cmp, uri, sizeof uri);

    dispatch_uri(uri);

    return 1;
}

static int FetchHttpDocument(const char *hostname, unsigned short port,
                             const char *pathname, const char *filename,
                             char *output, size_t outsize)
{
    char fetch[512];
    int fetchlen;
    int s = -1;
    struct sockaddr_in sin;
    struct hostent *hp;
    char *p;
    int success = 0;

    xhourglass_on();

    if ((hp = gethostbyname(hostname)) == NULL)
        goto out;

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        goto out;

    memset(&sin, 0, sizeof sin);
    memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_family = hp->h_addrtype;
    sin.sin_port = htons(port);

    if (connect(s, (struct sockaddr *) &sin, sizeof sin) < 0)
        goto out;

    fetchlen = sprintf(fetch,
                   "GET %s%s HTTP/1.0\r\n"
                   "Host: %s:%d\r\n"
                   "User-Agent: DCTI-Client/" VERSION "\r\n"
                   "\r\n",

                   pathname, filename,
                   hostname, port);

    if (send(s, fetch, fetchlen, 0) < fetchlen)
        goto out;

    fetchlen = 0;

    while (fetchlen < outsize)
    {
        int len = recv(s, output + fetchlen, outsize - fetchlen, 0);
        if (len > 0)
        {
            fetchlen += len;
            if (fetchlen == outsize)
                break;
        }
        else
            break;
    }

    if (fetchlen == 0)
        goto out;

    /* Knock off headers */
    output[outsize - 1] = 0;

    p = strstr(output, "\r\n\r\n");
    if (!p)
        goto out;

    memmove(output, p, output + outsize - p);

    success = 1;

out:
    if (s >= 0)
        socketclose(s);

    xhourglass_off();

    return success;
}

static int new_version_handler(bits event_code, toolbox_action *event,
                               toolbox_block *id, void *handle)
{
    char output[2048];
    char *response, *version, *url;
    os_error err;

    if (!FetchHttpDocument("www.distributed.net", 80, "/cgi/",
        "latestclient.cgi?os=riscos&cpu=arm&extra=gui", output, sizeof output))
    {
        err.errnum = 1;
        strcpy(err.errmess, msgs_lookup("BadVFetch"));
        report_error(&err);
    }
    else
    {
        response = strtok(output, "\r\n");
        version = strtok(NULL, "\r\n");
        url = strtok(NULL, "\r\n");

        if (strcmp(response, "VERSIONCHECK") == 0)
        {
            if (strcmp(version, VERSION) != 0)
            {
                wimp_error_box_selection s;

                err.errnum = 1;
                messagetrans_lookup(&mfd, "GetVerQ", err.errmess, sizeof err.errmess,
                                    VERSION, version, NULL, NULL, NULL);

                if (wimpver >= 322)
                {
                    char temp[256];
                    strcpy(temp, msgs_lookup("CancDown"));
                    s = wimp_report_error_by_category(&err,
                                                      wimp_ERROR_BOX_SHORT_TITLE|wimp_ERROR_BOX_NO_BEEP|
                                                      (wimp_ERROR_BOX_CATEGORY_QUESTION << wimp_ERROR_BOX_CATEGORY_SHIFT),
                                                      msgs_lookup("UpgAvail"), "!rc5des", wimpspriteop_AREA, temp);
                }
                else
                {
                    s = wimp_report_error(&err, wimp_ERROR_BOX_OK_ICON|wimp_ERROR_BOX_CANCEL_ICON|
                                                wimp_ERROR_BOX_SHORT_TITLE|wimp_ERROR_BOX_NO_BEEP,
                                                msgs_lookup("UpgAvail"));
                }

                if (s == wimp_ERROR_BOX_SELECTED_OK || s == 3)
                    dispatch_uri(url);
            }
            else
            {
                err.errnum = 1;
                messagetrans_lookup(&mfd, "NoNewVer", err.errmess, sizeof err.errmess,
                                    VERSION, NULL, NULL, NULL, NULL);

                wimp_report_error_by_category(&err, wimp_ERROR_BOX_OK_ICON|
                                                    wimp_ERROR_BOX_SHORT_TITLE|wimp_ERROR_BOX_NO_BEEP|
                                                    (wimp_ERROR_BOX_CATEGORY_INFO << wimp_ERROR_BOX_CATEGORY_SHIFT),
                                              msgs_lookup("NoUpg"), "!rc5des", wimpspriteop_AREA, NULL);
            }
        }
        else
        {
            err.errnum = 1;
            strcpy(err.errmess, msgs_lookup("BadVResp"));
            report_error(&err);
        }
    }

    return 1;
}

static int open_stats_handler(bits event_code, toolbox_action *event,
                              toolbox_block *id, void *handle)
{
    char uri[256];

    sprintf(uri, "http://%sstats.distributed.net/emsummary.idc?EM=%s",
                 event_code == action_DESStats ? "des" : "rc5",
                 user_id());

    dispatch_uri(uri);

    return 1;
}

static int help_handler(bits event_code, toolbox_action *event,
                        toolbox_block *id, void *handle)
{
    char buffer[1024];

    sprintf(buffer, "Filer_Run %s.!Help", our_dir);
    report_error(xos_cli(buffer));

    return 1;
}

static int open_graph_handler(bits event_code, toolbox_action *event,
                              toolbox_block *id, void *handle)
{
    new_graph_window();

    return 1;
}

static int error_handler(bits event_code, toolbox_action *event,
                         toolbox_block *id, void *handle)
{
    NOT_USED(event_code); NOT_USED(id); NOT_USED(handle);

    wimp_report_error_by_category((os_error *) &event->data.error,
        wimp_ERROR_BOX_OK_ICON |
        wimp_ERROR_BOX_CATEGORY_ERROR << wimp_ERROR_BOX_CATEGORY_SHIFT,
        msgs_lookup("_TaskName"),
        "!rc5des",
        wimpspriteop_AREA,
        0);

    return 1;
}

static int create_handler(bits event_code, toolbox_action *event,
                          toolbox_block *id, void *handle)
{
    char *name=event->data.created.name;

    NOT_USED(event_code); NOT_USED(handle);

    if (strcmp(name, "Display")==0)
    {
        event_register_wimp_handler(id->this_obj, wimp_REDRAW_WINDOW_REQUEST,
                                    display_redraw_handler, 0);
    	displayhandle = id->this_obj;
    }
    else if (strcmp(name, "ClientMenu")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_MENU_ABOUT_TO_BE_SHOWN,
                                       clientmenu_show_handler, 0);
        clientmenuhandle = id->this_obj;
    }
    else if (strcmp(name, "Choices")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_ChoicesTab, choices_tab_handler, 0);
        event_register_toolbox_handler(id->this_obj, action_ChoicesSet, choices_set_handler, 0);
        event_register_toolbox_handler(id->this_obj, action_WINDOW_DIALOGUE_COMPLETED, choices_close_handler, 0);
        event_register_toolbox_handler(id->this_obj, action_ACTION_BUTTON_SELECTED, choices_button_handler, 0);
        choices_handle = id->this_obj;
    }
    else if (strncmp(name, "ChSub", 5)==0)
    {
        int n = atoi(name+5);
        choices_pane[n] = id->this_obj;

        switch (n)
        {
          case 1:
            event_register_toolbox_handler(id->this_obj, action_STRING_SET_VALUE_CHANGED, netset_handler, 0);
            event_register_toolbox_handler(id->this_obj, action_STRING_SET_VALUE_CHANGED, netkey_handler, 0);
            event_register_toolbox_handler(id->this_obj, action_OPTION_BUTTON_STATE_CHANGED, netopt_handler, 0);
            break;
          case 2:
            event_register_toolbox_handler(id->this_obj, action_RADIO_BUTTON_STATE_CHANGED, connset_handler, 0);
            break;
          case 3:
            event_register_toolbox_handler(id->this_obj, action_OPTION_BUTTON_STATE_CHANGED, ckset_handler, 0);
            break;
          case 4:
            event_register_toolbox_handler(id->this_obj, action_OPTION_BUTTON_STATE_CHANGED, logset_handler, 0);
            event_register_toolbox_handler(id->this_obj, action_MooTest, moo_test, 0);
            break;
        }
    }
    else if (strcmp(name, "ProxyPass")==0)
    {
        proxypasswordhandle = id->this_obj;
        event_register_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
                                       proxypass_show_handler, 0);
    }
    else if (strcmp(name, "IbarMenu")==0)
    {
        ibarmenuhandle = id->this_obj;
    }
    else if (strcmp(name, "SaveAs")==0)
    {
        event_register_toolbox_handler(id->this_obj, action_WINDOW_ABOUT_TO_BE_SHOWN,
                                       saveas_show_handler, 0);
        event_register_toolbox_handler(id->this_obj, action_ACTION_BUTTON_SELECTED,
                                       saveas_save_handler, 0);
        event_register_toolbox_handler(id->this_obj, action_DRAGGABLE_DRAG_ENDED,
                                       saveas_drag_handler, 0);
    }
    else if (strcmp(name, "ProgInfo")==0)
    {
        proginfo_set_version(0, id->this_obj, VERSION " (" DATE ")");
    }


    return 1;
}

void initialise_wimp(void)
{
    const
    static wimp_MESSAGE_LIST(13) messages={message_TASK_WINDOW_OUTPUT,
                                           message_TASK_WINDOW_EGO,
                                           message_TASK_WINDOW_MORIO,
                                           message_URI_RETURN_RESULT,
                                           message_RC5_CHECK,
                                           message_RC5_HELLO,
                                           message_PREQUIT,
                                           message_DATA_OPEN,
                                           message_DATA_LOAD,
                                           message_DATA_SAVE,
                                           message_DATA_SAVE_ACK,
                                           message_HELP_REQUEST,
                                           0
                                          };

    const
    static toolbox_ACTION_LIST(35) action_nos={action_ERROR,
                                               action_OBJECT_AUTO_CREATED,
                                               action_WINDOW_ABOUT_TO_BE_SHOWN,
                                               action_MENU_ABOUT_TO_BE_SHOWN,
                                               action_STRING_SET_VALUE_CHANGED,
                                               action_WINDOW_DIALOGUE_COMPLETED,
                                               action_RADIO_BUTTON_STATE_CHANGED,
                                               action_OPTION_BUTTON_STATE_CHANGED,
                                               action_DRAGGABLE_DRAG_ENDED,
                                               action_ACTION_BUTTON_SELECTED,
                                               action_Quit,
                                               action_Stop,
                                               action_Start,
                                               action_Benchmark,
                                               action_Pause,
                                               action_Unpause,
                                               action_Test,
                                               action_Fetch,
                                               action_Flush,
                                               action_ForceFetch,
                                               action_ForceFlush,
                                               action_Update,
                                               action_OpenURL,
                                               action_NewVersion,
                                               action_Configure,
                                               action_DESStats,
                                               action_RC5Stats,
                                               action_Help,
                                               action_ChoicesTab,
                                               action_ChoicesSet,
                                               action_Graph,
                                               action_Hide,
                                               action_MooTest,
                                               action_SetPassword,
                                               0};

    int i;

    for (i=os_HANDLER_UNDEFINED_INSTRUCTION; i<=os_HANDLER_ADDRESS_EXCEPTION; i++)
    {
        void *h;
        byte *a, *b;

    	h=os_read_default_handler(i, &a, &b);
    	os_change_environment(i, h, a, b, 0, 0);
    }

    signal(SIGFPE, sighandler);
    signal(SIGILL, sighandler);
    signal(SIGSEGV, sighandler);
    signal(SIGSTAK, sighandler);
    signal(SIGOSERROR, sighandler);

    strcpy(our_dir, getenv("RC5DES$Dir"));

    taskhandle =
    toolbox_initialise(NONE, wimp_VERSION_RO3, (wimp_message_list *) &messages,
                       (toolbox_action_list *) &action_nos,
                       our_dir, &mfd, &id_block, &wimpver, &spritearea);


    #if SIGABRT != 1 || SIGSTAK != 7
    #error "Signals have been pissed around with!"
    #endif

    for (i=SIGABRT; i<=SIGSTAK; i++)
    	signal(i, sighandler);

    signal(SIGOSERROR, sighandler);

    event_initialise(&id_block);

    event_set_mask(wimp_MASK_NULL |
                   wimp_MASK_LEAVING |
                   wimp_MASK_ENTERING |
                   wimp_MASK_LOSE |
                   wimp_MASK_GAIN);

    event_register_message_handler(message_QUIT, quit_handler, 0);
    event_register_message_handler(message_PREQUIT, prequit_handler, 0);
    event_register_message_handler(message_DATA_OPEN, data_open_handler, 0);
    event_register_message_handler(message_TASK_WINDOW_EGO, taskwindow_ego_handler, 0);
    event_register_message_handler(message_TASK_WINDOW_MORIO, taskwindow_morio_handler, 0);
    event_register_message_handler(message_TASK_WINDOW_OUTPUT, taskwindow_output_handler, 0);
    event_register_message_handler(message_DATA_SAVE_ACK, saveas_ack_handler, 0);
    event_register_message_handler(message_URI_RETURN_RESULT, uri_result_handler, 0);
    event_register_message_handler(message_HELP_REQUEST, help_request_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Quit, quitmenu_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Stop, stop_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Start, start_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Test, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Benchmark, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Fetch, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Flush, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Update, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_ForceFetch, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_ForceFlush, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Pause, pause_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Unpause, pause_handler, 0);
    event_register_toolbox_handler(event_ANY, action_OpenURL, open_url_handler, 0);
    event_register_toolbox_handler(event_ANY, action_NewVersion, new_version_handler, 0);
    event_register_toolbox_handler(event_ANY, action_DESStats, open_stats_handler, 0);
    event_register_toolbox_handler(event_ANY, action_RC5Stats, open_stats_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Help, help_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Configure, operation_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Graph, open_graph_handler, 0);
    event_register_toolbox_handler(event_ANY, action_Hide, hide_handler, 0);
    event_register_toolbox_handler(event_ANY, action_SetPassword, set_password_handler, 0);
    event_register_toolbox_handler(event_ANY, action_ERROR, error_handler, 0);
    event_register_toolbox_handler(event_ANY, action_OBJECT_AUTO_CREATED, create_handler, 0);
    event_register_wimp_handler(event_ANY, wimp_MOUSE_CLICK, iconbar_click_handler, 0);
    event_register_wimp_handler(event_ANY, wimp_KEY_PRESSED, key_pressed_handler, 0);

    // Make absolutely certain...
    atexit(client_kill);
}

static int check_handler(wimp_message *message, void *handle)
{
    /* Very important not to reply to ourself... */
    if (message->sender != taskhandle)
    {
        message->size = 24;
        message->your_ref = message->my_ref;
        message->action = message_RC5_HELLO;
        *(int *)&message->data = hiding ? 1 : 0;

        wimp_send_message(wimp_USER_MESSAGE, message, message->sender);

    	if (hiding)
    	{
    	    hiding = FALSE;
    	    show_iconbar();
            toolbox_show_object(0, displayhandle, toolbox_POSITION_DEFAULT, NULL,
            	                   toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
        }

    }

    return 1;
}

static int hello_handler(wimp_message *message, void *handle)
{
    int flags = *(int *)&message->data;
    os_error err;

    if (!(flags & 1))
    {
        err.errnum=1;
        strcpy(err.errmess, msgs_lookup("NotAgain"));
        report_error(&err);
    }

    exit(1);

    return 1;
}

static int check_bounce_handler(wimp_event_no event_code, wimp_block *event,
                                toolbox_block *id, void *handle)
{
    if (event->message.action != message_RC5_CHECK)
    	return 0;

    /* No response to check - start away! */
    if (ReadConfig())
    {
        hiding = FALSE;
        stopping_for = Run;
        first_config = TRUE;
        show_iconbar();
        configure();
    }
    else
    {
        hiding = autolaunching && hide();
        if (!autolaunching)
            toolbox_show_object(0, displayhandle, toolbox_POSITION_DEFAULT, NULL,
            	                toolbox_NULL_OBJECT, toolbox_NULL_COMPONENT);
        if (!hiding)
            show_iconbar();

        autolaunching = FALSE;
        client_start(Run);
    }

    return 1;
}

static void check_other_instances()
{
    wimp_message m;
    m.size = 20;
    m.your_ref = 0;
    m.action = message_RC5_CHECK;
    wimp_send_message(wimp_USER_MESSAGE_RECORDED, &m, wimp_BROADCAST);

    event_register_message_handler(message_RC5_CHECK, check_handler, 0);
    event_register_message_handler(message_RC5_HELLO, hello_handler, 0);
    event_register_wimp_handler(event_ANY, wimp_USER_MESSAGE_ACKNOWLEDGE, check_bounce_handler, 0);
}

int main(int argc, char **argv)
{
    initialise_wimp();

    display_init();

    check_other_instances();

    srand(os_read_monotonic_time());

    if (argc >= 2 && strcasecmp(argv[1], "-run") == 0)
        autolaunching = TRUE;

    setjmp(restart);

    done_setjmp = TRUE;

    for (;;)
    {
        event_poll(0, 0, 0);

#ifdef PORTABLE_POWER_CHECK
        check_mains_power();
#endif
    }

    return 0;
}
