/*
 * main cycles program ..wait for messages to com in and
 *do "the right thing"
 */
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include "cycles.h"
#ifdef AZTEC_C
extern long  Enable_Abort;
#endif
extern BYTE   scr_image[];

long xdir[]   = { 0, 1, 0,-1};  /* up,right,down,left */
long ydir[]   = {-1, 0, 1, 0};  /* up,right,down,left */
long TIME     = 6;
long PLAYERS  = 2;
long GAMES    = 0;
long scores[MAX_COLOURS];

IMAGE   scr_data;             /* screen image data                      */
LIST    cycle_list;           /* list of current cycles                 */
TIMEREQ *tr;                  /* timer ExtIO request structure          */
PORT    *sPort;               /* server port                            */
PORT    *tPort;               /* timer port                             */
long     num_players = 0;     /* how many players are there             */
long     num_waiting = 0;     /* how many are waiting for new game      */
long     num_alive   = 0;     /* number of cycles that are still moving */

extern struct Window *war_window;  /* our window c/w close gadget */

main(argc,argv)
long argc; char **argv;
{
  ORDER   *msg;               /* order from client                      */
  CYCLE   *cycle;             /* current cycle structure                */
  long portsig,closesig;      /* signal bits for our ports              */
  long signals;               /* reserved signals                       */
  long used_colours = 0;      /* bitmask showing which colours are used */
  long i;                     /* misc.                                  */
  long games_played;

#ifdef AZTEC_C
  Enable_Abort = 0;
#endif
  /* initialize screen data structures, will tell clients the shape
   * of the screen by providing a pointer to this data
   */
  scr_data.x     = X_SIZE;
  scr_data.y     = Y_SIZE;
  scr_data.buf   = scr_image;
  tr             = 0;
  games_played   = 0;

  for(i=1; i<argc; i++) {
    switch(argv[i][0]) {
      case 't':  TIME = atoi(argv[i] + 1);
                 break;
      case 'p':  PLAYERS = atoi(argv[i] + 1);
                 if (PLAYERS < 2) PLAYERS = 2;
                 break;
      case 'g':  GAMES = atoi(argv[i] + 1);
                 break;
      }
    }
  sPort = CreatePort("Cycle Server",0L);
  if (!sPort) {
    printf("Error, can't open server port\n");
    terminate();
    }
  tr = Timer_Open(UNIT_MICROHZ,sPort);
  if (!tr) terminate();
  /* open up custom screen etc... */
  if (g_init()) terminate();

  draw_scores();

  /* do setup */
  g_restart();

  portsig  = (1<<(sPort->mp_SigBit));
  closesig = (1<< war_window->UserPort->mp_SigBit);

  srand(time(0)); /* seed the random number generator */
  NewList(&cycle_list);
  Timer_Post(tr, TIME / 1000,(TIME % 1000) * 1000);

  for(;;) {
    /* wait for either a time event or a client */
    signals = Wait(closesig | portsig | SIGBREAKF_CTRL_C);

    if (signals & portsig) {
      while(msg = (ORDER *)GetMsg(sPort)) {
        if ((TIMEREQ *)msg == tr) { /* time request reply */
          if (num_alive) move_cycles(&cycle_list);
            Timer_Post(tr,
                  TIME * num_alive / 1000,           /* seconds */
                  ((num_alive * TIME) % 1000) * 1000 /* micros  */
                  );
          continue;
        }
        cycle = msg->cycle;

        if (!cycle) {
          for (i=0; i<8; i++)
            if ((~used_colours) & (1<<i)) break;
          if (i == MAX_COLOURS) {
            printf("Error, no colours left for the new cycle\n");
            msg->status = STATUS_REMOVED;
            ReplyMsg(msg);
            continue;
          }
          cycle = AllocMem(sizeof(CYCLE),MEMF_PUBLIC);
          if (!cycle) {
            printf("Error, can't get memory for new cycle\n");
            msg->status = STATUS_REMOVED;
            ReplyMsg(msg);
            continue;
          }
          AddHead(&cycle_list, &cycle->node);
          num_players++;
          msg->cycle        = cycle;
          msg->table        = &scr_data;
          cycle->status     = STATUS_LOSER;
          cycle->dir        = DIR_RIGHT;
          cycle->x          = rand() % (X_SIZE - 10) + 5;
          cycle->y          = rand() % (Y_SIZE - 10) + 5;
          cycle->colour     = i;
          cycle->msg        = NULL;
          used_colours     |= (1<<i);
          scores[i]         = 0;
        }
        switch(msg->order) {
          case ORD_DIRECTION:
              if (cycle->status < STATUS_DEAD) {
                cycle->dir    = msg->dir;
                cycle->status = STATUS_GOT_MOVE;
                cycle->msg    = msg;
              }
              else {
                msg->status = cycle->status;
                ReplyMsg(msg);
              }
              continue;
          case ORD_AWAIT:
              if (cycle->status < STATUS_DEAD) /* still alive */
                num_alive--;
              cycle->msg    = msg;
              cycle->status = STATUS_AWAITING;
              num_waiting++;
              if (num_waiting == num_players) {
                if (GAMES > 0 && games_played >= GAMES)
                  terminate();
                else if (num_players >= PLAYERS) {
                  g_restart();
                  restart_cycles(&cycle_list);
                  games_played++;
                }
              }
              continue;

          case ORD_RETIRE:
              Remove(cycle);
              if (cycle->status < STATUS_DEAD) /* still alive */
                num_alive--;
              FreeMem(cycle,sizeof(CYCLE));
              num_players--;
              used_colours   &= ~(1<<cycle->colour);
              msg->status     = STATUS_REMOVED;
              ReplyMsg(msg);
              continue;

          default:
              printf("Got an invalid request, Ignoring\n");
              ReplyMsg(msg);
              continue;
        }
      }
    }
    if (signals & SIGBREAKF_CTRL_C || signals & closesig)
      terminate();
  }
}

terminate()
{
  ORDER *msg;

  free_cycles(&cycle_list);

  if (tr) {
    WaitIO(tr);
    tr->tr_node.io_Message.mn_ReplyPort = 0L;
    Timer_Close(tr);
  }
  /* inform all players that are still active that we are shutting down */
  while (num_players) {
    WaitPort(sPort);
    while(msg = (ORDER *)GetMsg(sPort)) {
      msg->status = STATUS_REMOVED;
      /* if a new player tries to join, don't      */
      /* decrement the coiunt the actual structure */
      /* has already been free so don't look at it */
      if (msg->cycle) num_players--;
      ReplyMsg(msg);
    }
  }
  g_finish();
  /* here we clear out the Port before deleting it, it is important to
   * Forbid() first so that no new messages arrive at the port between
   * the time we last look at it and the time it is DeletePort'ed
   */

  Forbid();
  while(msg = (ORDER *)GetMsg(sPort)) {
    msg->status = STATUS_REMOVED;
    ReplyMsg(msg);
  }
  DeletePort(sPort);
  Permit();
  exit(0);
}
