
#include <devices/timer.h>

#include <exec/libraries.h>
#include <exec/memory.h>
#include <libraries/commodities.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>
#include <clib/commodities_protos.h>
#include <devices/inputevent.h>
#include <clib/AMarquee_protos.h>
#include <pragmas/AMarquee_pragmas.h>

int main(int, char **);
void ProcessMsg(CxObj * broker, struct MsgPort * broker_mp, CxObj * cocustom);
void CxFunction(CxMsg *, CxObj *);

struct Library * AMarqueeBase = NULL;
struct QSession * session     = NULL;
struct Library * CxBase, *IconBase, * TimerBase;
int port = 20000;
BOOL BConnectOkay = FALSE;
struct timerequest * TimerIO;
struct MsgPort * event_mp;

char * connectTo = "";  /* IP name to connect to, or "" if we should go passive instead */
char * hosts = "";

struct NewBroker newbroker =
{ 
  NB_VERSION,
  "Divert",
  "Divert",
  "show divert",
  NBU_UNIQUE | NBU_NOTIFY,
  0, 0, 0, 0
};

/* Makes pcString lower case */
char * ToLower(char * pcString)
{
	char * pcTemp = pcString;
	
	while(*pcTemp) 
	{
		if ((*pcTemp >= 'A')&&(*pcTemp <= 'Z'))
			*pcTemp += 'a'-'A';
		pcTemp++;
	}
	return(pcString);
}

void edebug(int i)
{
  int a = *((int *)i);
}

/* return TRUE iff the string represents the boolean value true */
BOOL ParseBool(char * string)
{
  char temp[100];
  
  strncpy(temp,string,sizeof(temp));
  temp[99]=0;
  
  ToLower(temp);
  
  return(
    (*temp=='\0')              ||  /* then it's just KEYWORD, so yes, right? */
    (strcmp("true",temp) == 0) ||
    (strcmp("yes",temp)  == 0) ||
    (strcmp("1",temp)    == 0));   /* Anyone who specifies 1 for true is a weenie! */
}


int main(int argc, char ** argv)
{
  UBYTE ** ttypes;
  CxMsg * msg;
  
  if (NULL == (TimerIO = (struct timerequest *)AllocMem(sizeof(struct timerequest), MEMF_PUBLIC | MEMF_CLEAR)))
  {
    printf("Couldn't allocate timerequest\n");
    exit(10);
  }
  if (OpenDevice(TIMERNAME, UNIT_MICROHZ, TimerIO, 0))
  {
    printf("Couldn't open timer.device\n");
    FreeMem(TimerIO, sizeof(struct timerequest));
    exit(10);
  }
  TimerBase = (struct Library *) TimerIO->tr_node.io_Device;
  
  if (AMarqueeBase = OpenLibrary("amarquee.library",42L))
  {
    if (CxBase = OpenLibrary("commodities.library", 37L))
    {
      if (IconBase = OpenLibrary("icon.library", 36L))
      {
        struct MsgPort * broker_mp;
        CxObj * broker, * cocustom;

        if (event_mp = CreateMsgPort())
        {
          if (broker_mp = CreateMsgPort())
          { 
            newbroker.nb_Port = broker_mp;
        
            ttypes = ArgArrayInit(argc, argv);
            newbroker.nb_Pri = (BYTE) ArgInt(ttypes, "CX_PRIORITY", 0);
        
            connectTo = ArgString(ttypes, "CONNECT", "");
            hosts     = ArgString(ttypes, "ACCEPT",  "");
            port      = ArgInt(ttypes,    "PORT", port);
            
            if (broker = CxBroker(&newbroker, NULL))
            {
              if (cocustom = CxCustom(CxFunction, 0L))
              {
                AttachCxObj(broker, cocustom);
                ActivateCxObj(broker, 1L);
                ProcessMsg(broker, broker_mp, cocustom);
              }
              DeleteCxObjAll(broker);          
              while(msg = (CxMsg *) GetMsg(broker_mp)) ReplyMsg((struct Message *)msg);  
            }
            DeletePort(broker_mp);
          }
        
          while(msg = (CxMsg *) GetMsg(event_mp)) FreeMem(msg, ((struct Message *)msg)->mn_Length);
          DeletePort(event_mp);
        }
      
        ArgArrayDone();
        CloseLibrary(IconBase);
      }
      CloseLibrary(CxBase);
    }
    CloseLibrary(AMarqueeBase);
  }
  
  /* free timer.device */
  CloseDevice((struct IORequest *)TimerIO);
  FreeMem(TimerIO, sizeof(struct timerequest));
  return(0);
}

void Disconnect()
{
  if (session) QFreeSession(session);
  session = NULL;
  BConnectOkay = FALSE;
}

void Reconnect()
{
  LONG pPort = port;
  
  Disconnect();
  
  if (*connectTo) printf("Connecting to [%s]\n",connectTo);
             else printf("Accepting connections from [%s]\n",hosts);
             
  if (*connectTo) session = QNewSessionAsync(connectTo, pPort, "ARemote");
             else session = QNewHostSession(hosts, &pPort, "ARemote");
}

void ProcessMsg(CxObj * broker, struct MsgPort * broker_mp, CxObj * cocustom)
{
  struct Message * msg;
  CxMsg * cmsg;
  ULONG sigrcvd, msgid;
  BOOL keepGoing = TRUE;

  Reconnect();  /* attempt an initial connection */
     
  while(keepGoing)
  {
    sigrcvd = Wait(SIGBREAKF_CTRL_C | (1L<<broker_mp->mp_SigBit) | (1L<<event_mp->mp_SigBit) | (session ? (1L<<session->qMsgPort->mp_SigBit) : 0L));
    
    while(cmsg = (CxMsg *)GetMsg(broker_mp))
    {
      msgid = CxMsgID(cmsg);
      ReplyMsg((struct Message *)cmsg);
      
      switch(msgid)
      {
        case CXCMD_DISABLE: printf("disabled.\n"); ActivateCxObj(broker, 0L); break;
        case CXCMD_ENABLE:  printf("enabled.\n");  ActivateCxObj(broker, 1L); break;
        case CXCMD_KILL:    printf("kill.\n");     keepGoing = FALSE;         break;
        case CXCMD_UNIQUE:  printf("uniqued.\n");  keepGoing = FALSE;         break;
      }
    }

    /* handle incoming InputEvent messages from our custom CxObject */
    {
      BOOL BSent = FALSE;

      while(msg = (struct Message *)GetMsg(event_mp))
      {
        if ((session)&&(BConnectOkay))
        {
          struct InputEvent * ie = (struct InputEvent *) (((UBYTE *)msg)+sizeof(struct Message));
      
          /* Send a copy of the Input Event to our buddy */
          (void)QStreamOp(session, "e", ie, sizeof(struct InputEvent));
          BSent = TRUE;
        }
        FreeMem(msg, msg->mn_Length);
      }
      if (BSent) QGo(session, 0L);
    }

    if (session)
    {
      struct QMessage * qmsg;
      
      while(qmsg = (struct QMessage *)GetMsg(session->qMsgPort))
      { 
        if (qmsg->qm_Status == QERROR_NO_ERROR)
        {
          if (qmsg->qm_ID == 0) 
          {
            printf("Connection to %s established.\n",qmsg->qm_Path);
            BConnectOkay = TRUE;
          }
          else
          {
            if (qmsg->qm_DataLen == sizeof(struct InputEvent))
            { 
              struct InputEvent * ie = (struct InputEvent *)qmsg->qm_Data;
/*              GetSysTime(&(ie->ie_TimeStamp));  */
              AddIEvents(ie);
            }
            else printf("Error, bad data size (%i != %i)\n",qmsg->qm_DataLen, sizeof(struct InputEvent));
          }
        }
        else
        {
          printf("Error %i received, disconnecting\n", qmsg->qm_Status);
          FreeQMessage(session, qmsg);  /* Gotta free this NOW!  Before the session is gone */
          Disconnect();
          break;  /* Can't evaluate while() condition again!  session is dangling! */
        }
        FreeQMessage(session, qmsg);
      }
    }
    
    if (sigrcvd & SIGBREAKF_CTRL_C) {printf("CTRL-C detected.\n"); keepGoing = FALSE;}
  }
  
  if (session) 
  {
    QFreeSession(session);
    session = NULL;
  }
}

__geta4 void CxFunction(register CxMsg * cxm, CxObj * co)
{
  struct InputEvent * ie;
  
  ie = (struct InputEvent *)CxMsgData(cxm);

  if ((ie->ie_Class == IECLASS_RAWKEY)     ||
      (ie->ie_Class == IECLASS_RAWMOUSE)   ||
      (ie->ie_Class == IECLASS_POINTERPOS)) 
  {
     /* Send a message containing a copy of this InputEvent to our program for transmission */
     int bufLen = sizeof(struct Message)+sizeof(struct InputEvent);
     struct Message * msg = (struct Message *)AllocMem(bufLen, MEMF_PUBLIC | MEMF_CLEAR);
       
     if (msg)
     {
       msg->mn_Node.ln_Type = NT_MESSAGE;
       msg->mn_Length       = bufLen;
       msg->mn_ReplyPort    = NULL;   /* No reply needed; we'll just free it */
       memcpy(((UBYTE *)msg)+sizeof(struct Message), ie, sizeof(struct InputEvent));
       PutMsg(event_mp, msg);
     } 
  }
}