/*
 * main.c,v 1.1 1994/01/28 17:05:33 franktor Exp
 *
 * This file contains the basics needed for creating a glue between
 * the high-level API interface and any database server.
 *
 */

#ifndef VMS
#include <string.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sr-general.h>
#include <sr-api.h>
#include <high/eapi.h>
#include <high/sr_structcodec.h>
#include <high/server-structdesc.h>
#else
#include <string.h>
#include <time.h>
#include <types.h>
#include <errno.h>
#include <signal.h>

#include <sr-general.h>
#include <sr-api.h>
#include <eapi.h>
#include <sr_structcodec.h>
#include <server-structdesc.h>
#endif

/*
 * These functions have to be provided for this program to work.
 * An example can be found in the file "server.c"
 */

extern void init(int argc, char **argv, char **env);
#ifdef __STDC__
extern CloseResponse * handleClose ( CloseRequest *pkt );
extern DeleteResultSetResponse * handleDeleteResultSet ( DeleteResultSetRequest *pkt );
extern OpenResponse * handleOpen ( OpenRequest *pkt );
extern PresentResponse * handlePresent ( PresentRequest *pkt );
extern SearchResponse * handleSearch ( SearchRequest *pkt );
#else
extern CloseResponse * handleClose (/* CloseRequest *pkt */);
extern DeleteResultSetResponse * handleDeleteResultSet (/* DeleteResultSetRequest *pkt */);
extern OpenResponse * handleOpen (/* OpenRequest *pkt */);
extern PresentResponse * handlePresent (/* PresentRequest *pkt */);
extern SearchResponse * handleSearch (/* SearchRequest *pkt */);
#endif

/*
 * A few prototypes.  The functions are provided for in this file.
 */
#ifdef __STDC__
EapiPacket * handle_packet ( EapiPacket *pkt );
#else
EapiPacket * handle_packet (/* EapiPacket *pkt */);
#endif
int get_max_files();
void free_EapiPacket(EapiPacket *ep);


#define MAX_ERRORS 30 /* Increase if transmitting things like pictures */

int
main(int argc, char **argv, char **env)
{
  int fd;
  connectionParameters conn;
  int max_files;
  int errcount = 0;
  int continue_loop = 1;

  signal(SIGCHLD, SIG_IGN);

  init(argc, argv, env);
  max_files = get_max_files();

  /*
   * Set up connection parameters
   */
  conn.method = connect_tcpip; /* May change to pipe later */
  conn.u.ip.address = NULL;
  conn.u.ip.port = 2400;

  LOG(facHigh, llevExceptions, "YOHOO!");

  switch(CreateListener ( &conn, &fd, desc_EapiPacket ))
  {
  case srPktStat_failed:
    LOG(facHigh, llevExceptions, "CreateListener failed, exiting.");
    return 1;
  case srPktStat_ok:
    LOG(facHigh, llevDebug, "CreateListener returned OK.");
    break;
  }

  LOG(facHigh, llevTrace, "Done initialising.");
  while(continue_loop)
  {
    EapiPacket *pkt, *opkt;
    fd_set readfd, writefd, exceptfd;
    Boolean selectOnWrite = False;

    FD_ZERO(&readfd);
    FD_ZERO(&writefd);
    FD_ZERO(&exceptfd);

    FD_SET(fd, &readfd);
    FD_SET(fd, &exceptfd);
    if ( selectOnWrite)
      FD_SET(fd, &writefd);
    LOG(facHigh, llevTrace, "Selecting...");
    if (select(max_files, &readfd, &writefd, &exceptfd, (struct timeval *) NULL) < 0)
    {
      perror("select");
      continue;
    }
    switch ( handle_socket (fd,
                            FD_ISSET(fd, &readfd) ? True: False,
                            FD_ISSET(fd, &writefd) ? True: False,
                            FD_ISSET(fd, &exceptfd) ? True: False,
                            &selectOnWrite))
    {
      case sockStatOK:
        LOG(facHigh, llevTrace, "Got sockStatOK from handle_socket().");
        break;
      case sockStatFailed:
      case sockStatClosed:
        LOG(facHigh, llevExceptions, "handle_socket() failed.");
        break;
      case sockStatPacketReady:
        errcount = 0;
        LOG(facHigh, llevDebug, "Got packet from handle_socket().");
        if (read_struct(fd, (void **) &pkt) != sockStatOK)
          LOG(facHigh, llevExceptions, "Failed to read packet.");
        else
          {
            int ref = pkt->ref;
            if((opkt = handle_packet(pkt)))
            {
              opkt->ref = ref;
              send_struct(fd, (void *) opkt, &selectOnWrite);
              if (opkt->type == eapiCloseResponse ||
                  (opkt->type == eapiOpenResponse && opkt->u.open_response->openAccepted == False))
                continue_loop = 0;
            }
            free_EapiPacket(pkt);
            free_EapiPacket(opkt);
          }
      continue;
    }
    if (++errcount > MAX_ERRORS)
    {
      LOG(facHigh, llevExceptions, "Seems like endless loop, exiting.");
      exit(1);
    }
  }
  LOG(facHigh, llevTrace, "%s exiting.", argv[0]);
  return 0;
}

/*
 * Handle any incoming packet from the client side, and
 * return a response.
 */

EapiPacket *
handle_packet(EapiPacket *pkt)
{
  EapiPacket *opkt = (EapiPacket *) malloc(sizeof(EapiPacket));
  switch(pkt->type)
  {
  case eapiOpenRequest:
    LOG(facHigh, llevDebug, "Open request.");
    opkt->type = eapiOpenResponse;
    opkt->u.open_response = handleOpen(pkt->u.open_request);
    break;
  case eapiCloseRequest:
    LOG(facHigh, llevDebug, "CloseRequest.");
    opkt->type = eapiCloseResponse;
    opkt->u.close_response = handleClose(pkt->u.close_request);
    break;
  case eapiSearchRequest:
    LOG(facHigh, llevDebug, "SearchRequest");
    opkt->type = eapiSearchResponse;
    opkt->u.search_response = handleSearch(pkt->u.search_request);
    break;
  case eapiPresentRequest:
    LOG(facHigh, llevDebug, "PresentRequest");
    opkt->type = eapiPresentResponse;
    opkt->u.present_response = handlePresent(pkt->u.present_request);
    break;
  case eapiDeleteResultSetRequest:
    LOG(facHigh, llevDebug, "DeleteResultSetRequest.");
    opkt->type = eapiDeleteResultSetResponse;
    opkt->u.delete_result_set_response =
      handleDeleteResultSet(pkt->u.delete_result_set_request);
    break;
  case eapiInterruptRequest:
    LOG(facHigh, llevDebug, "Interrupt request (not supported).");
    opkt->type = eapiInterruptResponse;
    opkt->u.interrupt_response = NULL;
    /* Not supported */
    break;
  default:
    LOG(facHigh, llevDebug, "Unknown packet, can't handle.");
    abort(); /* To get core-file */
    break;
  }
  return opkt;
}

int
get_max_files()
{
#ifdef RLIMIT_NOFILE
  struct rlimit lim;
  if (getrlimit(RLIMIT_NOFILE, &lim) == -1) {
    perror("getrlimit");
    exit(1);
  }
  lim.rlim_cur = lim.rlim_max;
  if(setrlimit(RLIMIT_NOFILE, &lim) == -1)
    perror("setrlimit");
  if (getrlimit(RLIMIT_NOFILE, &lim) == -1) {
    perror("getrlimit");
    exit(1);
  }
  return lim.rlim_cur;
#else
  LOG(facHigh, llevDebug, "Don't know how to change number of filedescriptors.");
  return 64;
#endif
}

void free_EapiPacket(EapiPacket *ep)
{
  free_struct((char *) ep, desc_EapiPacket);
}
