/* LarkImpl.c 
   Dan Swinehart October 14, 1982  1:25 PM
   L. Stewart October 27, 1982  11:15 PM
   L. Stewart November 9, 1982  9:58 AM, use ctxQ
   L. Stewart December 8, 1982  3:57 PM, silent DTMF events
   L. Stewart December 20, 1982  10:04 AM, more space for EventReport
   L. Stewart December 27, 1982  11:07 PM, ELoad, less space for E.R.
   L. Stewart January 1, 1983  4:11 PM, remove AllocZero
   L. Stewart January 17, 1983  3:47 PM, Lark.mesa revisions
 */

#include <Alloc.h>
#include <Queue.h>
#include <Ec.h>
#include <Env.h>
#include <Signal.h>
#include <rpc.h>
#include <rpcinternal.h>
#include <rpclupine.h>
#include <encrypt.h>
#include <LarkNet.h>

/* runtime */
extern CallSwat();
extern InitOS();
extern Move2();
extern MoveBlock();
extern Swab();
extern Zero();

/* RPC package */
extern AddToDispatcherSpecs();
extern AddType();
extern int CallFormatted();
extern int diagnoseBinding;
extern int ExportFailed;
extern ExportInterface();
extern int ImportFailed;
extern int ImportInterface();
extern struct VersionRange *matchAllVersions;
extern RPCInitialize();
extern RPCInitializeCtx();
extern int *StartDispatcherSpecs();
extern StartNProcess();
extern int StdDispatcher();
extern struct ShortSTRING *ToRope();

/* Alloc package */
extern int *Allocate();
extern Free();

/* Lark package */

/* in LarkEventsImpl.c */
extern InitEventReport();
extern EventReport();
extern EventProcess();
extern AudioStuff();
extern AudioProcess();
extern AudCmd();
extern StartNet();
extern StopNet();

/* context package */
extern Block();
extern CallContext();
extern Dismiss();

/* signaller package */
extern int CONT();

/* chario package */
extern wf();
extern wf1();
extern wf2();
extern wf3();
extern wf4();

/* pup package */
extern GetPupHost();
extern ELoad();

/* unknown */
extern int mySoc;

/* (Forward) Interface procedures to which Server, below, dispatches */
extern Reset();			/* in LarkAud.c */
extern GenerateTones();		/* in LarkAud.c */
extern Feep();
extern Commands();
extern Connect();
extern Disconnect();
extern PleaseRegister();
extern WhatIsStatus();
extern WhatAreConnections();
extern WhatAreTones();
/* End of interface procedures */

extern struct ZN *sysZone;
extern struct EncryptionKey nullKeyB;
extern struct Queue ctxQ;

struct ShortSTRING *spNl, *spDT;
static struct ShortSTRING *sp1, *sp2, *sp6;
static struct ShortSTRING *sp9, *spSt, *sp3St, *sp5Sq, *spSt3, *spX;
static struct ShortSTRING *spD1, *spRegister;
static struct ShortSTRING *spSq, *sp1Sq, *sp1X;
static int nilst[2];
struct Conversation *shhhh;
  /* Place holder for later encrypted conversations */

int *smartsHandle;
int DEBUG;

int handle;
static struct Seal sl;
static struct InterfaceName clientInterface;
static int args[10];  /* prob not needed */

static int inProg;

int pNil[2];
struct ShortSTRING *instance, *serverRname, *myRname;
struct Model *myModel;
static int lyib, encrpcb;

Main() {
  InitOS(); /* Sets up sysZone */
  inProg = 0;
  DEBUG = false;
  shhhh = unencrypted;
  Options();
  RPCInitialize(&ctxQ, sysZone);
  spNl = (struct ShortSTRING *) nilst;
  Zero(nilst, 2);
  sp1 = ToRope("$$$$W");
  sp2 = ToRope("$$$$WW");
  sp6 = ToRope("$$$$WWWWWW");
  sp9 = ToRope("$$$$WWWWWWWWW");
  spSt = ToRope("$$$$S");
  sp1Sq = ToRope("$$$$WT");
  spSq = ToRope("$$$$K");
  sp3St = ToRope("$$$$WWWS");
  sp5Sq = ToRope("$$$$WWWWWK");
  spSt3 = ToRope("$$$$SSS");
  spRegister = ToRope("$$$$DWDSS");
  spX = ToRope("$$$$X"); /* Connection spec */
  sp1X = ToRope("$$$$WX"); /* INTEGER, ConnectionSpec */
  spD1 = ToRope("$$$$D");
  spDT = ToRope("$$$$DT");
  InitEventReport();
  /* EventProcess is temporary until the interrupt procedures get fixed up */
  StartNProcess("EventProcess", sysZone, &ctxQ, &EventProcess, 200, 10);
  StartNProcess("EventReport", sysZone, &ctxQ, &EventReport, 350, 90);
  StartNProcess("AudioProcess", sysZone, &ctxQ, &AudioProcess, 350, 90);
  AudioStuff();
  for (;;) {
    CallContext(ctxQ.head);
    };
  };

static Options()
  {
  lyib = encrpcb = false;
  wf("type 1 to encrypt, 0 to not encrypt\r");
  encrpcb = (GetChar() == '1') ? true: false;
  wf("type 1 to use Einstein.Lark, 0 to use Morley.Lark\r");
  lyib = (GetChar() == '1') ? true: false;
  };

Initialize()
  {
  RPCInitializeCtx(true);
  ELoad();
  diagnoseBinding = true;
  AddType('Z', 0);	/* Get things started */
  AddType('X', dBlock(	/* Connection specification */
  	16, 		/* eltSize: word elements */
  	0, 		/* overhead: 14 words (all overhead!) */
  	14, 		/* length: Sequence length field, not a sequence */
  	false, 		/* atLen: length is 0 */
  	true)); 	/* eltWd: all Swabbed, have to reverse some of them */
  AddType('K', dBlock(	/* LarkModel specification */
  	16, 		/* one word elements */
  	1, 		/* sequence only */
  	0,
  	true, 		/* length is in record at word 0 */
  	false)); 	/* deviceStates: sequence of pairs of bytes */
  AddType('T', dBlock(  /* events:  int time; char device, event; */
    32,
    1,
    0,
    true,
    false));
  handle = 0;
  Zero(&clientInterface, lenInterfaceName);
  clientInterface.type = ToRope("$$$$LarkSmarts.Lark");
/* clientInterface.instance = 0; */
/* Use broadcast binding methods. */
  clientInterface.instance = ToRope((lyib) ? "$$$$Einstein.Lark" : "$$$$Morley.Lark");
  Move2(&clientInterface.version, matchAllVersions);
  { /* Implicit loop until ENABLE succeeds */
  if (ENABLE(ImportFailed, &CONT, &sl))
    CallSwat(ecPup1+100);
  while (!AgentInitialize()) CallSwat(ecLarkImpl+23);
  handle = ImportInterface(&clientInterface);
  DISABLE(&sl);
  wf("Identify[...] = ");
  args[0] = (int) Swab(GetPupHost());	/* netAddress */
  serverRname = CallFormatted(handle, iIdentify, sp1, spSt3, args);
  myRname = args[2];
  instance = args[3];
  wf3("[serverRname: %r, myRname: %r, instance: %r]\r", serverRname, myRname, instance);
  shhhh = StartConversation(myRname, &nullKeyB, serverRname, slCBCCheck);
  wf1("StartConversation[...] = %xH\r", shhhh);
  if (encrpcb) AttachConversation(handle, shhhh);
  Server(instance);
  myModel = (struct Model *) Allocate(sysZone, lenModel);
  myModel->genre = 1; /* Lark1 */
  myModel->hardwareVersion = 5; /* or something */
  myModel->softwareVersion = 6; /* or something */
  /* Now register in (once, for now) */
  args[0] = (int) pNil;	/* oldHandle: none yet */
  args[1] = (int) Swab(GetPupHost());	/* netAddress: */
  args[2] = (int) myModel;	/* model: */
  args[3] = (int) instance
  args[4] = (int) myRname; /* <<not sure this still belongs here.>> */
  wf("Register[...] = ");
  smartsHandle = CallFormatted(handle, iRegister, spRegister, spD1, args);
  wf2("%xH, %xH]\r", smartsHandle[0], smartsHandle[1]);
  }; /* endloop */
  };

static Server(instance)
  struct ShortSTRING *instance;
  {
  struct InterfaceName *interface;
  int *specs;
  interface = (struct InterfaceName *) Allocate(sysZone, lenInterfaceName);
  interface->type = ToRope("$$$$Lark.Lark");
  interface->instance = instance;
  Move2(&interface->version, matchAllVersions);
  specs = StartDispatcherSpecs(10);
  AddToDispatcherSpecs(specs, iReset, &Reset, spSt, spNl);
  AddToDispatcherSpecs(specs, iGenerateTones, &GenerateTones, sp9, sp1);
  AddToDispatcherSpecs(specs, iFeep, &Feep, sp5Sq, sp1);
  AddToDispatcherSpecs(specs, iCommands, &Commands, spSq, spNl);
  AddToDispatcherSpecs(specs, iConnect, &Connect, spX, spNl);
  AddToDispatcherSpecs(specs, iDisconnect, &Disconnect, sp2, spNl);
  AddToDispatcherSpecs(specs, iPleaseRegister, &PleaseRegister, spNl, spNl);
  AddToDispatcherSpecs(specs, iWhatIsStatus, &WhatIsStatus, sp1, sp1Sq);
  AddToDispatcherSpecs(specs, iWhatAreConnections, &WhatAreConnections, sp1, sp1X);
  AddToDispatcherSpecs(specs, iWhatAreTones, &WhatAreTones, spNl, sp1);
  ExportInterface(interface, &StdDispatcher, specs);
  };

/* Actual calls via RPC */

int Feep(on, off, waveTable, queueIt, notify, events)
  int on, off, waveTable, queueIt, notify;
  struct EvSeq *events;
  {
  int i, len, thisNotify;
  char d, e, c;
  thisNotify = 0;
  for (i = 0; i < events->length; i += 1) {
    if ((i + 1) == events->length) thisNotify = notify;
    Block();
    d = events->evs[i].device;
    e = events->evs[i].event;
    if ((e >= 128) && (e <= 137)) c = e - 128 + '0';
    else switch (e) {
      case 138: c = 'A'; break;
      case 139: c = 'B'; break;
      case 140: c = 'C'; break;
      case 141: c = 'D'; break;
      case 142: c = '*'; break;
      case 143: c = '#'; break;
      default: {
        PlayTone(0, 0, 40, ((e * 10) - 40) & 0x00ff, 1, queueIt, waveTable, thisNotify);
        queueIt = true;
        continue;
        };
      };
    LocPlayDTMF(c, on, off, queueIt, waveTable, thisNotify);
    queueIt = true;
    };
  Free(sysZone, events);
  return (1);  /* a Mesa TRUE */
  };

Commands(events)
  struct EvSeq *events;
  {
  AudCmd(events);
  Free(sysZone, events);
  };

Connect(specs)
  struct CSpecs *specs;
  {
  if (inProg) CallSwat(ecLarkImpl+6);
  inProg = 1;
  StartNet(specs);
  Free(sysZone, specs);
  inProg = 0;
  };