/*-*-C-*-
 *
 * ResEd Menu CSE: main
 */

#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"
#include "focus.h"
#include "interactor.h"
#include "registry.h"

#include "menuedit.h"
#include "help.h"
#include "props.h"
#include "protocol.h"


/* Globals */

char appdir[FILENAMELEN];       /* contents of Resed$Menu$Dir */
int taskhandle;
int parenttaskhandle;
MsgRec msgs;                    /* global messages file */

/* Statics */

static unsigned int eventmask = (BIT(EV_NULL_REASON_CODE) |
                                 BIT(EV_POINTER_LEAVING_WINDOW) |
                                 BIT(EV_POINTER_ENTERING_WINDOW) );


/*
 * Crank up the Wimp
 */

static error * start_wimp ()
{
    int task;
    error *err;
    char *taskname = message_lookup(&msgs, "TaskName");
    int messages[] = {

        MESSAGE_DRAG_CLAIM,
        MESSAGE_DRAGGING,
        MESSAGE_CLAIM_ENTITY,

        MESSAGE_MENUS_DELETED,
        MESSAGE_MENU_WARNING,

        MESSAGE_DATA_SAVE,
        MESSAGE_DATA_SAVE_ACK,
        MESSAGE_DATA_LOAD,
        MESSAGE_DATA_LOAD_ACK,

        MESSAGE_RAM_FETCH,
        MESSAGE_RAM_TRANSMIT,

        MESSAGE_TASK_CLOSE_DOWN,
        MESSAGE_HELP_REQUEST,
        MESSAGE_MODE_CHANGE,
        MESSAGE_FONT_CHANGED,

        MESSAGE_RESED_OBJECT_LOAD,
        MESSAGE_RESED_OBJECT_LOADED,
        MESSAGE_RESED_OBJECT_SENDING,
        MESSAGE_RESED_OBJECT_SEND,
        MESSAGE_RESED_OBJECT_RENAMED,
        MESSAGE_RESED_OBJECT_DELETED,
        MESSAGE_RESED_OBJECT_NAME,
        MESSAGE_RESED_SPRITES_CHANGED,
        MESSAGE_RESED_KEYCUT_DETAILS,

        0 };

    if (taskname == NULL)
        taskname = "ResEd Menu Editor";         /* fallback */
    strncpy((char *)&task, "TASK", 4);
    err = swi (Wimp_Initialise,
               R0, 310,
               R1, task,
               R2, taskname,
               R3, messages,
               OUT,
               R1, &taskhandle,  END);
    return err;
}


/* 
 * Read in window prototypes for this module and others.
 */

static error * load_prototypes (void)
{
    char name[FILENAMELEN];
    strcpy(name, appdir);
    strcat(name, ".Templates");

    ER ( swi(Wimp_OpenTemplate, R1, name, END) );
    
    ER ( menuedit_load_prototypes() );
    ER ( props_load_prototypes() );

    ER ( swi(Wimp_CloseTemplate, END) );
    return NULL;
}


/*
 * Respond to mouse click events in any window
 */

static error * mouse_click (int *buf)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    unsigned int modifiers = wimp_read_modifiers ();
    void *closure;
    RegistryType type = registry_lookup_window(mouse->windowhandle, &closure);
    switch (type)
    {
    case MenuEdit:
        return menuedit_mouse_click (mouse, modifiers, (MenuObjPtr) closure);
    case MenuDbox:
        return props_menu_dbox_mouse_click (mouse, modifiers, (MenuObjPtr) closure);
    case EntryDbox:
        return props_entry_dbox_mouse_click (mouse, modifiers, (MenuEntryPtr) closure);
    }
    return NULL;
}



/*
 * Handle keypresses.  ESCAPE cancels the current foreground interaction.
 * Other keys may be handled by individual window types, and unknown ones
 * are passed back to the Wimp.
 */

static error * key_pressed (int *buf)
{
    KeyPressPtr key = (KeyPressPtr) buf;
    void *closure;
    Bool consumed = FALSE;

    /* offer ESCAPE to any interactor first */
    if (key->code == 0x1b && interactor_active ())
    {
        interactor_cancel ();   /* Kill current drag, lassoo etc */
        consumed = TRUE;
    }
    else
    {
        switch (registry_lookup_window (key->caret.windowhandle, &closure))
        {
        case MenuEdit:
            ER ( menuedit_key_pressed ((MenuObjPtr) closure, key, &consumed) );
            break;
        case MenuDbox:
            ER ( props_menu_dbox_key_pressed ((MenuObjPtr) closure, key, &consumed) );
            break;
        case EntryDbox:
            ER ( props_entry_dbox_key_pressed ((MenuEntryPtr) closure, key, &consumed) );
            break;
        }
    }

    if (!consumed)
    {
        ER ( swi (Wimp_ProcessKey,  R0, key->code,  END) );
    }
    return NULL;
}


/*
 * Respond to all message events.  Rather annoyingly, we can't determine the
 * window/icon pair without reference to the message type, so the test
 * for the type is here rather than in the window-specific procs.
 */

static error * message (int event, int *buf)
{
    MessageHeaderPtr hdr = (MessageHeaderPtr) buf;
    Bool protocol;

    ER ( protocol_incoming_message (event, buf, &protocol) );
    if (protocol)
        return NULL;            /* handled by protocol message dispatcher */

    switch (event)
    {
    case EV_USER_MESSAGE:
    case EV_USER_MESSAGE_RECORDED:
        switch (hdr->messageid)
        {
        case MESSAGE_QUIT:
            error_exit (0);
        case MESSAGE_TASK_CLOSE_DOWN:
            block
            {
/*                dprintf ("MENU: Received TASK_CLOSE_DOWN %d (shell is %d)\n",
                         hdr->taskhandle, parenttaskhandle); */
                if (hdr->taskhandle == parenttaskhandle)
                    error_exit (0);
            }
            break;
        case MESSAGE_HELP_REQUEST:
            return help_message ((MessageHelpRequestPtr) buf);
/*
        case MESSAGE_DRAGGING:
            return dragdrop_message_dragging ((MessageDraggingPtr) buf);
*/
        case MESSAGE_DATA_SAVE:
            return protocol_send_resed_object_name_request ((MessageDataSavePtr) buf);
        case MESSAGE_MODE_CHANGE:
            return wimp_examine_mode ();
        case MESSAGE_FONT_CHANGED:
            return menuedit_font_changed ();
        case MESSAGE_CLAIM_ENTITY:
            return focus_message_claim_entity ((MessageClaimEntityPtr) buf);
        }
        break;
    case EV_USER_MESSAGE_ACKNOWLEDGE:
        switch (hdr->messageid)
        {
        }
        break;
    }
    return NULL;
}


/*
 * Respond to any open window request events
 */

static error * open_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {
    case MenuEdit:
        return menuedit_reopen_window (win, (MenuObjPtr)closure);
    case MenuDbox:
        return props_reopen_menu_dbox (win, (MenuObjPtr) closure);
    case EntryDbox:
        return props_reopen_entry_dbox (win, (MenuEntryPtr) closure);
    }
    return NULL;
}


/*
 * Respond to any close window request events.  Strictly speaking
 * we don't need to handle this for dboxes (which don't have close
 * icons normally).  Having the code is worthwhile because it lets
 * someone add close icons later if they want them.
 */

static error * close_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {
    case MenuEdit:
        return menuedit_close_window ((MenuObjPtr)closure);
    case MenuDbox:
        return props_close_menu_dbox ((MenuObjPtr) closure);
    case EntryDbox:
        return props_close_entry_dbox ((MenuEntryPtr) closure);
    }
    return NULL;
}


/*
 * Respond to any redraw window request events
 */

static error * redraw_window_request (int *buf)
{
    WindowRedrawPtr redraw = (WindowRedrawPtr) buf;
    void *closure;
    RegistryType type = registry_lookup_window(redraw->handle, &closure);
    
    switch (type)
    {
    case MenuEdit:
        ER ( menuedit_redraw_window (redraw, (MenuObjPtr)closure) );
        break;
    }
    return NULL;
}


static void usage ()
{
    fprintf (stderr, "%s\n", message_lookup(&msgs, "Usage"));
    error_exit (0);
}


/*
 * Get next Wimp event into *event, details filled in buf
 */

error * main_next_event (int *event, int *buf)
{
    unsigned int mask = eventmask & interactor_event_mask();

    if (mask & BIT(EV_NULL_REASON_CODE)) /* NULL events disabled */
    {
        ER ( swi (Wimp_Poll,  R0, mask,  R1, buf,  OUT,  R0, event,  END) );
    }
    else
    {
        int after;
        ER ( swi (OS_ReadMonotonicTime,  OUT,  R0, &after,  END) );
        after += interactor_timeout();
        ER ( swi (Wimp_PollIdle,  R0, mask,  R1, buf,  R2, after,  OUT,  R0, event,  END) );
    }
    return NULL;
}


/*
 * Act on a Wimp event
 */

error * main_dispatch_event (int event, int *buf)
{
    Bool consumed;
    ER ( interactor_event (event, buf, &consumed) );
    if (!consumed)
        switch (event)
        {
        case EV_REDRAW_WINDOW_REQUEST:
            ER ( redraw_window_request (buf) );
            break;
        case EV_OPEN_WINDOW_REQUEST:
            ER ( open_window_request (buf) );
            break;
        case EV_CLOSE_WINDOW_REQUEST:
            ER ( close_window_request (buf) );
            break;
        case EV_MOUSE_CLICK:
            ER ( mouse_click(buf) );
            break ;
        case EV_KEY_PRESSED:
            ER ( key_pressed(buf) );
            break;
        case EV_USER_MESSAGE:
        case EV_USER_MESSAGE_RECORDED:
        case EV_USER_MESSAGE_ACKNOWLEDGE:
            ER ( message(event, buf) );
            break;
        }
    return NULL;
}


/*
 * Main
 */

int main (int argc, char **argv)
{
    int buf[64], event;

    strcpy(appdir, getenv(APPDIR));               /* want to copy this before Wimp_Poll called */
    EE ( message_openfile (&msgs, "<" APPDIR ">.Messages", 256) );    /* APPDIR can't have changed yet */
    debug_file("<" APPDIR ">.logfile");

    if (argc == 2)
        parenttaskhandle = atoi (argv[1]);
    else
        usage();

/*    dprintf ("MENU: parenttaskhandle = %d\n", parenttaskhandle);  */
            
    EE ( wimp_examine_mode() );
    EE ( start_wimp () );
    EE ( load_prototypes () );

    /* Main loop */

    while (1)
    {
        EE ( main_next_event (&event, buf) );
        ED ( main_dispatch_event (event, buf) );
    }
}
