/* Communication routines for application program interface (client side) */

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include "apicom.h"
#define BUFSIZE 8192
#define MAXNDX 26


/* Global variables */
static int sock[MAXNDX];	/* Socket descriptors */
static char *inbuff[MAXNDX];	/* Input buffers */
static char *inp[MAXNDX];	/* Input buffer pointers */
static int incount[MAXNDX];	/* Number of characters in input buffer */

static int sendit(), getch(), getdata();


/* Initialize */
xant_comc_init()
{
  int i;
  for (i = 0; i < MAXNDX; i++) sock[i] = 0;
  return 0;
}


/* Start a connection to the server

      Returns:   0 if successful
      		-1 if invalid session letter
		-2 if not enough memory
      		-3 if socket creation failed
      		-4 if socket connect failed		*/

int xant_comc_connect(sockndx)
     int sockndx;
{
  int s;
  struct sockaddr_un s_un;

  /* Return if already connected */
  if (sock[sockndx]) return 0;
  
  /* Create a socket */
  if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -3;

  /* Make a connection to the server */
  s_un.sun_family = AF_UNIX;
  sprintf(s_un.sun_path, "%s%c", SOCKET_NAME, sockndx + 'a');
  if (connect(s, &s_un, sizeof s_un) < 0)
    {
      (void) close(s);
      return -4;
    }

  /* Allocate an input buffer */
  if (!(inbuff[sockndx] = (char *) malloc(BUFSIZE)))
    {
      (void) close(s);
      return -2;
    }
  inp[sockndx] = inbuff[sockndx];
  incount[sockndx] = 0;

  sock[sockndx] = s;
  return 0;
}


/* Send data and read response

      Returns:   0 if successful
      		-1 if socket isn't connected
      		-2 if protocol error occurs
		-3 if output buffer is too small
		-4 if send fails
		-5 if receive fails			*/

int xant_comc_put(sockndx, inbuffer, inlength, outbuffer, outlength)
     int sockndx;
     char *inbuffer, *outbuffer;
     int inlength, *outlength;
{
  int j, len;
  char ch, *outptr;
  static char hdr[] = { API_SYNCH, 0, 0 };
  
  if (!sock[sockndx]) return -1;

  len = *outlength;
  *outlength = 0;
  hdr[2] = inlength;
  if (sendit(sockndx, hdr, sizeof hdr) < 0) return -4;
  if (sendit(sockndx, inbuffer, inlength) < 0) return -4;

  if (getch(sockndx, &ch) < 0) return -5;
  if (ch != API_SYNCH) return -2;
  if (getch(sockndx, &ch) < 0) return -5;
  *outlength = ch << 8;
  if (getch(sockndx, &ch) < 0) return -5;
  *outlength += ch;
  if (*outlength > len) return -3;

  for (outptr = outbuffer, len = *outlength; len > 0; outptr += j, len -= j)
    {
      if (!incount[sockndx])
	{
	  int i = getdata(sockndx);
	  if (i < 0) return -5;
	}
      j = incount[sockndx];
      if (j > len) j = len;
      memcpy(outptr, inp[sockndx], j);
      inp[sockndx] += j;
      incount[sockndx] -= j;
    }
  return 0;
}


/* Disconnect the current session */
int xant_comc_disc(sockndx)
     int sockndx;
{
  if (!sock[sockndx]) return -1;
  (void) close(sock[sockndx]);
  free(inbuff[sockndx]);
  sock[sockndx] = 0;
  return 0;
}




/*** Private routines ***/


/* Send data */
static int sendit(sockndx, buff, length)
     int sockndx;
     register char *buff;
     register int length;
{
  while (length > 0)
    {
      int n = send(sock[sockndx], buff, length, 0);
      if (n <= 0) return -1;
      buff += n;
      length -= n;
    }
  return 0;
}


/* Get next character from input buffer.  Fetch more data if necessary.  */
static int getch(sockndx, ch_return)
     int sockndx;
     char *ch_return;
{
  if (!incount[sockndx])
    {
      int i = getdata(sockndx);
      if (i) return i;
    }
  incount[sockndx]--;
  *ch_return = *inp[sockndx]++;
  return 0;
}


/* Fetch more data into input buffer.  Return -1 if failure.  */
static int getdata(sockndx)
     int sockndx;
{
  int len;
  len = recv(sock[sockndx], (char *) inbuff[sockndx], BUFSIZE, 0);
  /*...Need a timeout */
  if (len <= 0) return -1;
  inp[sockndx] = inbuff[sockndx];
  incount[sockndx] = len;
  return 0;
}
