#include "kernel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "oslib/os.h"
#include "sockets.h"
#include "Desk.Core.h"
#include "Desk.Resource.h"
#include "Desk.Event.h"
#include "Desk.EventMsg.h"
#include "Desk.Msgs.h"
#include "Desk.WimpSWIs.h"


#include "def.h"
#include "defstruc.h"
#include "connstrc.h"
#include "eventstrc.h"

/*#include "module.h"*/
#include "event.h"
#include "syslogif.h"
#include "sockwatch.h"

static EventHandlers eventHandlers;
static int *pollWord = NULL;

static int event_handler_null(SOCKET socket, socket_event_type reason)
{
  /* Really, really, don't want to get here....*/
  xsyslog_irq_logmessage(SYSLOG_FILE,"event_handler_null: should never call null event handler\n",LOG_WARNING);
  return EVENT_CLAIM;
}



Desk_bool event_pollword_handler(Desk_event_pollblock *pollBlock, void *ref)
/*int event_eventv_handler(_kernel_swi_regs *r, void *privateWord)*/
{
  int i=0;
  Desk_bool result=EVENT_PASSON;
  char buf[256];
  int bufGot=0;
  int reason,count,pollCount,foundCount,tmp;
  SOCKET socket;
  OSERROR *error;
  /* temporary hack to make this compile...
  int tr[10];
  typedef struct {
    int r[10];
  } fakeRegs;
  fakeRegs *r = (fakeRegs *) tr;

  UNUSED(ref);
  r->r[0] = Event_Internet;
  r->r[2] = 42;
  r->r[1] = 1;*/

  SYSLOG_ENTRY("event_pollword_handler");

  /* May be called to handle multiple sockets */
  if (pollBlock->data.pollword.address == (int) pollWord)
  {
    pollCount = *pollWord;
    *pollWord = 0;
    for (i=0; i<eventHandlers.handlerCount; i++)
    {
      error=socketwatch_modifycount(eventHandlers.handler[i].socket,0,&count,&reason);
      /* Decrement the count of events and write it back - currently the reason code
       * gets overwritten if more than one event happens
      error=socketwatch_modifycount(eventHandlers.handler[i].socket,(count>0) ? (count-1):0,&tmp,&reason);*/
      xsyslogf(SYSLOG_FILE,LOG_DEBUG_LOW,"event_pollword_handler: handlers = %d, checking sock %d, error = %X, count = %d, reason = %d\n",eventHandlers.handlerCount,eventHandlers.handler[i].socket,error,count,reason);
      if (count>0 && error==NULL)
      {
        foundCount++;
        /* If it's ours, call the relevant handler, and make sure
           we don't call the null handler to save time */
        if (eventHandlers.handler[i].routine != event_handler_null)
        {
          /*xsyslog_irq_irqmode(TRUE);*/
          xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_eventv_handler: socket %d, count %d, reason %d\n",eventHandlers.handler[i].socket,count,reason);
          if (reason == 2)
          {
            bufGot = recv(eventHandlers.handler[i].socket, buf, sizeof(bufGot), MSG_OOB);
            xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_eventv_handler: OOB data\n");
            xsyslog_irq_logdata(SYSLOG_FILE,LOG_DEBUG_LOW,buf,bufGot,0);
          }
          if (reason == 1)
          {
            bufGot = recv(eventHandlers.handler[i].socket, buf, sizeof(bufGot), MSG_DONTWAIT | MSG_PEEK);
            if (bufGot == 0)
            {
              xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_eventv_handler: close msg\n");
              reason = 3;
            }
          }
          result = (eventHandlers.handler[i].routine) (eventHandlers.handler[i].socket, reason);
          /*xsyslog_irq_irqmode(FALSE);*/
          /*return result;*/
        }
        /*else
          return EVENT_CLAIM;*/ /* it was ours, but the handler's not set up */
      }
    }
    xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_pollword_handler: reported %d events, found %d\n",pollCount,foundCount);
    SYSLOG_EXIT("event_pollword_handler");
    return result; /* not found - someone else's socket */
  }

  SYSLOG_EXIT("event_pollword_handler- nf");
  return result; /* not our pollword */
}


OSERROR *event_initialise(void *modulePrivateWord)
{
  int i=0;

  eventHandlers.handlerCount = 0;
  for (i=0; i<MAX_EVENT_HANDLERS; i++)
  {
    eventHandlers.handler[i].socket = INVALID_SOCKET;
    eventHandlers.handler[i].routine = event_handler_null;
  }

  pollWord = NULL;

  Desk_Event_Claim(Desk_event_NONZEROPOLLWORD, Desk_event_ANY,
    Desk_event_ANY, event_pollword_handler, (void *) NULL);

  return NO_OSERROR;
}

OSERROR *event_finalise(void *modulePrivateWord)
{
  int i=0;

  for (i=0; i<MAX_EVENT_HANDLERS; i++)
  {
    /* remove the pollword counter for them, and ignore any errors */
    if (eventHandlers.handler[i].socket != INVALID_SOCKET)
      event_deregister_handler(eventHandlers.handler[i].socket,
                               eventHandlers.handler[i].routine);
    eventHandlers.handler[i].socket = INVALID_SOCKET;
    eventHandlers.handler[i].routine = event_handler_null;
  }
  Desk_Event_Release(Desk_event_NONZEROPOLLWORD, Desk_event_ANY,
    Desk_event_ANY, event_pollword_handler, (void *) NULL);
  return NO_OSERROR;
}

OSERROR *event_register_handler(SOCKET socket, int (*routine) (SOCKET, socket_event_type))
{
  int i=0;
  int *count,*reason;

  SYSLOG_ENTRY("event_register_handler");
  xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_register_handler: reg socket %d\n",socket);

  if (routine == NULL)
  {
    /* if we're supplied with a null routine, all we can do is
     * remove it (and possibly flag an error)
     */
    xsyslog_irq_logmessage(SYSLOG_FILE,"event_register_handler: attempt to register null handler\n",LOG_WARNING);
    SYSLOG_EXIT("event_register_handler");
    return event_deregister_handler(socket,routine);
  }

  /* If this socket is already registered, change it's handler */
  for (i=0; i<eventHandlers.handlerCount; i++)
  {
    if (eventHandlers.handler[i].socket == socket)
    {
      eventHandlers.handler[i].routine = routine;
      SYSLOG_EXIT("event_register_handler");
      return NO_OSERROR;
    }
  }

  /* if there's an unused handler slot, use that */
  for (i=0; i<eventHandlers.handlerCount; i++)
  {
    if (eventHandlers.handler[i].socket == INVALID_SOCKET)
    {
      eventHandlers.handler[i].socket = socket;
      eventHandlers.handler[i].routine = routine;
      SYSLOG_EXIT("event_register_handler");
      return socketwatch_register(socket,&count,&reason,&pollWord);
    }
  }

  eventHandlers.handler[eventHandlers.handlerCount].socket = socket;
  eventHandlers.handler[eventHandlers.handlerCount].routine = routine;
  eventHandlers.handlerCount++;

  SYSLOG_EXIT("event_register_handler");
  return socketwatch_register(socket,&count,&reason,&pollWord);

}


OSERROR *event_deregister_handler(SOCKET socket, int (*routine) (SOCKET, socket_event_type))
{
  int i=0;

  xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"event_deregister_handler: dereg socket %d\n",socket);
  if (routine == NULL)
  {
    /* if we're supplied with a null routine, what do we do?
     */
    printf("event_deregister_handler: attempt to remove null handler\n");
    xsyslog_irq_logmessage(SYSLOG_FILE,"event_deregister_handler: attempt to remove null handler\n",LOG_WARNING);
    return NO_OSERROR;
  }

  /* If this socket is already registered, remove it's handler */
  for (i=0; i<eventHandlers.handlerCount; i++)
  {
    if (eventHandlers.handler[i].socket == socket)
    {
      eventHandlers.handler[i].routine = event_handler_null;
      eventHandlers.handler[i].socket  = INVALID_SOCKET;
      /* if this is last on the list, reduce the count, to save time
       * checking in the event routine
       */
      if (i==eventHandlers.handlerCount-1)
        eventHandlers.handlerCount--;
      return socketwatch_deregister(socket);
    }
  }

  /* we didn't find it - what do we do? */
  printf("event_deregister_handler: attempt to remove unregistered handler\n");
  xsyslog_irq_logmessage(SYSLOG_FILE,"event_deregister_handler: attempt to remove unregistered handler\n",LOG_WARNING);
  return NO_OSERROR;

}

void event_poll_single(Desk_bool maskOutNullPoll)
{
  Desk_event_pollblock pollBlock;
  Desk_event_pollmask mask;

  mask = Desk_Event_mask;

  /* If we're running normally, we're only interested in a pollworld-non-zero
   * if the pollword is currently zero
   * If we're in the middle of something and just want to slot in
   * a quick poll (hence maskOutNullPoll=FALSE)
   * then we don't want to know about pollwords
   */
  if ((pollWord == NULL) || (maskOutNullPoll == TRUE))
  {
    mask.data.nonzeropollword = 0;
  }
  else
  {
    mask.data.nonzeropollword = 1;
  }
  if (maskOutNullPoll == FALSE)
    mask.data.null = 0;
  else
    mask.data.null = 1;

  Desk_Wimp_Poll3(mask,&pollBlock,pollWord);

  xsyslogf(SYSLOG_FILE,LOG_DEBUG_VERYLOW,"event_poll_single: event type %d,pollmask = %X (to poll = %X), pollWord=%X\n",pollBlock.type,Desk_Event_mask.value,mask.value,pollWord);
  Desk_Event_Process(&pollBlock);
}
