/* 
 * game.c 
 *
 * game will prepare the game board as a shared memeory, then waits 
 * for the specified number of players via a message queue. 
 * It selects the player of each round.
 *
 * By Kamran Karimi
 */
   
#include "game.h"

int semid = -1;
int shmid = -1;
int msgid = -1;
struct GameBoard *gb = (struct GameBoard *) -1; /* our shared memory! */

static void sig_handler(int signo)
{
 fprintf(stderr,"Game: Quiting\n");

 if(semid > -1)
  if(semctl(semid, SEM_NUM, IPC_RMID, 0) < 0)
   fprintf(stderr,"Game: failed to remove the semaphore, BECAUSE %s\n", 
                                                           strerror(errno));
 if(gb != (struct GameBoard *) -1)
  shmdt((char *)gb);
 if(shmid > -1) 
  if(shmctl(shmid, IPC_RMID, 0) < 0)
   fprintf(stderr,"Game: failed to remove the shared memory, BECAUSE %s\n", 
                                                             strerror(errno));
 if(msgid > -1)
  if(msgctl(msgid,IPC_RMID,NULL) < 0)
   fprintf(stderr,"Game: failed to remove the message queue, BECAUSE %s\n", 
                                                               strerror(errno));
 exit(5);
}

static void shm_sig_handler(int signo)
{
 if(signo == DIPC_SIG_READER)
 {
  /* fprintf(stderr,"Game: BECAME A READER!\n"); */
  if(gb->architecture != MY_DIPC_ARCH)
   fprintf(stderr,"Game: Diferent Architectures!. Needs Conversion\n");
 }
 else if(signo == DIPC_SIG_WRITER)
 {
  /* fprintf(stderr,"Game: BECAME A WRITER!\n"); */
  if(gb->architecture != MY_DIPC_ARCH)
  {
   fprintf(stderr,"Game: Diferent Architectures!. Needs Conversion\n");
   gb->architecture = MY_DIPC_ARCH;
  }
 } 
 else
  fprintf(stderr,"Game: SIGNAL %d\n", signo);  
}


int main(int argc, char **argv)
{
 int count, NumPlayers;
 unsigned short seminit[2]; 
 struct sembuf sem[2];
 struct sigaction sact;
 struct message mess;
 char temp1[MAX_STRING + 20], temp2[MAX_STRING + 20];
 char has_char;
 
 if(argc != 3)
 {
  printf("USAGE: %s <Number Of Players> <\"String To Find\">\n", argv[0]);
  exit(0);
 }

 printf("Game: Initializing...\n");
 /* read the command line parameters */
 NumPlayers = atoi(argv[1]);
 if(NumPlayers < 1)
 {
  fprintf(stderr,"Game: There should be at least one player\n");
  exit(20);
 }
 if(NumPlayers > MAX_PLAYERS)
 {
  NumPlayers = MAX_PLAYERS;
  fprintf(stderr,"Game: Too many players. Changed to %d\n", MAX_PLAYERS);
 }
 strcpy(temp1, argv[2]);
 if(strlen(temp1) < MIN_STRING)
 {
  fprintf(stderr,
   "Game: string is too small. should have >= %d characters\n",MIN_STRING);
  exit(20);
 }
 if(strlen(temp1) > MAX_STRING)
 {
  temp1[MAX_STRING - 1] = 0;
  fprintf(stderr,"Game: string too long. The first %d characters retained\n", 
                                                                   MAX_STRING);
 } 
 /* strip start and end blanks. do some simple checks on the input string */
 for(count = strlen(temp1) - 1; count >= 0; count--)
 {
  if(temp1[count] != ' ') break;
 }
 temp1[count + 1] = 0;
 count = 0;
 while(temp1[count] == ' ') count++;
 strcpy(temp2, &temp1[count]);
 strcpy(temp1, temp2); 
 has_char = 0;
 for(count = 0; count < strlen(temp1); count++)
 {
  if(temp1[count] == ' ')
   temp2[count] = ' ';
  else
  {
   char ch = tolower(temp1[count]);
   if(ch >= 'a' && ch <= 'z')
   { 
    temp2[count] = '-';
    has_char = 1;
   } 
   else
   {
    fprintf(stderr,
      "Game: String Should only have one of [a-z,A-Z,' '] characters\n");
    exit(20);
   }
  }  
 }
 if(has_char == 0)
 {
  fprintf(stderr,"Game: String has no valid characters\n");
  exit(20);
 }
 temp1[count] = 0; 

 sact.sa_handler = shm_sig_handler;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;
 if(sigaction(DIPC_SIG_READER, &sact, NULL) < 0)
  fprintf(stderr,"Game: Can't Catch Signal, BECAUSE: %s\n",strerror(errno));
 
 if(sigaction(DIPC_SIG_WRITER, &sact, NULL) < 0)
  fprintf(stderr,"Game: Can't Catch Signal, BECAUSE: %s\n", strerror(errno));
  
 sact.sa_handler = sig_handler;
 if(sigaction(SIGINT, &sact, NULL) < 0)
  fprintf(stderr,"Game: Can't Catch Signal, BECAUSE: %s\n",strerror(errno));
  
 /* prepare the semaphores */
 semid = semget(SEM_KEY, SEM_NUM, SEM_MODE | IPC_CREAT);
 if(semid < 0)
 {
  fprintf(stderr,"Game: semget() failed BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }
 /* sem[0] for initialization, sem[1] for result */
 seminit[0] = seminit[1] = 0;
 if(semctl(semid, SEM_NUM, SETALL, (union semun) seminit) < 0)
 {
  fprintf(stderr,
   "Game: Can not initialize the semaphores BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }
 msgid = msgget(MSG_KEY, MSG_MODE | IPC_CREAT);
 if(msgid < 0) 
 {
  fprintf(stderr,"Game: msgget() failed: BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }
 /* prepare the shared memory */
 shmid = shmget(SHM_KEY, sizeof(struct GameBoard), SHM_MODE | IPC_CREAT);
 if(shmid < 0) 
 {
  fprintf(stderr,"Game: shmget() failed: BECAUSE %s\n", strerror(errno));
  raise(SIGINT);
 }
 gb = (struct GameBoard *)shmat(shmid,0,0);
 if(gb == (struct GameBoard *) -1)
 {
  fprintf(stderr,"Game: shmat() failed BECAUSE %s\n",strerror(errno));
  raise(SIGINT);
 }
 gb->architecture = MY_DIPC_ARCH;
 gb->NumPlayers = NumPlayers;
 strcpy(gb->orig_string, temp1);
 strcpy(gb->found_string, temp2);
 for(count = 0; count < NumPlayers; count++)
  gb->scores[count] = 0; 
 printf("Game: Initialization Done.\n");
 count = 0;
 printf("Game: Waiting for %d player(s).\n", NumPlayers);
 /* wait till all the messages have arrived */
 while(count < NumPlayers)
 {
  if(count > 0)
   printf("Game: Waiting for %d more players\n", NumPlayers - count);    
  if(msgrcv(msgid, (struct msgbuf *)&mess, 
                          sizeof(int) + sizeof(struct Player), 0, 0) < 0)
  {
   fprintf(stderr,"Game: msgrcv() failed: %s\n", strerror(errno));
   raise(SIGINT);
  } 
  fprintf(stderr,"Game: Player Number %d (%s@%s) Arrived\n",count + 1, 
                                      mess.player.name, mess.player.addr);
  if(mess.architecture != MY_DIPC_ARCH)
   fprintf(stderr,
     "Game: Diferent architectures. needs conversion\n");                                    
  strcpy(gb->players[count].name, mess.player.name);
  strcpy(gb->players[count].addr, mess.player.addr);
  strcpy(gb->players[count].spec, mess.player.spec);
  gb->players[count].pid = mess.player.pid;
  count++;
 }
 printf("Game: All players arrived\n");
 gb->turn = -1;
 while(1)
 {
  gb->turn = gb->turn + 1;
  if(gb->turn >= NumPlayers)
   gb->turn = 0;
  sem[0].sem_num = 0;
  sem[0].sem_op = NumPlayers; /* allow players to continue */
  sem[0].sem_flg = SEM_UNDO;
  /* check for shared memory reader/writer interrupts */
  while(1)
  {
   if(semop(semid, &sem[0], 1) < 0)
   {
    if(errno != EINTR)
    { 
     fprintf(stderr,"Game: Can't do semop() BECAUSE: %s\n", strerror(errno));
     raise(SIGINT);
    } 
   }
   else break;
  }
  if(!strcmp(gb->orig_string, gb->found_string))
  {
   printf("Game: The string Was found. Game Over\n");
   break;
  } 
  sem[1].sem_num = 1;
  sem[1].sem_op = -NumPlayers; /* wait till all the players are done */
  sem[1].sem_flg = SEM_UNDO;
  while(1)
  {
   if(semop(semid, &sem[1], 1) < 0)
   {
    if(errno != EINTR)
    { 
     fprintf(stderr, "Game: Can't do semop() BECAUSE: %s\n", strerror(errno));
     raise(SIGINT);
    } 
   }
   else break;
  }    
 }
 raise(SIGINT);
 exit(0);
}
