/*
 * main.c,v 1.4 1994/01/31 02:04:11 franktor Exp
 */

#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sr-general.h>
#include <high/sr_structcodec.h>
#include <high/client-structdesc.h>
#include <high/sr_structproto.h>
#include "client.h"
#include <malloc.h>
#include <errno.h>
extern int errno;

#include <sys/file.h>		/* fcntl hack */
#include <fcntl.h>		/* fcntl hack */

int
main ( int argc, char **argv )
{
  int max_file_descriptors;
  struct rlimit lim;
  int errors = 0;
  connectionParameters conn;
  int listen_fd;
  int i;
  Boolean ttydebug = False;
  char *logfile = "./hlvlclient.log";

  facLogLevel(facAll, llevTrace);
  facLogLevel (facStruct, llevDebug);

  for (i = 1; i < argc; i++)
  {
    if (!strncmp(argv[i], "-debug", strlen(argv[i])))
    {
      ttydebug = True;
      continue;
    }
    if (!strncmp(argv[i], "-logfile", strlen(argv[i])))
    {
      if (++i == argc)
      {
        fprintf(stderr, "You must specify a filename after the -logfile option.\n");
        exit(1);
      }
      logfile = argv[i];
      continue;
    }
    if (!strncmp(argv[i], "-verbose", strlen(argv[i])))
    {
      facLogLevel (facAll, llevTrace); /* Turn on maximum debug */
      continue;
    }
    if (!strncmp(argv[i], "-help", strlen(argv[i])))
      fprintf(stderr, "Unknown argument: %s\n", argv[i]);
    fprintf(stderr, "Usage: %s [-debug] [-verbose] [-logfile <filename>]\n", argv[0]);
    fprintf(stderr, "  -debug:   Writes debug-information to terminal.\n");
    fprintf(stderr, "  -verbose: Turns on more debug-information.\n");
    fprintf(stderr, "  -logfile: Specifies which file to output logging to.\n");
    fprintf(stderr, "            Default is \"%s\".\n", logfile);
    exit(1);
  }

  if (ttydebug == False)
    initLogger(logfile);

  /*
   * Find out number of available file descriptords.
   */
#ifdef RLIMIT_NOFILE
  if (getrlimit(RLIMIT_NOFILE,&lim) == -1) {
    perror("getrlimit");
    exit(1);
  }
  LOG(facHigh, llevTrace, "Max file descriptors changed from %d to %d.",lim.rlim_cur,lim.rlim_max);
  lim.rlim_cur = lim.rlim_max;
  if (setrlimit(RLIMIT_NOFILE,&lim) == -1)
    perror("setrlimit");

  max_file_descriptors = lim.rlim_cur;
#else
  max_file_descriptors = 64;
  LOG(facHigh, llevTrace, "Unable to change number of descriptors, guessing at 64.");
#endif

  /*
   * Initialize structcodec usage:
   */
  conn.method		= connect_tcpip_multi;
  conn.u.ip.address	= NULL;
  conn.u.ip.port	= (DEFAULT_HCLIENT_PORT);

  switch(CreateListener(&conn, &listen_fd, desc_HC_Packet))
  {
  default: break;

  case srPktStat_failed:
    LOG(facHigh, llevExceptions, "CreateListener failed, exiting.");
    exit (-1);

  case srPktStat_ok:
    LOG(facHigh, llevDebug, "CreateListener returned filedesc %d.", listen_fd);
    break;
  }

  /*
   * Initialize usage of SR-protocoll:
   */
  if (OSI_Initialise (llevAll, stderr) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to establish connection, exiting.");
    exit(1);
  }
  

  /*
   * Main loop
   */

  for (;;)
  {
    SROperation opr;
    fd_set	hc_read, hc_write, hc_exceptions;
    int		ref;	/* SR_PollNetwork sets this variable */
    Client *client;

    /*
     * Add loops to set flags in the bit-arrays (hc_read, etc) here.
     */
    FD_ZERO(&hc_read);
    FD_ZERO(&hc_write);
    FD_ZERO(&hc_exceptions);
    FD_SET(listen_fd, &hc_read);
    FD_SET(listen_fd, &hc_write);
    FD_SET(listen_fd, &hc_exceptions);
    for (client = first_client; client; client = client->next)
    {
      FD_SET(client->fd, &hc_read);
      FD_SET(client->fd, &hc_exceptions);
      if (client->selectOnWrite)
        FD_SET(client->fd, &hc_write);
    }

    ref = -1;

    opr = SR_PollNetwork(0, max_file_descriptors, &hc_read, &hc_write,
                         &hc_exceptions, &ref);
    LOG(facHigh, llevTrace, "Operation on ref %d.", ref);
    switch(opr)
    {
    case oprInitialiseResponse:
      handleInitialiseResponse (ref);
      break;
    case oprCloseResponse:
      handleCloseResponse (ref);
      break;
    case oprSearchResponse:
      handleSearchResponse (ref);
      break;
    case oprPresentResponse:
      handlePresentResponse (ref);
      break;
    case oprDeleteResultSetResponse:
      handleDeleteResultSetResponse (ref);
      break;
    case oprAbort:
      handleAbortResponse (ref);
      break;

    case oprNone:
    {
      int fd, packets = 0;
      /*
       * Check for activity on the file descriptor listening for new clients,
       * and call CreateListener2() to get new descriptors for new clients.
       */
      if (FD_ISSET(listen_fd, &hc_read) || FD_ISSET(listen_fd, &hc_write))
      {
        int new_fd;
        if (CreateListener2 (listen_fd, &new_fd, desc_HC_Packet) == True)
        {
          Client *new_client;
          LOG(facHigh, llevTrace, "Added new client with fd %d.", new_fd);
          add_client(new_fd);
          break;
        }
        else
        {
          LOG(facHigh, llevExceptions, "Failed to add new client.");
          exit(1);
          break;
        }
      } else {
        /*
         * Check for activity on the file descriptors belonging to the
         * different clients.
         */
        for (fd = 0; fd < max_file_descriptors; fd++)
          if (FD_ISSET(fd, &hc_read) || FD_ISSET(fd, &hc_write) ||
              FD_ISSET(fd, &hc_exceptions))
          {
            Client *client;
            ServerConnection *conn;
  
            client=fd2client(fd);
            if (client == (Client *) NULL)
              LOG(facHigh, llevExceptions, "Activity on socket not belonging to a client.");
            LOG(facHigh, llevTrace, "Incoming (high-level) packet on fd %d.", fd);
  
            switch (handle_socket(fd,
				  FD_ISSET(fd, &hc_read) ? True : False,
				  FD_ISSET(fd, &hc_write) ? True : False,
				  FD_ISSET(fd, &hc_exceptions) ? True : False,
				  &client->selectOnWrite))
            {
              case sockStatOK:
                LOG (facHigh, llevDebug, "handle_socket(): OK");
                if (++client->loopcount > MAX_LOOPS)
                {
                  LOG(facHigh, llevExceptions, "Too many loops, closing connection.");
                  abortClient(client);
                }
                break;
              case sockStatFailed:
              case sockStatClosed:
                client->loopcount = 0;
                LOG (facHigh, llevDebug, "handle_socket(): False");
                LOG (facHigh, llevDebug, "Closing socket %d and removing client.", client->fd);
                abortClient(client);
                break;
              case sockStatPacketReady:
              {
                HC_Packet *packet;

                client->loopcount = 0;
                packet = (HC_Packet *) malloc(sizeof(HC_Packet));
                switch(read_struct(fd, (void **) &packet))
                {
                case sockStatClosed:
                case sockStatFailed:
                case sockStatPacketReady:
                  LOG(facHigh, llevDebug, "Error in receiving struct.");
                  break;
                case sockStatOK:
                  LOG(facHigh, llevDebug, "Got packet type %d.", packet->type);
                  switch (packet->type)
                  {
                  case hcOpenRequest:     
                    handleOpenRequest(fd, packet->ref, packet->u.open_request);
		    break;
		  case hcCloseRequest:
		    handleCloseRequest(fd, packet->ref, packet->u.close_request);
		    break;
		  case hcSearchRequest:
		    handleSearchRequest(fd, packet->ref, packet->u.search_request);
		    break;
		  case hcPresentRequest:
		    handlePresentRequest(fd, packet->ref, packet->u.present_request);
		    break;
		  case hcDeleteResultSetRequest:
		    handleDeleteResultSetRequest(fd, packet->ref, packet->u.delete_result_set_request);
		    break;
		  default:
		    LOG(facHigh, llevExceptions, "Got unknown packet type from client.");
		    break;
                  }
                  free_packet(packet);
	        }
	      }
	    }
            break;
	  }
        if (fd == max_file_descriptors)
          LOG(facHigh, llevExceptions, "Did not find activity on any descriptors.");
      }
      break;
    }
    default:
      LOG(facHigh, llevExceptions, "Got unkown packet from server (%d).", opr);
      break;
    }
  }
}
