/*
 * player.c
 *
 * Shows the board information to the human player and gathers inputs.
 * 
 * By Kamran Karimi
 */

#include "game.h"

int semid;
int shmid;
int msgid;

struct GameBoard *gb = (struct GameBoard *) -1; /* our shared memory! */

void Show_info(void)
{
 int count;
 
 printf("************************\n"); 
 printf("Scores:\n");
 for(count = 0; count < gb->NumPlayers; count++)
  printf("%s[pid: %d]@%s[%s]  Score: %d\n",gb->players[count].name,
                          gb->players[count].pid, 
                          gb->players[count].addr,
                          gb->players[count].spec, 
                          gb->scores[count]);
 printf("\n");     
 printf("String so far: %s\n", gb->found_string);
}

/* activate conversion routines from here. 
   If this is commented, the corresponding signals 
   should be ignored, otherwise you should make sure to 
   restart interruptable system calls, for example
   the semop() call. see below. */
static void shm_sig_handler(int signo)
{
 if(signo == DIPC_SIG_READER)
 {
  /* fprintf(stderr,"Player: BECAME A READER!\n"); */
  if(gb->architecture != MY_DIPC_ARCH)
   fprintf(stderr,"Player: Diferent Architectures!. Needs Conversion\n");
 }
 else if(signo == DIPC_SIG_WRITER)
 {
  /* fprintf(stderr,"Player: BECAME A WRITER!\n"); */
  if(gb->architecture != MY_DIPC_ARCH)
  {
   fprintf(stderr,"Player: Diferent Architectures!. Needs Conversion\n");
   gb->architecture = MY_DIPC_ARCH;
  } 
 } 
 else
  fprintf(stderr,"Player: SIGNAL %d\n", signo);  
}

static void alarm_handler(int signo)
{
 printf("\nPlayer: You didn't enter anything in time!\n");
}
 
int main()
{
 struct sembuf sem[2];
 struct message mess;
 struct sigaction sact;
 struct passwd *pw;
 char input[2];
 int start, end, count;
 int attempt = 0;
 struct utsname utsn;
 
 if(uname(&utsn) < 0)
 {
  fprintf(stderr, "Player: Can not get information about the system\n");
  exit(20);
 }
#ifdef __GLIBC__
 if((strlen(utsn.nodename) + strlen(utsn.__domainname) > MAX_ADDR) ||
#else
 if((strlen(utsn.nodename) + strlen(utsn.domainname) > MAX_ADDR) ||
#endif
    (strlen(utsn.sysname) + strlen(utsn.release) + 
     strlen(utsn.machine) > MAX_SPEC - 4))
 {
  fprintf(stderr, "Player: Insuficient space for system information\n");
  exit(20);
 }
 pw = getpwuid(getuid());
 if(!pw)
 {
  fprintf(stderr, "Player: Can not find my login name");
  exit(20);
 }
 sact.sa_handler = shm_sig_handler; /* or SIG_IGN */
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;

 if(sigaction(DIPC_SIG_WRITER, &sact, NULL) < 0)
 {
  fprintf(stderr,"Player:  ERROR: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }
 
 if(sigaction(DIPC_SIG_READER, &sact, NULL) < 0)
 {
  fprintf(stderr,"Player:  ERROR: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }
 sact.sa_handler = alarm_handler;
 sigemptyset(&sact.sa_mask);
 sact.sa_flags = 0;

 if(sigaction(SIGALRM, &sact, NULL) < 0)
 {
  fprintf(stderr,"Player:  ERROR: Can't Catch Signal");
  fprintf(stderr,"  BECAUSE: %s\n",strerror(errno));
 }
 
 semid = semget(SEM_KEY, 2, SEM_MODE);
 if(semid < 0)
 {
  fprintf(stderr,"Player: semget() failed BECAUSE %s\n",strerror(errno));
  exit(0);
 }
 /* sem[0] for initialization, sem[1] for result */
 shmid = shmget(SHM_KEY, sizeof(struct GameBoard), SHM_MODE);
 if(shmid < 0) 
 {
  fprintf(stderr,"Player: shmget() failed BECAUSE %s\n",strerror(errno));
  exit(20);
 }
 gb = (struct GameBoard *)shmat(shmid,0,0);
 if(gb == (struct GameBoard *) -1)
 {
  fprintf(stderr,"Player: shmat() failed BECAUSE %s\n", strerror(errno));
  exit(20);
 }
 msgid = msgget(MSG_KEY, MSG_MODE);
 if(msgid < 0) 
 {
  fprintf(stderr,"Player: msgget() failed BECAUSE %s\n",strerror(errno));
  exit(20);
 }
 mess.mtype = MSG_TYPE;
 strncpy(mess.player.name,pw->pw_name, MAX_NAME);
#ifdef __GLIBC__
 sprintf(mess.player.addr,"%s.%s", utsn.nodename,utsn.__domainname);
#else
 sprintf(mess.player.addr,"%s.%s", utsn.nodename,utsn.domainname);
#endif
 sprintf(mess.player.spec,"%s %s (%s)",utsn.sysname, utsn.release,
                                       utsn.machine);
 mess.player.pid = getpid();
 mess.architecture = MY_DIPC_ARCH;
 if(msgsnd(msgid, (struct msgbuf *)&mess, 
                           sizeof(int) + sizeof(struct Player), 0) < 0)
 {
  fprintf(stderr,"Player: msgsnd() failed BECAUSE: %s\n", strerror(errno));
  exit(20);
 }
 printf("Player: Informed the Game process of your arrival\n");
 while(1) /* main loop */
 {
  sem[0].sem_num = 0;
  sem[0].sem_op = -1; /* wait for the 'game' */
  sem[0].sem_flg = SEM_UNDO;
  /* if you want to get rid of this while loop, make the program ignore
     DIPC_SIG_READER and DIPC_SIG_WRITER signals. Note that if the 
     program is going to work in a heterogeneous system, you need these
     signals for data conversions */  
  while(1)
  {
   if(semop(semid, &sem[0], 1) < 0)
   {
    if(errno != EINTR)
    {
     fprintf(stderr,"Player: can't do semop() BECAUSE: %s\n",strerror(errno));
     exit(20);
    } 
   }
   else break;   
  }
  Show_info();
  if(!strcmp(gb->orig_string, gb->found_string))
  {
   printf("Player: String Was Found. Game Over\n");
   break;
  } 
  if(!strcmp(gb->players[gb->turn].addr, mess.player.addr) &&
     (gb->players[gb->turn].pid == getpid()))
  {
   attempt++;
   printf("Player: This is Your turn (#%d)  ",attempt);
   printf("Enter a character: ");
   fflush(stdout);
   alarm(PLAYER_TIMEOUT);
   input[0] = 0;
   read(STDIN_FILENO, input, 2);
   alarm(0);
   start = 0;
   do
   { 
    for(count = start; count < strlen(gb->orig_string); count++)
    {
     if(gb->found_string[count] == '-' ||
        gb->found_string[count] == ' ')
      break;
    }
    if(gb->found_string[count] == ' ') start = count + 1;
    else break;
   } while(1);
   for(end = start; end < strlen(gb->orig_string); end++)
   { 
    if(gb->found_string[end] == ' ') break;
   }
   for(count = start; count < end; count++)
   {
    if(gb->found_string[count] == '-' && gb->orig_string[count] == input[0])
    {
     gb->found_string[count] = input[0];
     (gb->scores)[gb->turn]++;
    } 
   }  
  }
  else
   printf("Player: this is the turn of %s@%s (pid: %d)!\n", 
    gb->players[gb->turn].name, gb->players[gb->turn].addr, 
    gb->players[gb->turn].pid); 
  sem[1].sem_num = 1;
  sem[1].sem_op = 1; /* allow the 'game' to continue */
  sem[1].sem_flg = SEM_UNDO;
  while(1)
  {
   if(semop(semid, &sem[1], 1) < 0)
   {
    if(errno != EINTR)
    { 
     fprintf(stderr, "Player: Can't do semop() BECAUSE: %s\n", strerror(errno));
     exit(20);
    } 
   }
   else break;
  } 
 }
 exit(0);       
}
