
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include <dos/dos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/gadtools.h>

#include <clib/intuition_protos.h>
#include <clib/AMarquee_protos.h>
#include <pragmas/AMarquee_pragmas.h>

#define UNLESS(x) if(!(x))

struct Library * GraphicsBase  = NULL;
struct Library * IntuitionBase = NULL;
struct Library * AMarqueeBase  = NULL;
struct QSession * session      = NULL;
struct Window * win            = NULL;

const int CHAR_WIDTH   = 10;
const int CHAR_HEIGHT  = 10;
const int BOARD_WIDTH  = 40;
const int BOARD_HEIGHT = 24;
const int TOP_OFFSET   = 10;
const int LEFT_OFFSET  = 5;

struct SillyGameInfo
{
  int oldX, oldY;  /* previous position to erase */
  int newX, newY;  /* new position to draw */
  char oldC, newC; /* char to erase, draw */
};

void CleanExit(void)
{
  if (win)           CloseWindow(win);
  if (session)       QFreeSession(session);       /* This MUST be done before we close the library! */
  if (AMarqueeBase)  CloseLibrary(AMarqueeBase);
  if (IntuitionBase) CloseLibrary(IntuitionBase);
  if (GraphicsBase)  CloseLibrary(GraphicsBase);
  printf("Program complete.\n");
}

void DoSillyUpdate(struct SillyGameInfo * data)
{
  int topOffset = win->WScreen->WBorTop + win->WScreen->RastPort.TxHeight + TOP_OFFSET;
  int leftOffset = LEFT_OFFSET;
  
  /* erase old position */
  if ((data->oldX >= 0)&&(data->oldY >= 0)&&
      (data->oldX < BOARD_WIDTH)&&(data->oldY < BOARD_HEIGHT))
  {
    SetAPen(win->RPort, 2);
    Move(win->RPort, (data->oldX*CHAR_WIDTH)+leftOffset, (data->oldY*CHAR_HEIGHT)+topOffset);
    Text(win->RPort, &data->oldC, 1);
  }
  
  /* Draw new position */
  if ((data->newX >= 0)&&(data->newY >= 0)&&
      (data->newX < BOARD_WIDTH)&&(data->newY < BOARD_HEIGHT))
  {
    SetAPen(win->RPort, 1);
    Move(win->RPort, (data->newX*CHAR_WIDTH)+leftOffset, (data->newY*CHAR_HEIGHT)+topOffset);
    Text(win->RPort, &data->newC, 1);
  }
}


/* Main program */
int main(int argc, char ** argv)
{
  const int port = 2957;
  struct SillyGameInfo sgi;
  BOOL BDie = FALSE;
  char buf[200],myName[200];
  
  atexit(CleanExit);
    
  UNLESS(GraphicsBase = OpenLibrary("graphics.library", 37L))
  {
    printf("Couldn't open graphics.library v37!\n");
    exit(RETURN_ERROR);  
  }
  UNLESS(IntuitionBase = OpenLibrary("intuition.library", 37L))
  {
    printf("Couldn't open intuition.library v37!\n");
    exit(RETURN_ERROR);
  }
  UNLESS(AMarqueeBase = OpenLibrary("amarquee.library",37L))
  {
    printf("Couldn't open amarquee.library v37!\n");
    exit(RETURN_ERROR);
  }
  if (argc < 2) 
  {
    printf("Usage:  SillyGame hostname\n");
    exit(RETURN_WARN);
  }
  printf("Connecting to %s:%i...\n",argv[1], port);
  sprintf(myName,"SillyGame_%i",time(NULL));  /* For multiple sessions on one host! */
  UNLESS(session = QNewSession(argv[1], port, myName))
  {
    printf("Couldn't connect to server %s:%i\n",argv[1], port);
    exit(RETURN_WARN);
  }
  
  printf("SillyGame: connected to server %s:%i\n", argv[1], port);
  
  UNLESS(win = OpenWindowTags(NULL,
	WA_Left,	    50,
	WA_Top,	        50,
	WA_Width,	    BOARD_WIDTH*CHAR_WIDTH+(LEFT_OFFSET*2),
	WA_Height,	    BOARD_HEIGHT*CHAR_HEIGHT+(TOP_OFFSET*2),
	WA_Title,	    "AMarquee Silly Game", 
	WA_CloseGadget, TRUE,
	WA_DepthGadget, TRUE,
	WA_Activate,	TRUE,
	WA_DragBar,	    TRUE,
	WA_Flags,       WFLG_NEWLOOKMENUS,
	WA_IDCMP,       IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY,
    TAG_DONE))
  {
    printf("Couldn't open window!\n");
    exit(RETURN_WARN);
  }

  /* Initialize our guy's state */
  srand(time(NULL));
  sgi.oldX = sgi.newX = rand() % BOARD_WIDTH;
  sgi.oldY = sgi.newY = rand() % BOARD_HEIGHT;
  sgi.oldC = sgi.newC = 'X';
  
  /* Setup--put in our interests!  */
  sprintf(buf, "/#?/~(%s)/data", myName);
  UNLESS((QStreamOp(session, "data", &sgi, sizeof(struct SillyGameInfo)))  &&
         (QSubscribeOp(session, buf, sizeof(struct SillyGameInfo)))        &&
         (QGetOp(session,       buf, sizeof(struct SillyGameInfo)))        &&
         (QGo(session,0L)))
  {
    printf("-->error setting up!\n");
    exit(RETURN_WARN);
  }
  
  DoSillyUpdate(&sgi);

  while(BDie == FALSE)
  {
    struct QMessage * qMsg;
    struct IntuiMessage * message;
    
    ULONG signals = (1L << session->qMsgPort->mp_SigBit) | (SIGBREAKF_CTRL_C) | (1L << win->UserPort->mp_SigBit);

    /* Wait for next message from the server */
    signals = Wait(signals);
    
    if (signals & (1L << session->qMsgPort->mp_SigBit))
    {      
      while(qMsg = (struct QMessage *) GetMsg(session->qMsgPort))
      {
        if (qMsg->qm_Status != QERROR_NO_ERROR) 
        {
          printf("-->Error %i detected (line %i), exiting!\n", qMsg->qm_Status, qMsg->qm_ErrorLine);
          BDie = TRUE;
        }
        else
        {
          struct SillyGameInfo * data = (struct SillyGameInfo *) qMsg->qm_Data;

          if ((data)&&(qMsg->qm_DataLen == sizeof(struct SillyGameInfo))) DoSillyUpdate(data);
        }
        FreeQMessage(session,qMsg);
      }
    }
    if (signals & (1L<<win->UserPort->mp_SigBit)) 
    {
      while (message = (struct IntuiMessage *)GT_GetIMsg(win->UserPort))
	  {
        ULONG class = message->Class;		/* extract needed info from message */
        ULONG code  = message->Code;

        /* tell Intuition we got the message */
        GT_ReplyIMsg(message);

        /* see what events occured, take correct action */
        switch(class)
        {		
          case IDCMP_CLOSEWINDOW: 
            BDie = TRUE;
          break;
      				
          case IDCMP_VANILLAKEY: 
            if ((code >= '0')&&(code <= '9'))
            {
              switch(code)
              {
                case '1': sgi.newX--; sgi.newY++; break;
                case '2': sgi.newY++;             break;
                case '3': sgi.newX++; sgi.newY++; break;
                case '4': sgi.newX--;             break;
                case '5':                         break;
                case '6': sgi.newX++;             break;
                case '7': sgi.newX--; sgi.newY--; break;
                case '8': sgi.newY--;             break;
                case '9': sgi.newX++; sgi.newY--; break;
              }
              if (sgi.newX < 0) sgi.newX = 0;
              if (sgi.newY < 0) sgi.newY = 0;
              if (sgi.newX >= BOARD_WIDTH)  sgi.newX = BOARD_WIDTH-1;
              if (sgi.newY >= BOARD_HEIGHT) sgi.newY = BOARD_HEIGHT-1;
            }
            else sgi.newC = code;

            UNLESS((QStreamOp(session, "data", &sgi, sizeof(struct SillyGameInfo)))&&(QGo(session, 0L)))
            {
              printf("Error sending info!\n");
              BDie = TRUE;
            }
            
            DoSillyUpdate(&sgi);
            sgi.oldX = sgi.newX;
            sgi.oldY = sgi.newY;
            sgi.oldC = sgi.newC;
          break;				
        }
      }
    }
    if (signals & SIGBREAKF_CTRL_C) BDie = TRUE;
  }
  
  /* Erase our guy from all the screens */
  sgi.newC = ' ';
  (void)QStreamOp(session, "data", &sgi, sizeof(struct SillyGameInfo)); 
  (void)QGo(session, 0L);
  
  /* Note: CleanExit() is called here, via the atexit() function! */
  return(0);
}
