/* Title:   wimpevent.c
 * Purpose: functions for wimp event dispatching
 * Author:  IDJ
 * History: 19-Jun-94: IDJ: created
 *
 */

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

#include "os.h"

#include "wimpevent.h"
#include "x.h"

#define MAX_WIMP_HANDLER 20

static event_wimp_handler_item    *Handlers[MAX_WIMP_HANDLER+1];
static event_wimp_handler_item    *All_Handlers = 0;

#define MATCHES(h, object_id, event_code, handler, handle) \
   ((h)->object_id == (object_id) && (h)->event_code == (event_code) && \
         (h)->handler == (handler) && (h)->handle == (handle))

static int Matches (int event_code, event_wimp_handler_item *h, toolbox_block *id_block)

{
    /*
     * first see if "other" wimp event handler, and interested in event
     * then look to see if interested in all objects,
     * then interest in this object.
     */

    if (event_code >= MAX_WIMP_HANDLER && h->event_code != event_code && h->event_code != -1)
        return 0;

   return h->object_id == event_ANY || h->object_id == id_block->this_obj;
}

static void Call (int event_code, event_wimp_handler_item *h, wimp_block *poll_block, toolbox_block *id_block, int *claimed)

{  for (; h != NULL; h = h->next)
      if (Matches (event_code, h, id_block) && h->handler != NULL)
         /* call the handler */
         if (h->handler (event_code, poll_block, id_block, h->handle))
         {  *claimed = TRUE;
            return;
         }

    *claimed = FALSE;
}

extern void wimpevent_dispatch (int event_code, wimp_block *poll_block, toolbox_block *id_block)

{ /*
   * if it's not a known wimp event, use dft handler, else we call any wimp handlers
   * on the chain for this event.  If they return non-zero, they have claimed
   * the event.
   */

   bool claimed = FALSE;

   Call (event_code, Handlers [event_code < MAX_WIMP_HANDLER?
         event_code: MAX_WIMP_HANDLER - 1], poll_block, id_block,
         &claimed);
   if (!claimed)
      Call (event_code, All_Handlers, poll_block, id_block, &claimed);
}

/* --------------------------- registering handlers for wimp events ----------------------------- */

void wimpevent_register_wimp_handler (toolbox_o object_id, int event_code, event_wimp_handler *handler, void *handle)
{
    event_wimp_handler_item *h, *new_h;
    int                   elem;

    /*
     * see if there's one there already
     */

    /*
     * start looking down the linked list which hangs off the array element for this event
     * Any events > 19 are just handled by a default case.
     */

    if (event_code < MAX_WIMP_HANDLER)
        elem = event_code;
    else
        elem = MAX_WIMP_HANDLER;

    if (event_code == -1)
        h =  All_Handlers;
    else
        h = Handlers[elem];


    while (h != NULL)
    {
        /*
         * if there's already a handler, just return.
         */

        if (MATCHES (h, object_id, event_code, handler, handle))
            return;

        h = h->next;
    }


    /*
     * make a new entry in the list for this event
     */

    new_h = x_ALLOC (sizeof *new_h);
    new_h->object_id  = object_id;
    new_h->event_code = event_code;
    new_h->handler    = handler;
    new_h->handle     = handle;

    if (event_code == -1)
    {
        new_h->next               = All_Handlers;
        All_Handlers   = new_h;
    }
    else
    {
        new_h->next               = Handlers[elem];
        Handlers[elem] = new_h;
    }
}



void wimpevent_deregister_wimp_handler (toolbox_o object_id, int event_code, event_wimp_handler *handler, void *handle)
{
    event_wimp_handler_item *h, *prev_h;
    event_wimp_handler_item **listhead;
    int elem;

    /*
     * see if handler registered, and if so delete it.
     */

    /*
     * start looking down the linked list which hangs off the array element for this event
     */

    if (event_code <= MAX_WIMP_HANDLER-1)
        elem = event_code;
    else
        elem = MAX_WIMP_HANDLER;

    if (event_code == -1)
    {
        h = prev_h = All_Handlers;
        listhead = &All_Handlers;
    }
    else
    {
        h = prev_h = Handlers[elem];
        listhead = &Handlers[elem];
    }

    while (h != NULL)
    {
        /*
         * see if there's a handler, which matches.
         */

        if (MATCHES (h, object_id, event_code, handler, handle))
            break;

        prev_h = h;
        h = h->next;
    }


    /*
     * delete the handler from the list
     */

    if (h != NULL)
    {
        if (h == *listhead)
            *listhead = h->next;
        else
            prev_h->next = h->next;

        x_FREE (h, sizeof *h);
    }
}
