/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : SharedObjectServer.cc                                         */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/name.h>
#include <sys/kernel.h>
#include <sys/psinfo.h>
#include "SharedObjectServer.h"
#include "Syslog.h"

SharedObjectServer::SharedObjectServer(const char *name)
  : SharedObject(name)
{
  pid_t pid;

  // Attach server name to its pid so clients can find us
  if ((_processNameId = qnx_name_attach(0, name)) == -1) {
    throw ServerExists(name);
  }

  _clientPid = 0;
  _requestPtr = 0;

  char errorBuf[256];

  // Identify this as a server to QNX
  if (qnx_pflags(~0, _PPF_SERVER | _PPF_PRIORITY_REC | _PPF_PRIORITY_FLOAT,
		 0, 0) == -1) {
    sprintf(errorBuf, 
	    "SharedObjectServer::SharedObjectServer() - qnx_pflags(): %s",
	    strerror(errno));

    throw Exception(errorBuf);
  }

  //ignore SIGHUP's so we don't die if the shell goes away
  signal(SIGHUP, SIG_IGN);
}


SharedObjectServer::~SharedObjectServer()
{
  Boolean debug = False;
  
  dprintf("SharedObjectServer::~SharedObjectServer()");

  if (_processNameId != -1) {
    qnx_name_detach(0, _processNameId);
    dprintf("SharedObjectServer::~SharedObjectServer() - detached name");
  }

  dprintf("SharedObjectServer::~SharedObjectServer() - free()...");
  free((void *)_requestPtr);
  dprintf("SharedObjectServer::~SharedObjectServer() - done");
}


void SharedObjectServer::allocateBuffers()
{
  _maxReqBytes = maxRequestBytes();

  // Allocate request buffer
  if ((_requestPtr = (Request *)malloc(_maxReqBytes)) == 0) {

    fprintf(stderr, 
	    "SharedObjectServer::allocateBufffers() - "
	    "couldn't malloc %d bytes!\n",
	    _maxReqBytes);
    
    exit(1);
  }
}


void SharedObjectServer::run()
{
  allocateBuffers();

  Boolean debug = False;

  int nReplyBytes;
  Reply *replyPtr;

  while (True) {

    // Wait for client request
    _clientPid = Receive(AnyPid, (void *)_requestPtr, _maxReqBytes);

    dprintf("SharedObjectServer::run() - received message, code %d\n",
	    _requestPtr->code);

    // Call appropriate method
    if (processRequest(_requestPtr, _maxReqBytes, &replyPtr, &nReplyBytes)) {
      // Reply to client
      dprintf("SharedObjectServer::run() - Reply to client %d with %d bytes\n",
	      _clientPid, nReplyBytes);

      Reply(_clientPid, (void *)replyPtr, nReplyBytes);
      dprintf("SharedObjectServer::run() - done with Reply()\n");
    }
  }
}


void SharedObjectServer::addCallback(SharedObject::RequestCode code, 
				     CallbackPtr callback)
{
  CallbackEntry *entry = new CallbackEntry(code, callback);
  _callbacks.add(&entry);
}


Boolean SharedObjectServer::processRequest(Request *request, int nRequestBytes,
					   Reply **reply, int *nReplyBytes)
{
  CallbackEntry *entry;

  // Search for method that matches request code
  for (int i = 0; i < _callbacks.size(); i++) {
    _callbacks.get(i, &entry);

    if (entry->_code == request->code) {
      // Found callback for this Request code
      return callMemberFunction(this, entry->_callback)(request, nRequestBytes,
							reply, nReplyBytes);
    }
  }

  // Couldn't find matching code (throw an exception here???)
  fprintf(stderr, 
	  "SharedObjectServer::processRequest() - couldn't find callback\n");

  return False;
}


size_t SharedObjectServer::maxRequestBytes() 
{
  return 512;
}


pid_t SharedObjectServer::clientPid()
{
  return _clientPid;
}



const char *SharedObjectServer::clientName()
{
  qnx_psinfo(PROC_PID, _clientPid, &_psInfo, 0, 0);

  char *ptr;
  // Look only at last name in program path
  if ((ptr = strrchr(_psInfo.un.proc.name, '/')) == 0)
    ptr = _psInfo.un.proc.name;
  else
    // Skip past '/'
    ptr++;

  return ptr;
}

