/* Title:   tbevent.c
 * Purpose: dispatching toolbox events.
 * Author:  IDJ
 * History: 19-Jun-94: IDJ: created
 *
 */

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

#include "messagetrans.h"

#include "tbevent.h"
/* #include "riscos.h" */
#include "x.h"

static event_toolbox_handler_item *Handlers[256];

static event_toolbox_handler_item *All_Handlers = 0;

static int Hash (int word)

{  /* hashing algorithm: add ascending bytes, with carry, and use lower byte as result */

   int temp = (word & 0xff) + (word >> 8 & 0xff) + (word >> 16 & 0xff) +
         (word >> 24 & 0xff);

   return temp + (temp >> 8) & 0xff;
}

#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)


/* ------------------------------------ dispatching toolbox events -------------------------------- */

static int Matches (int event_code, event_toolbox_handler_item *h, toolbox_block *id_block)
{
    /*
     * first look to see if interested in all objects of all classes,
     * then interest in this object.
     */

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

    if (h->object_id == event_ANY)      /* all objects */
        return 1;
    else if (h->object_id == id_block->this_obj)    /* this object */
        return 1;

    return 0;
}

static bool Call (event_toolbox_handler_item *h,
      wimp_block *poll_block, toolbox_block *id_block)

{  toolbox_action *action = (toolbox_action *) poll_block->reserved;

   for (; h != NULL; h = h->next)
      if (Matches (action->action_no, h, id_block) &&
            h->handler != NULL)
      {
         if (h->handler (action->action_no, action,
               id_block, h->handle))
            return TRUE;
      }

   return FALSE;
}

extern void tbevent_dispatch (wimp_block *poll_block,
      toolbox_block *id_block)

{  toolbox_action *action = (toolbox_action *) poll_block->reserved;

   if (!Call (Handlers [Hash (action->action_no)], poll_block, id_block))
      Call (All_Handlers, poll_block, id_block);
}

/* -------------------------- registering handlers for toolbox events ------------------------------ */

void tbevent_register_toolbox_handler (toolbox_o object_id, int
      event_code, event_toolbox_handler *handler, void *handle)

{   event_toolbox_handler_item *h, *new_h;
    int elem;

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

    elem = Hash(event_code);

    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;
   }
}



/* --------------------------- deregistering toolbox event handlers --------------------------------- */

void tbevent_deregister_toolbox_handler (toolbox_o object_id,
      int event_code, event_toolbox_handler *handler, void *handle)

{
    event_toolbox_handler_item *h, *prev_h;
    event_toolbox_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
     */

    elem = Hash(event_code);

    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);
    }
}
