/*
    callbacks for message ports
    5/2/93 Bernhard Fastenrath
*/

#include <dos.h>
#include <signal.h>
#include <dos/dos.h>
#include <exec/types.h>
#include <pragmas/exec_pragmas.h>

#include "callbacks.h"

static struct CbPort *find_cbport (struct MsgPort *port);

static struct MinList port_list = { NULL, NULL, NULL };
static ULONG signals = SIGBREAKF_CTRL_C;

ULONG free_signals = 0;
callback signal_callback = NULL;

void
RemoveAllCallbacks ()
{
  struct Callback *cnode;
  struct CbPort *pnode;

  if (!port_list.mlh_Head) return;

  while (pnode = RemTail (&port_list))
  {
    while (cnode = RemTail (&pnode -> callbacks))
      free (cnode);

    free (pnode);
  }
  signals = SIGBREAKF_CTRL_C;
}

void
RemoveCallback (struct Callback *cb)
{
  struct CbPort *node;

  Remove (cb);
  free (cb);

  if (EmptyMinList (&cb -> cbport -> callbacks)) {
    Remove (cb -> cbport);
    free (cb -> cbport);
  }
  signals = SIGBREAKF_CTRL_C | free_signals;
  for (node = port_list.mlh_Head; node -> node.mln_Succ; node = node -> node.mln_Succ)
    signals |= node -> sig;
}

struct Callback *
AddPortCallback (struct MsgPort *port, callback fun, ULONG class)
{
  struct Callback *new_cb;
  struct CbPort *pn;

  if (!port_list.mlh_Head)
    InitListHeader (&port_list);

  if (!(pn = find_cbport (port)))
    return NULL;

  if (!(new_cb = Malloc (struct Callback)))
    return NULL;

  new_cb -> class = class;
  new_cb -> function = fun;
  new_cb -> cbport = pn;

  AddHead (&pn -> callbacks, new_cb);

  return new_cb;
}

void
SetSignalCallback (ULONG signal_mask, callback fun)
{
  free_signals = signal_mask;
  signals |= free_signals;  
  signal_callback = fun;
}

void
AppMainLoop ()
{
  struct IntuiMessage *msg;
  ULONG received = 0;
  struct CbPort *pnode;
  struct Callback *cnode;

  while (!(received & SIGBREAKF_CTRL_C))
  {
    received = Wait (signals);

    if (free_signals & received)
      signal_callback (received);
      
    for (pnode = port_list.mlh_Head; pnode -> node.mln_Succ; pnode = pnode -> node.mln_Succ)
    {
      while (msg = GetMsg (pnode -> port))
      {
	for (cnode = pnode -> callbacks.mlh_Head; cnode -> node.mln_Succ; cnode = cnode -> node.mln_Succ)
	{
	  /* cnode -> class has to be zero for non-IntuiMessages which
	     don't have a `class' field following the message struct!
          */
	  if (!cnode -> class || msg -> Class == cnode -> class)
	    if (cnode -> function (msg, cnode)) {
	      msg = NULL;
              break;
            }
	}
	if (msg && msg -> ExecMessage.mn_ReplyPort != pnode -> port)
	  ReplyMsg (msg);
      }
    }
  }
#if 0
  printf ("AppMainLoop: User Break detected.\n");
#endif
}

void
InitListHeader (struct MinList *list)
{
  list -> mlh_Head = &list -> mlh_Tail;
  list -> mlh_TailPred = &list -> mlh_Head;
  list -> mlh_Tail = 0;
}

static struct CbPort*
find_cbport (struct MsgPort *port)
{
  struct CbPort *pn;

  for (pn = port_list.mlh_Head; pn -> node.mln_Succ; pn = pn -> node.mln_Succ)
    if (pn -> port == port)
      return pn;

  if (!(pn = Malloc (struct CbPort)))
    return NULL;
  
  pn -> port = port;
  signals |= pn -> sig = 1L << port -> mp_SigBit;

  InitListHeader (&pn -> callbacks);

  AddHead (&port_list, (struct MinNode *) pn);

  return pn;
}
