/* Application program interface server

   This module checks for requests from an application program and
   processes them.  Requests come in through the apicoms module using
   an internal protocol described in api.h.
*/

#include "api.h"
#include "keys.h"
#include <string.h>
#include <memory.h>

#define True 1
#define False 0
#define HWORD(a, n) ((a[n] << 8) + a[(n) + 1])
#define storew(a, i, val) { a[i] = (val) >> 8; a[(i)+1] = (val) & 0xff; }
typedef char Boolean;


/* Global variables */
static char *replybuff;		/* Buffer for building replies */
static int lreply;		/* Length of replybuff */
static Boolean waiting;		/* True if waiting for keyboard unlock */
static long stoptime;		/* When to stop waiting for unlock */


/* External and forward routine declartions */
extern int *coms_init();
extern Boolean s3270_key();
static void gotdata();


/* Prepare to receive connections.  Return address of file descriptor
   to use for select() calls.  */

int *api_init(session)
     char *session;
{
  if (strlen(session) > 1)
    error("Session ID must not be more than one character long");
  lreply = 8192;
  if (!(replybuff = (char *) malloc(lreply)))
    error("Not enough memory");
  waiting = False;
  return coms_init(session[0], gotdata);
}


/* Check for pending events.  Return 1 if something was found.  */
int api_check()
{
  /* If we've waited too long for keyboard to unlock then send timeout code */
  if (waiting && stoptime && time((long *) 0) > stoptime)
    {
      replybuff[0] = 0;
      replybuff[1] = 1;
      coms_put(replybuff, 2);
      waiting = False;
      return 1;
    }
  return coms_check();
}


/* Shut down operation */
api_close()
{
  coms_close();
  return 0;
}


/* This routine is called when the keyboard is unlocked and queue is empty */
api_unlock()
{
  if (waiting)
    {
      replybuff[0] = 0;
      replybuff[1] = 0;
      coms_put(replybuff, 2);
      waiting = False;
    }
  return 0;
}


/* Callback routine for data received from client */
static void gotdata(buff, length)
     char *buff;
     int length;
{
  int func, replylen;
  Boolean extended, locked;
  int cols, rows, col, row, curspos, startpos, len;
  char keybuff[5];
  unsigned char dir, ftype, attrib;
  
  if (length-- <= 0) return;
  func = *buff++;

  replybuff[0] = 0;
  replylen = 1;

  switch (func)
    {
    case API_VERSION:
      replylen = 2;
      replybuff[1] = API_VERSION_NUM;
      break;

    case API_DISC:
      coms_disc();
      break;

    case API_STATUS:
      s3270_getvals(&cols, &rows, &curspos, &extended, &locked);
      storew(replybuff, 1, rows);
      storew(replybuff, 3, cols);
      storew(replybuff, 5, curspos);
      replybuff[7] = extended;
      replybuff[8] = locked;
      replylen = 9;
      break;

    case API_SENDKEY:
      while (length > 1)
	{
	  if (*buff <= k_lastcode &&
	      !s3270_key(buff, *buff == k_string ? 2 : 1))
	    {
	      replybuff[0] = 2;
	      break;
	    }
	  buff += 2;
	  length -= 2;
	}
      break;

    case API_WAIT:
      replylen = 2;
      replybuff[1] = 1;
      s3270_getvals(&cols, &rows, &curspos, &extended, &locked);
      if (!locked)
	{
	  replybuff[1] = 0;
	  break;
	}
      if (length < 1) break;
      waiting = True;
      stoptime = (*buff == 0 ? time((long *) 0) + 59 : 0);
      return;

    case API_COPYPS:
      replylen = 2;
      replybuff[0] = 2;
      replybuff[1] = 0;
      if (length < 4) break;
      startpos = HWORD(buff, 0);
      len = HWORD(buff, 2);

      /* Verify that area requested is within the screen */
      s3270_getvals(&cols, &rows, &curspos, &extended, &locked);
      if (startpos >= rows * cols || len > rows * cols) break;

      /* Make sure we have enough room in the reply buffer */
      replylen = len + 2;
      if (replylen > lreply)
	{
	  lreply = replylen;
	  if (!(replybuff = (char *) realloc(replybuff, lreply)))
	    error("Not enough memory");
	}

      /* Copy the requested screen area */
      replybuff[0] = 0;
      replybuff[1] = locked;
      x_getscr(startpos, len, &replybuff[2]);
      break;

    case API_SETCURS:
      s3270_getvals(&cols, &rows, &curspos, &extended, &locked);
      replybuff[0] = 2;
      curspos = HWORD(buff, 0);
      col = curspos % cols;
      row = curspos / cols;
      if (col < 0 || col >= cols || row < 0 || row >= rows) break;

      keybuff[0] = k_cursor_move;
      keybuff[1] = col >> 8;
      keybuff[2] = col & 255;
      keybuff[3] = row >> 8;
      keybuff[4] = row & 255;
      if (s3270_key(keybuff, 5)) replybuff[0] = 0;
      break;

    case API_COPYSTR:
      replylen = 2;
      replybuff[0] = 2;
      replybuff[1] = 3;
      if (length < 2) break;
      startpos = HWORD(buff, 0);
      buff += 2;
      length -= 2;

      s3270_getvals(&cols, &rows, &curspos, &extended, &locked);
      col = startpos % cols;
      row = startpos / cols;
      if (col < 0 || col >= cols || row < 0 || row >= rows) break;

      replybuff[1] = 1;
      if (locked) break;
      
      /* Move cursor to starting position */
      keybuff[0] = k_cursor_move;
      keybuff[1] = col >> 8;
      keybuff[2] = col & 255;
      keybuff[3] = row >> 8;
      keybuff[4] = row & 255;
      if (!s3270_key(keybuff, 5)) break;

      /* Send string as keyboard input */
      replybuff[1] = 2;
      while (length > 0)
	{
	  buff[-1] = k_string;
	  len = length > 250 ? 250 : length;
	  if (!s3270_key(&buff[-1], len + 1)) break;
	  buff += len;
	  length -= len;
	}
      if (length > 0) break;
      replybuff[0] = 0;
      replybuff[1] = 0;
      break;

    case API_GETFIELD:
      replylen = 6;
      replybuff[0] = 2;
      if (length < 4) break;
      startpos = HWORD(buff, 0);
      dir = buff[2];
      ftype = buff[3];
      buff += 4;
      length -= 4;
      if (dir > 2 || ftype > 2) break;
      if (s3270_getfield(startpos, dir, ftype, &startpos, &len, &attrib))
	break;
      storew(replybuff, 1, startpos);
      storew(replybuff, 3, len);
      replybuff[5] = attrib;
      replybuff[0] = 0;
      break;
      
    default:
      replybuff[0] = 1;
      break;
    }
  coms_put(replybuff, replylen);
}
