//=========================================================================
// Summary  : */
// Filename : EventService.cc
// Author   : */
// Project  : */
// Revision : 1
// Created  : 2000/08/19
// Modified : 2000/08/19
//=========================================================================
// Description :
//=========================================================================
#include "EventService.h"

#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <sys/kernel.h>
#include "Syslog.h"
#include "VehicleConfigurator.h"

EventService::EventService(const char *name, Task *task)
{
  _name = strdup(name);
  _task = task;
}


EventService::~EventService()
{
  free((void *)_name);
}


void EventService::subscribe(TaskInterface *client,
			     EventCode eventCode,
			     Callback callback)
{
  /* IMPORTANT! Note: This function is copied, byte-for-byte, in TaskServer::subscribe().
     If you change something here, change it in TaskServer::subscribe(), too! */

  int publisherNode;
  pid_t localProxyPid=-1, remoteProxyPid=-1;
  Boolean debug = False;

  dprintf("EventService::subscribe() - pid %d subscribing to server %s, event code %d.",
  	getpid(), client->serverName(), eventCode);

  // First, let's find out if we've already subscribed to this event. It's not likely, but if
  //  there's some piece of code that just subscribes to something on another node willy-nilly,
  //  we'll end up with tons of remote proxies unless we check. Here goes...
  for (int i = 0; i < _eventTable.size(); i++) {
	  EventTableEntry *entry;
	  _eventTable.get(i, &entry);
	  if ( ( entry->_eventCode == eventCode ) && ( entry->_callback == callback )
		&& ( !strcmp( entry->_client->serverName(), client->serverName() ) ) )
	  {
		  // Good news, we've already subscribed!
		  dprintf("EventService::subscribe() - already subscribed to event %d from %s.\n",
			eventCode, client->serverName() );
		  return;
	  }
  }

  publisherNode = VehicleConfigurator::getNodeFromIFName( (char *)client->serverName() );

  if ( publisherNode != getnid() )
  {
	  dprintf("EventService::subscribe() - publisher is on another node.");

      TaskServer::Event *event = new TaskServer::Event();

      event->eventCode = eventCode;
      event->serverPid = VehicleConfigurator::getServerPidFromIFName( (char *)client->serverName() );

      // The node stored in the event is the node the event comes from. Hence, this isn't getnid(), it's publisherNode.
      event->node = publisherNode;

      // Ok, so this is confusing. The event object that we're creating here is put into memory.
      // This memory is read when the event is triggered. So, when this proxy is triggered
      event->forward = False;

	  if ( ( localProxyPid = qnx_proxy_attach(0, event, sizeof(TaskServer::Event), -1) ) == -1 )
	  {
		  Syslog::write("EventService::subscribe() - couldn't attach local proxy - errno %d. Not subscribing.", errno);
		  delete event;
		  return;
	  }
	  delete event;

      if ( ( remoteProxyPid = qnx_proxy_rem_attach(publisherNode, localProxyPid) ) == -1 )
      {
          Syslog::write("EventService::subscribe() - couldn't attach remote proxy - errno %d. Not subscribing.\n", errno);
          qnx_proxy_detach(localProxyPid);
	      return;
	  }
	  dprintf("EventService::subscribe() - local proxy = %d, remote = %d.", localProxyPid, remoteProxyPid);
  }

  dprintf("EventService::subscribe() - about to call TaskInterface::subscribe(%d, %d).", eventCode, remoteProxyPid);
  client->subscribe(eventCode, remoteProxyPid);

  EventTableEntry *entry = new EventTableEntry(client, eventCode, callback, localProxyPid, remoteProxyPid);
  _eventTable.add(&entry);
}


void EventService::processEvent()
{
  Boolean debug = False;
  TaskServer::Event event;

  // Wait for event notification
  pid_t senderPid = Receive(AnyPid, (void *)&event, sizeof(event));

  if (senderPid == -1) {

    if (errno == EINTR) {
      // Interrupted by signal; return
      dprintf("EventService::processEvent() - %s", strerror(errno));
      return;
    }
    else {
      dprintf("EventService::processEvent() - %s\n", strerror(errno));
      return;
    }

  }

  // Determine callback which corresponds to this notification
  Boolean found = False;
  for (int i = 0; i < _eventTable.size(); i++) {
    EventTableEntry *entry;
    _eventTable.get(i, &entry);

    if (event.eventCode == entry->_eventCode &&
	event.serverPid == entry->_client->serverPid()) {

      // Detach & invoke callback
	  if ( event.node == getnid() )
      {
	    dprintf("EventService::processEvent() - Detaching proxy %d.\n", senderPid);
        if (qnx_proxy_detach(senderPid) == -1) {
          dprintf("EventService::processEvent() - %s\n", strerror(errno));
        }
      }

      dprintf("EventService::processEvent() - found callback\n");

      callMemberFunction(_task, entry->_callback)(entry->_client, event.eventCode);

      dprintf("EventService::processEvent() - made callback\n");
      found = True;
    }
  }

  if (!found) {
    dprintf("EventService::processEvent() - unknown event, code=%d, pid=%d\n",
	   event.eventCode, event.serverPid);
  }
}



