#include "internal.h"
#include <strings.h>

/*-----------------------*/
/* Global Variables      */
/*-----------------------*/

extern int             sd;
extern KPlayer          *currentPlayer;
extern KPlayer          *player1;
extern KPlayer          *player2;
extern KPlayer          *player3;
extern KPlayer          *player4;
extern KTileList        *Bag;

extern KWordList        *newWords;

extern pthread_mutex_t p1Lock;
extern pthread_mutex_t p2Lock;
extern pthread_mutex_t p3Lock;
extern pthread_mutex_t p4Lock;
extern pthread_mutex_t bagLock;
extern pthread_mutex_t currentPlayerLock;
extern pthread_mutex_t canChallengeLock;
extern pthread_mutex_t numberOfAPLock;
extern pthread_mutex_t numberOfPlayersLock;
extern pthread_mutex_t writeLock;

extern pthread_t       getPlayersTid;
extern pthread_t       timerTid;
extern int             numberOfPlayers;
extern int             gameStarted;
extern int             gameFinished;
extern int             canChallenge;
extern int             numberOfAP;
extern int             lastPersonNotBMove;
extern int             noMoreGame;

extern sem_t           finishedGettingPlayers;
extern sem_t           finishedCurrentPlayerMove;
extern sem_t           waitForChallenge;


KBMove *saveGameLastMovePtr;

extern int score[27];

int lastScore;

/*--------------------------------------------------*/   

int
KgetPlayerInfo(PlayerPacket *packet)
 {
  /* Debugged by Navid Azizi on October 1st */

  /* This function returns the playerID and score for each active player in the game
   * Pre: p is the player who requested the information, packet is defined and will return
          the information
   * Post: The requested information will be returned in the PlayerPacket packet and
           OK will be returned.  If packet is invalid, INVALID will be returned */

  int i;
  KPlayer *next;

  /* Check for invalid input */
  if (packet != NULL)
   {   
    
    /* Store values in packet */    
    packet->type = PLAYERINFORETURN;
    packet->numberOfPlayers = numberOfPlayers;
    (packet->message)[0] = '\0';

    next = currentPlayer;
    /* Go through all players and get their pid and score */
    for (i = 0; i < numberOfPlayers; i++)
     {
      packet->pid[i] = KgetPlayerID(next);
      packet->score[i] = KgetPlayerScore(next);
      next = KgetNextPlayer(next);
     }
    return(OK);
   }	 
  else
    return(INVALID);
  }	
  
/*--------------------------------------------------*/   
  
int
Kresign(KPlayer *p)
 {
  /* If p is the currentplayer, then remove all his tiles, and place them back
   * in the bag, and adjust his score, and he no longer is able to play.
   * Pre: p must be defined
   * Post: if playerID is the currentplayer, then he is resigned and OK is returned, else  
   *       NOTCURRENTPLAYER is returned     */

  pthread_mutex_t *lock;
  KTile *t;
  double temp;
  int n;

  if (p == currentPlayer)
   {
 
    /* Get a lock on the player */
    lock = KgetLockForPlayer(p);
 
    if (lock == NULL)
      return(MISCERROR);

    pthread_mutex_lock(&bagLock);
    pthread_mutex_lock(lock);

    while (p->rack->length > 0)
     {
      t = KdequeueHead(p->rack);
      p->score = p->score - t->score;
      
      /* Find a random position to add tile */
      temp = drand48();                /* Generates 0.0 to 1.0 */
      n = temp*((Bag->length)+1);      /* Generates 0 to length of bag */
      if (n == (Bag->length)+1)
        n = (Bag->length);


      /* Insert tile in random position */
      KnumberEnqueue(t, Bag, n);
     }
    
    /* Set player to resigned, and adjust number of players */
    p->alive = RESIGNED;
    numberOfPlayers--;

    pthread_mutex_unlock(lock);
    pthread_mutex_unlock(&bagLock);
	
	if (numberOfPlayers == numberOfAP)
	 {
	  noMoreGame = 1;
	 }  

    return(OK);
   }
  return(NOTCURRENTPLAYER);
 }

/*--------------------------------------------------*/ 

int
KgetPlayerTray(KPlayer *p, TrayPacket *packet)
 {
  /* Debugged by Navid Azizi on October 1st */


  /* This function will return player p's Tray in a traypacket
   * Pre: p and pakcet must be defined.
   * Post: The player's packet will be returned in a packet and OK will be returned on 
           success, MISCERROR will be returned on an error */

  pthread_mutex_t *lock;
  KTile *t;
  int   i;

  /* Find the lock for the player and lock him */
  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  /* Go through the tray and put information into the packet */
  if (packet != NULL)
   {
    i = 0;
    t = p->rack->Tiles;
    while (t != NULL)
     {
      (packet->letters)[i] = t->letter;
      (packet->scores)[i]  = t->score;
      t = t->link;
      i++;
     }
   }	 
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }

  /* Place some info in the packet */
  packet->type = SHOWTRAYRETURN;
  packet->numberOfTiles = i;
  pthread_mutex_unlock(lock);
  return(OK);

 }
  
/*--------------------------------------------------*/   
   
int
KswapMove(KPlayer *p, SMovePacket *packet)
 {
  /* Debugged by Navid Azizi on October 1st */

  /* This function will move some of a player's pieces to the bag
   * Pre: p and packet must be defined
   * Post: If p is the current player, some of his pieces will be placed in the bag and OK returned
           On failure, MISCERROR or NOTCURRENTPLAYER will be returned */

  pthread_mutex_t *lock;
  KTile            *t;
  int              i;
  KTileList        *tempRack;
  double           temp;
  int              n;

  /* Check if current player*/
  if (p == currentPlayer)
   {

    /* Get lock on player */
    lock = KgetLockForPlayer(p);

    if ((lock == NULL) || (packet == NULL))
      return(MISCERROR);

    pthread_mutex_lock(&bagLock);
    pthread_mutex_lock(lock);

    /* Create a temporary rack to keep letters of move in */
    tempRack = KcreateGroup(RACK);

    /* Check if all the letters in the move are in the rack */
    for (i = 0; i < packet->theMove.numMoves; i++)
     {

      /* Find the tile for that letter */
      t = KfindLetter(packet->theMove.letters[i],p->rack);

      /* If the letter is not there put all tiles from temprack to player's rack */
      if (t == NULL)
       {
        while (tempRack->length > 0)
         {
          t = KdequeueHead(tempRack);
          KheadEnqueue(t,p->rack);
         }

        /* Free memory, release locks and return when move is invalid */
        KdestroyList(tempRack);
        pthread_mutex_unlock(lock);
        return(INVALID);
       }

      /* Take letter out and put in tempRack */
      else
       {
        KdequeueTile(t);
        KheadEnqueue(t,tempRack);
       }
     }

    /* All letters in move are in rack, now place them in the bag */
    for (i = 0; i < packet->theMove.numMoves; i++)
     {
      /* Put the tiles back in the bag */
      t = KdequeueHead(tempRack);

      /* Find a random position to add tile */
      temp = drand48();                /* Generates 0.0 to 1.0 */
      n = temp*((Bag->length)+1);      /* Generates 0 to length of bag */
      if (n == (Bag->length)+1)
        n = (Bag->length);


      /* Insert tile in random position */
      KnumberEnqueue(t, Bag, n);
     }

    KdestroyList(tempRack);

    /* unlock the locks */
    pthread_mutex_unlock(lock);
    pthread_mutex_unlock(&bagLock);

    return(OK);
   }
  return(NOTCURRENTPLAYER);
 }

  
/*-----------------------------------------------------------------*/

int
KboardMove(KPlayer *p, BMovePacket *packet)
 {
  /* Debugged by Navid Azizi on October 4th */

  /* This function will try to play player p's move which is in packet
   * Pre: p and pakcet must be defined.
   * Post: If the move is valid, the move will be played and OK returned, else MISCERROR, or NOTCURRENTPLAYER 
           will be returned */

  pthread_mutex_t *lock;
  KTile            *t;
  KTileList        *tempRack;
  int              i;
  KScore           moveScore;
  KBMove           testMove;

  /* Check if current player*/
  if (p == currentPlayer)
   {

    /* Get lock on player */
    lock = KgetLockForPlayer(p);

    if ((lock == NULL) || (packet == NULL))
      return(MISCERROR);

    pthread_mutex_lock(lock);

    /* Create a temporary rack to keep letters of move in */
    tempRack = KcreateGroup(RACK);

    /* Check if all the letters in the move are in the rack */
    for (i = 0; i < packet->theMove.numMoves; i++)
     {

      /* Find the letter in the rack */
      t = KfindLetter((packet->theMove.moves)[i].letter,p->rack);

      /* If letter is not found, but a blank exists, take that out */
      if (t == NULL)
        t = KfindLetter('?',p->rack);

      /* If the letter is not there put all tiles from temprack to player's rack */
      if (t == NULL)
       {
        while (tempRack->length > 0)
         {
          t = KdequeueHead(tempRack);
          KheadEnqueue(t,p->rack);
         }

        /* Free memory, release locks and return when move is invalid */
        KdestroyList(tempRack);
        pthread_mutex_unlock(lock);
        return(INVALID);
       }

      /* Take letter out and put in tempRack */
      else
       {
        KdequeueTile(t);
        KheadEnqueue(t,tempRack);
       }
     }

    /* All letters in move are in rack, can ask BM for validness */

    bcopy(&(packet->theMove),&testMove,sizeof(KBMove));
	
    /* Check the validity of the move */
    if (KCheckBMove(testMove) == TRUE)
     {
      free(newWords);
      newWords = KNewWords(testMove);
      moveScore = KAddBMove(testMove);
      lastScore = moveScore;
      p->score = p->score + moveScore;
      KdestroyWholeList(tempRack);
     }

    /* Board move is not valid, return tiles in temprack to rack */
    else
     {	 
      while (tempRack->length > 0)
       {
        t = KdequeueHead(tempRack);
        KheadEnqueue(t,p->rack);
       }
      KdestroyList(tempRack);        
      pthread_mutex_unlock(lock);
      return(INVALID);
     }

    pthread_mutex_unlock(lock);

    return(OK);
   }

  /* Not current player */
  else
   {
    return(NOTCURRENTPLAYER);
   }
 }

  
/*-----------------------------------------------------------------*/

KScore
KtryMove(KPlayer *p,BMovePacket *packet)
 {
  /* Debugged by Navid Azizi on October 4th */

  /* This function will try to score player p's move which is in packet
   * Pre: p and pakcet must be defined.
   * Post: If the move is valid, the score will be returned, else MISCERROR
           will be returned */

  pthread_mutex_t *lock;
  KTile            *t;
  KTileList        *tempRack;
  int              i;
  KScore           moveScore;
  KBMove           testMove;

  /* Get lock on player */
  lock = KgetLockForPlayer(p);

  if ((lock == NULL) || (packet == NULL))
    return(MISCERROR);


  pthread_mutex_lock(lock);

  /* Create a temporary rack to keep letters of move in */
  tempRack = KcreateGroup(RACK);

  /* Check if all the letters in the move are in the rack */
  for (i = 0; i < packet->theMove.numMoves; i++)
   {
  
    /* Find the letter in the rack */
    t = KfindLetter((packet->theMove.moves)[i].letter,p->rack);

    /* If letter is not found, but a blank exists, take that out */
    if (t == NULL)
      t = KfindLetter('?',p->rack);


    /* If the letter is not there put all tiles from temprack to player's rack */
    if (t == NULL)
     {
      while (tempRack->length > 0)
       {
        t = KdequeueHead(tempRack);
        KheadEnqueue(t,p->rack);
       }

      /* Free memory, release locks and return when move is invalid */
      KdestroyList(tempRack);        
      pthread_mutex_unlock(lock);
	  printf("returning invalid due to %c not being in rack\n",(packet->theMove.moves)[i].letter);
	  KprintList(p->rack);
      return(INVALID);
     }

    /* Take letter out and put in tempRack */
    else
     {
      KdequeueTile(t);
      KheadEnqueue(t,tempRack);
     }
   }

  /* All letters in move are in rack, can ask BM for validness */

  bcopy(&(packet->theMove),&testMove,sizeof(KBMove));

  /* Check the validity of the move */
  if (KCheckBMove(testMove) == TRUE)
    moveScore = KScoreBMove(testMove);     
  else
   {
    /* Place pieces back in players rack and return invalid */
    while (tempRack->length > 0)
     {
      t = KdequeueHead(tempRack);
      KheadEnqueue(t,p->rack);
     }
    KdestroyList(tempRack);

    pthread_mutex_unlock(lock);
    return(INVALID);
   }

  /* Put pieces back in Rack */
  while (tempRack->length > 0)
   {
    t = KdequeueHead(tempRack);
    KheadEnqueue(t,p->rack);
   }
  KdestroyList(tempRack);

  pthread_mutex_unlock(lock);

  return(moveScore);
 }


/*-----------------------------------------------------------------*/

int
KshowBoard(BoardPacket *packet)
 {
  /* Debugged by Navid Azizi on October 1st */

  /* This function gets the status of the board and places it in a boardPacket
   * Pre: packet must be defined
   * Post: The boardstatus will be placed in packet and OK returned on success
           On failure, MISCERROR will be returned */

  int i;
  int j;
  KGridRef gref;

  if (packet != NULL)
   {
    /* Go through the move and ask BM for each tile and place in the packet */  
    for (i = 0; i < 15; i++)
	 {
      for (j = 0; j < 15; j++)
       {
        gref.x = i;
        gref.y = j;
        if (KEmptySquare(gref) == TRUE)
          (packet->board)[i][j] = (char)0;
        else
          (packet->board)[i][j] = KTileSquare(gref);
       }
	  } 
     return(OK);
    }
  else
   return(MISCERROR);
 }
   

/*-----------------------------------------------------------------*/

int
KChallenge(KPlayer *p)
 {
  /* Debugged by Navid Azizi on October 5 */

  /* Challenges the currentplayer which is p 
   * Pre: p must be defined
   * Post: If challenge is successful OK is returned, and the player picks up his tiles.  If challenge is not 
           successful UNSUCCESSFULCHALLENGE is returned*/

  int      wordNotExists=0;
  int      i;
  FILE     *look;
  char     command[25]="grep -w ";
  char     command1[25];
  char     *test;
  char     tempWord[20];
  char     word[20];
  KBMove    *deletedMove;
  KScore    letterscore;
  KTile     *t;
  pthread_mutex_t *lock;
  int       delme;
  

  wordNotExists = 0;


  /* Go through the newwords that were generated by the last move */
  for (i = newWords->num_words-1; i >= 0; i--)
   {
    strcpy(word,newWords->words[i]);  
   
    strcpy(command1,command);

    /* Make the command string to and call unix */
    strcat(command1,word);
    strcat(command1," blah.dic > check.txt\0");

    system(command1);

    /* open the file */
    look = fopen("check.txt","r");
    if (look == NULL)
     {
      perror("ERROR opening file\n");
      exit(1);
     }

    /* Get the first line only, if not on first line, then word will not be in the rest */ 
    test = fgets(tempWord,20,look);
 
    /* No words match, exist loop, challenge is successful */
    if (test == NULL)
     {
      wordNotExists = 1;
      break;
     }
    else
      tempWord[strlen(tempWord)-1] = '\0';


    /* Compare the word from lookup */
    if (strcmp(word,tempWord) == 0)
     {

      fclose(look);
      system("rm check.txt");
     }
    else
     {
      wordNotExists = 1;
      fclose(look);
      system("rm check.txt");
      break;
     }
   }


  if (wordNotExists == 0)
   {
    return(UNSUCCESSFULLCHALLENGE);
   }

  /* Challenge is successful, remove tiles from board, and subtract score */
  else
   {

    deletedMove = KLastMove();

    lock = KgetLockForPlayer(currentPlayer);

    if (lock == NULL)
      return(MISCERROR);

    pthread_mutex_lock(lock);


    /* For all moves, remove tile, place back on rack */
    for (i = deletedMove->numMoves - 1; i >= 0; i--)
     {

      delme = KRemoveTile((deletedMove->moves)[i].Pos);


      /* Check if the tile was originally a blank */
      if ((deletedMove->moves)[i].fromBlank == 1)
       {
        letterscore = 0;
        t = KallocTile('?',letterscore);
       }
      else
       {
        letterscore = score[(deletedMove->moves)[i].letter - 97];
        t = KallocTile((deletedMove->moves)[i].letter,letterscore);
       }
      KheadEnqueue(t,currentPlayer->rack);
     }

    currentPlayer->score = currentPlayer->score - lastScore;

    pthread_mutex_unlock(lock);


    return(OK);      
   }
 }

/*-----------------------------------------------------------------*/

int
KsaveGame(KPlayer *p)
 {

  KTile            *t;
  int              i,j;
  FILE             *f;
  KPlayer          *next;
  KGridRef         pos;
  int              playernumber;

  /* Check if current player*/
  if (p == currentPlayer)
   {


    pthread_mutex_lock(&bagLock);
    pthread_mutex_lock(&p1Lock);
    pthread_mutex_lock(&p2Lock);
    pthread_mutex_lock(&p3Lock);
    pthread_mutex_lock(&p4Lock);


    f = fopen("savegame","w");

    if (f == NULL)
     {

      pthread_mutex_unlock(&p4Lock);
      pthread_mutex_unlock(&p3Lock);
      pthread_mutex_unlock(&p2Lock);
      pthread_mutex_unlock(&p1Lock);
      pthread_mutex_unlock(&bagLock);
      return(PROBLEMSAVINGGAME);
     }

    fprintf(f,"currentplayer ");

    if (currentPlayer == player1)
     {
      fprintf(f,"1\n");
     } 
    else if (currentPlayer == player2)
     {
      fprintf(f,"2\n");
     } 
    else if (currentPlayer == player3)
     {
      fprintf(f,"3\n");
     } 
    else if (currentPlayer == player4)
     {
      fprintf(f,"4\n");
     }

    fprintf(f, "numberofplayers %i\n",numberOfPlayers);

    next = player1;
    for (i = 0; i < numberOfPlayers; i++)
     {
      if (next == player1)
        playernumber = 1;
      else if (next == player2)
        playernumber = 2;
      else if (next == player3)
        playernumber = 3;
      else if (next == player4)
        playernumber = 4;

      fprintf(f,"player %i %i %i %i %i %i", playernumber,next->alive,next->score,next->howManyMoves,next->lostturn,next->rack->length);
      t = next->rack->Tiles;
      while (t != NULL)
       {
        fprintf(f," %c %i",t->letter,t->score);
        t = t->link;

       }
      fprintf(f,"\n");

      next = KgetNextPlayerWithOutLock(next);

     }

    fprintf(f,"Bag %i",Bag->length);
    t = Bag->Tiles;
    while (t != NULL)
     {
      fprintf(f," %c %i",t->letter,t->score);
      t = t->link;
     }

    fprintf(f,"\nBoard\n");
    for (i = 0; i < LENGTH; i++)
     {
      for (j = 0; j < LENGTH; j++)
       {
        pos.x = i;
        pos.y = j;
        if (KEmptySquare(pos)) 
         {
          fprintf(f," ");
         }
        else
         {
          fprintf(f,"%c",KTileSquare(pos));
         } 
       }
      fprintf(f,"\n");
     }

    saveGameLastMovePtr = KLastMove();
	
    pthread_mutex_unlock(&p4Lock);
    pthread_mutex_unlock(&p3Lock);
    pthread_mutex_unlock(&p2Lock);
    pthread_mutex_unlock(&p1Lock);
    pthread_mutex_unlock(&bagLock);

    fclose(f);
    return(OK);
   }
  else
    return(NOTCURRENTPLAYER);
 }

/*-----------------------------------------------------------------*/

int
KrestoreGame(KPlayer *p)
 {

  KTile            *t;
  int              i,j;
  FILE             *f;
  KPlayer          *next;
  char             buffer[500];
  int              tempint;
  int              tempint2;
  char             tempchar;
  char             tempboard[LENGTH][LENGTH];

  /* Check if current player*/
  if (p == currentPlayer)
   {
 
    pthread_mutex_lock(&bagLock);
    pthread_mutex_lock(&p1Lock);
    pthread_mutex_lock(&p2Lock);
    pthread_mutex_lock(&p3Lock);
    pthread_mutex_lock(&p4Lock);

    f = fopen("savegame","r");

    if (f == NULL)
     {
      pthread_mutex_unlock(&p4Lock);
      pthread_mutex_unlock(&p3Lock);
      pthread_mutex_unlock(&p2Lock);
      pthread_mutex_unlock(&p1Lock);
      pthread_mutex_unlock(&bagLock);
      return(PROBLEMRESTORINGGAME);
     }

    fscanf(f,"%s",buffer);
	
    if (strcmp(buffer,"currentplayer") != 0)
     {
      pthread_mutex_unlock(&p4Lock);
      pthread_mutex_unlock(&p3Lock);
      pthread_mutex_unlock(&p2Lock);
      pthread_mutex_unlock(&p1Lock);
      pthread_mutex_unlock(&bagLock);
      return(PROBLEMRESTORINGGAME);
     }

    fscanf(f,"%i",&tempint);

    if (tempint == 1)
      currentPlayer = player1;
    else if (tempint == 2)
      currentPlayer = player2;
    else if (tempint == 3)
      currentPlayer = player3;
    else if (tempint == 4)
      currentPlayer = player4;

    fscanf(f,"%s",buffer);
    fscanf(f,"%i",&numberOfPlayers);
	
    for (i = 0; i < numberOfPlayers; i++)
     {
      fscanf(f,"%s",buffer);
      fscanf(f,"%i",&tempint);
	  
      if (tempint == 1)
        next = player1;
      else if (tempint == 2)
        next = player2;
      else if (tempint == 3)
        next = player3;
      else if (tempint == 4)
        next = player4;

      KdestroyWholeList(next->rack);
      next->rack = KcreateGroup(RACK);
      fscanf(f,"%i %i %i %i %i", &(next->alive),&(next->score),&(next->howManyMoves),&(next->lostturn),&tempint);

      for (j = 0; j < tempint; j++)
       {
        fscanf(f,"%c",&tempchar);

        fscanf(f,"%c %i",&tempchar,&tempint2);

        t = KallocTile(tempchar,tempint2);
        KheadEnqueue(t,next->rack);
       }

     }

    fscanf(f,"%s",buffer);
    fscanf(f,"%i",&tempint);

    KdestroyWholeList(Bag);
    Bag = KcreateGroup(BAG);

    for (j = 0; j < tempint; j++)
     {
      fscanf(f,"%c",&tempchar);
      fscanf(f,"%c %i",&tempchar,&tempint2);
      t = KallocTile(tempchar,tempint2);
      KheadEnqueue(t,Bag);
     }

    fscanf(f,"%s",buffer);
    fscanf(f,"%c",&tempchar);
    for (i = 0; i < LENGTH; i++)
     {
      for (j = 0; j < LENGTH; j++)
       {
        fscanf(f,"%c",&tempchar);
        tempboard[i][j] = tempchar;
       }
      fscanf(f,"%c",&tempchar);
     }

    KsetNewBoard(tempboard);
	KsetLastMove(saveGameLastMovePtr);

    pthread_mutex_unlock(&p4Lock);
    pthread_mutex_unlock(&p3Lock);
    pthread_mutex_unlock(&p2Lock);
    pthread_mutex_unlock(&p1Lock);
    pthread_mutex_unlock(&bagLock);

    fclose(f);
    return(OK);
   }
  else
    return(NOTCURRENTPLAYER);
 }


