#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_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 sem_t           finishedGettingPlayers;
extern sem_t           finishedCurrentPlayerMove;
extern sem_t           waitForChallenge;


int score[27];

/*-------------------------------------------------------*/

pthread_mutex_t *
KgetLockForPlayer(KPlayer *p)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* The function will return a pointer to the lock for the player p
   * Pre: p must be defined and must be in the set of player1 to player 4
   * Post: The lock for the player will be returned on success
           On failure NULL will be returned */

  /* Find the player and return the lock for him */
  if (p == NULL)
    return(NULL);
  
  if (p == player1)
    return(&p1Lock);
  else if (p == player2)
    return(&p2Lock);
  else if (p == player3)
    return(&p3Lock);
  else if (p == player4)
    return(&p4Lock);
  else
    return(NULL);
 }

/*-------------------------------------------------------*/

KPlayer *
KgetNextPlayer(KPlayer *p)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Given the currentplayer p, returns the next player to play
   * Pre: p must be defined and must be in the set player1 - player4
   * Post: a pointer to the next player is returned */

  KPlayer *next=NULL;
  pthread_mutex_lock(&currentPlayerLock);

  /* If the player is player1 go through player2,player3... and get next active player */
  if (p == player1)
   {
    if ((player2 != NULL) && (KgetPlayerStatus(player2) == ACTIVE))
      next = player2;
    else if ((player3 != NULL) && (KgetPlayerStatus(player3) == ACTIVE))
      next = player3;
    else if ((player4 != NULL) && (KgetPlayerStatus(player4) == ACTIVE))
      next = player4;
    else
      next = NULL;
   }

  /* If the player is player2 go through player3,player4... and get next active player */
  else if (p == player2)
   {
    if ((player3 != NULL) && (KgetPlayerStatus(player3) == ACTIVE))
      next = player3;
    else if ((player4 != NULL) && (KgetPlayerStatus(player4) == ACTIVE))
      next = player4;
    else if ((player1 != NULL) && (KgetPlayerStatus(player1) == ACTIVE))
      next = player1;
    else
      next = NULL;
   }

  /* If the player is player3 go through player4,player1... and get next active player */
  else if (p == player3)
   {
    if ((player4 != NULL) && (KgetPlayerStatus(player4) == ACTIVE))
      next = player4;
    else if ((player1 != NULL) && (KgetPlayerStatus(player1) == ACTIVE))
      next = player1;
    else if ((player2 != NULL) && (KgetPlayerStatus(player2) == ACTIVE))
      next = player2;
    else
      next = NULL;
   }

  /* If the player is player4 go through player1,player2... and get next active player */
  else if (p == player4)
   {
    if ((player1 != NULL) && (KgetPlayerStatus(player1) == ACTIVE))
      next = player1;
    else if ((player2 != NULL) && (KgetPlayerStatus(player2) == ACTIVE))
      next = player2;
    else if ((player3 != NULL) && (KgetPlayerStatus(player3) == ACTIVE))
      next = player3;
    else
      next = NULL;
   }
  else
    next = NULL;

  pthread_mutex_unlock(&currentPlayerLock);

  return(next);
 }
  
  
/*-------------------------------------------------------*/  
  
KPlayer *
KgetNextPlayerWithOutLock(KPlayer *p)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Given the currentplayer p, returns the next player to play
   * Pre: p must be defined and must be in the set player1 - player4
   * Post: a pointer to the next player is returned */

  KPlayer *next=NULL;


  /* If the player is player1 go through player2,player3... and get next active player */
  if (p == player1)
   {
    if ((player2 != NULL) && (player2->alive == ACTIVE))
      next = player2;
    else if ((player3 != NULL) && (player3->alive == ACTIVE))
      next = player3;
    else if ((player4 != NULL) && (player4->alive == ACTIVE))
      next = player4;
    else
      next = NULL;
   }

  /* If the player is player2 go through player3,player4... and get next active player */
  else if (p == player2)
   {
    if ((player3 != NULL) && (player3->alive == ACTIVE))
      next = player3;
    else if ((player4 != NULL) && (player4->alive == ACTIVE))
      next = player4;
    else if ((player1 != NULL) && (player1->alive == ACTIVE))
      next = player1;
    else
      next = NULL;
   }

  /* If the player is player3 go through player4,player1... and get next active player */
  else if (p == player3)
   {
    if ((player4 != NULL) && (player4->alive == ACTIVE))
      next = player4;
    else if ((player1 != NULL) && (player1->alive == ACTIVE))
      next = player1;
    else if ((player2 != NULL) && (player2->alive == ACTIVE))
      next = player2;
    else
      next = NULL;
   }

  /* If the player is player4 go through player1,player2... and get next active player */
  else if (p == player4)
   {
    if ((player1 != NULL) && (player1->alive == ACTIVE))
      next = player1;
    else if ((player2 != NULL) && (player2->alive == ACTIVE))
      next = player2;
    else if ((player3 != NULL) && (player3->alive == ACTIVE))
      next = player3;
    else
      next = NULL;
   }
  else
    next = NULL;

  return(next);
 }
    
  
/*------------------------------------------------------------------------------------*/

void
KprintLetter(int n)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Given a number from 0 to 26 it the function will print the srabble letter associtated
     with it.
   * Pre: n must be defined and in the range 0 to 26 inclusive
   * Post: the letter is printed out */

  if ((n < 0) || (n > 26))
    printf("invalid\n");
   else if (n == 26)
    printf("?\n");
  else
    printf("%c",(char)(97+n));
 }

/*-----------------------------------------------------------------*/

int
KplaceLetterInBag(KTile *t)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Randomly places tile t in Bag
   * Pre: t must be defined (not NULL)
   * Pos: t is randomly inserted into bag, and OK is returned.  If error occurs MISCERROR is returned.*/

  double temp;
  int    n;

  /* Check for bad inputs */
  if (t == NULL)
    return(MISCERROR);

  pthread_mutex_lock(&bagLock);

  /* 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);

  pthread_mutex_unlock(&bagLock);

  return(OK);
 }

/*-----------------------------------------------------------------*/


int
KfillUpRack(KPlayer *p)
 {
  /* Debugged by Navid Azizi on October 1st */

  /* Given a player p, will fill up their rack to 7 tiles.
   * Pre: p must be defined and in the set player1-player4
   * Post: The player will pick up tiles to get to 7 tiles and OK returned on success
           On failure MISCERROR will be returned */

  pthread_mutex_t *lock;
  KTile            *t;
  TrayPacket      packet;
  int i=0;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(&bagLock);
  pthread_mutex_lock(lock);

  packet.type = NEWTILES;

  /* Take a tile out of the bag and place in rack */
  while ((p->rack->length < 7) && (Bag->length > 0))
   {
    t = KdequeueHead(Bag);
    packet.letters[i] = t->letter;
    packet.scores[i] = t->score;
    KheadEnqueue(t,p->rack);
    i++;
   }
  packet.numberOfTiles = i;


 
  if (p->type == HUMAN)
   {

    if (write(p->playerIDsend,&packet,sizeof(GeneralPacket)) < 0)
     {
      perror("Write failed\n");
      exit(1);
     }

   }

  pthread_mutex_unlock(lock);
  pthread_mutex_unlock(&bagLock);
  return(OK);
 }

/*-----------------------------------------------------------------*/

int
KplayerDropsLetter(KPlayer *p, KTile *t)
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* This funtion will drop the tile t from the players rack into the bag
   * Pre: t and p must be defined, and t must be in the players rack
   * Post: t will be in the Bag and OK will be returned on success.
           On failure MISCERROR will be returned */


  pthread_mutex_t *lock;
  int n;
  double temp;

  /* Lock the player */
  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);
 
  /* Check that t is in the player's rack */
  if (t->inlist != p->rack)
    return(INVALID);

  pthread_mutex_lock(&bagLock);
  pthread_mutex_lock(lock);

  /* Take the tile out of players rack */
  KdequeueTile(t);

  /* 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);

  pthread_mutex_unlock(lock);
  pthread_mutex_unlock(&bagLock);
  return(OK);
 }

/*-----------------------------------------------------------------*/

int
KplayerPickLetter(KPlayer *p)
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* This funtion will take one tile from the bag and put on the players rack
   * Pre: p must be defined and in set player1 - player 4
   * Post: player p will have an extra tile and OK will be returned on success.
           On failure MISCERROR will be returned */

  KTile *t;
  pthread_mutex_t *lock;


  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(&bagLock);
  pthread_mutex_lock(lock);

  /* Take tile out of bag */
  t = KdequeueHead(Bag);

  /* Put tile in players rack */
  KheadEnqueue(t, p->rack);

  pthread_mutex_unlock(lock);
  pthread_mutex_unlock(&bagLock);
  return(OK);
 }

/*-----------------------------------------------------------------*/

char
KgetLetter(int n)
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* Returns a character depending on the number n which is one of the tiles available
   * Pre: n must be between 0 and 100
   * Post: a character is returned. */

  if ((n < 0) || (n >= 100))
    return(NULL);
  if (n < 9)
    return('a');
  if (n < 11)
    return('b');
  if (n < 13)
    return('c');
  if (n < 17)
    return('d');
  if (n < 29)
    return('e');
  if (n < 31)
    return('f');
  if (n < 34)
    return('g');
  if (n < 36)
    return('h');
  if (n < 45)
    return('i');
  if (n < 46)
    return('j');
  if (n < 47)
    return('k');
  if (n < 51)
    return('l');
  if (n < 53)
    return('m');
  if (n < 59)
    return('n');
  if (n < 67)
    return('o');
  if (n < 69)
    return('p');
  if (n < 70)
    return('q');
  if (n < 76)
    return('r');
  if (n < 80)
    return('s');
  if (n < 86)
    return('t');
  if (n < 90)
    return('u');
  if (n < 92)
    return('v');
  if (n < 94)
    return('w');
  if (n < 95)
    return('x');
  if (n < 97)
    return('y');
  if (n < 98)
    return('z');
  if (n < 100)
    return('?');
  return(NULL);	
 }

/*-----------------------------------------------------------------*/

void
KinitializeBag()
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* The bag is initialized with all the tiles in a random order
   * Pre: The global variable Bag must be defined
   * Post: The global variable Bag had a random ordering of tiles inside it*/

  struct timeval time;
  int numbers[100];
  double temp;
  int togo=100;
  int n;
  int i;
  KTile *t;
  char letter;

  /* Assign scores to each letter */
  score[0] = 1;   /* A */
  score[1] = 3;   /* B */
  score[2] = 3;   /* C */
  score[3] = 2;   /* D */
  score[4] = 1;   /* E */
  score[5] = 4;   /* F */
  score[6] = 2;   /* G */
  score[7] = 4;   /* H */
  score[8] = 1;   /* I */
  score[9] = 8;   /* J */
  score[10] = 5;  /* K */
  score[11] = 1;  /* L */
  score[12] = 3;  /* M */
  score[13] = 1;  /* N */
  score[14] = 1;  /* O */
  score[15] = 3;  /* P */
  score[16] = 10; /* Q */
  score[17] = 1;  /* R */
  score[18] = 1;  /* S */
  score[19] = 1;  /* T */
  score[20] = 1;  /* U */
  score[21] = 4;  /* V */
  score[22] = 4;  /* W */
  score[23] = 8;  /* X */
  score[24] = 4;  /* Y */
  score[25] = 10; /* Z */
  score[26] = 0;  /* blank */

  /* Initialize 100 tiles */
  for (i = 0; i < 100; i++)
    numbers[i] = 1;

  pthread_mutex_lock(&bagLock);

  /* Create Bag structure */
  Bag = KcreateGroup(BAG);
  if (Bag == NULL)
   {
    perror("Out of Memory\n");
    exit(1);
   }

  /* Get time of day to randomly seed the random number generator */
  gettimeofday(&time,NULL);
  srand48(time.tv_sec);

  /* Keep adding tiles randomly to the bag until all tiles are added */
  while (togo > 0)
   {
    /* Pick a letter, and be sure that it has not been added to bag already */
    while(1)
     {
      temp = drand48();  /* Generates 0.0 to 1.0 */

      n = temp*100;      /* Generates 0 to 99 */

      if (n == 100)
        n = 99;

      if (numbers[n] > 0)
        break;
     }
  

    /* Remember that the tile has been picked */
    (numbers[n])--;

    /* Find the associated letter for the tile */
    letter = KgetLetter(n);

    /* Make the tile */
    if (letter != '?')
      t = KallocTile(letter, score[(int)(letter-97)]);
    else
      t = KallocTile('?', 0);
 
    if (t == NULL)
     {
      perror("Out of Memory\n");
      exit(1);
     }

    /* Add the tile to the Bag */      
    KheadEnqueue(t,Bag);
    togo--;
   }

  pthread_mutex_unlock(&bagLock);

 }
    
/*-----------------------------------------------------------------*/

int
KBagSize()
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* Returns the number of tiles in the bag
   * Pre: 
   * Post: The number of tiles in the bag is returned */


  pthread_mutex_lock(&bagLock);
  return(Bag->length);
  pthread_mutex_unlock(&bagLock);
 } 

/*-----------------------------------------------------------------*/

KScore
KCurrentScore(int pid)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Returns the score for the player with ID pid if they are  the currentplayer 
   * Pre: pid must be defined
   * Post: the score is returned on success, on failure NOTCURRENTPLAYER is returned */

  KScore temp;

  pthread_mutex_lock(&currentPlayerLock);

  if (currentPlayer->playerID == pid)
    temp = currentPlayer->score;
  else
    temp = NOTCURRENTPLAYER;

  pthread_mutex_unlock(&currentPlayerLock);
  return(temp);
 }

/*-----------------------------------------------------------------*/

int
KMove(int pid,KBMove m)
 {
  BMovePacket packet1;

  int temp;

  if (pid == currentPlayer->playerID)
   {
    if (m.type == PLACE)
     {
      packet1.type = BMOVE;
      bcopy(&m,&(packet1.theMove),sizeof(KBMove));
      temp = KboardMove(currentPlayer,&packet1);
      return(temp);
     }
    else if (m.type == SWAP)
     {
      printf("should never be here\n");
      temp = MISCERROR;
      return(temp);
     }
   }
  
  return(NOTCURRENTPLAYER);
 }

/*-----------------------------------------------------------------*/

int
KHowManyMoves()
 {
  /* Debugged by Navid Azizi on September 30th */
  
  /* This function will return the number of moves played in total in the game. 
   * Pre: 
   * Post: The total number of moves will be returned */   

  int temp=0;

  /* Add the players moves if they ever played */
  if (KgetPlayerStatus(player1) != NOTEXIST)
    temp = temp + player1->howManyMoves;
  if (KgetPlayerStatus(player2) != NOTEXIST)
    temp = temp + player2->howManyMoves;
  if (KgetPlayerStatus(player3) != NOTEXIST)
    temp = temp + player3->howManyMoves;
  if (KgetPlayerStatus(player4) != NOTEXIST)
    temp = temp + player4->howManyMoves;
      
  return(temp);
 }

/*-----------------------------------------------------------------*/

KScore
KAverageScore(int pid)
 {
  /* Debugged by Navid Azizi on September 30th */

  /* Returns the average score for the player with ID pid if they are  the currentplayer 
   * Pre: pid must be defined
   * Post: the average score is returned on success, on failure NOTCURRENTPLAYER is returned */

  KScore temp;

  pthread_mutex_lock(&currentPlayerLock);

  if (currentPlayer->playerID == pid)
    temp = currentPlayer->score/currentPlayer->howManyMoves;
  else
    temp = NOTCURRENTPLAYER;

  pthread_mutex_unlock(&currentPlayerLock);
  return(temp);
 }


/*------------------------------------------------------------*/

int
KcallAP(KPlayer *p)
 {
  /* Debugged by Navid Azizi on October 4th */

  /* Calls the AP and gets a move, determines what kind of move and calls appropriate function 
   * Pre: p must be defined
   * Post: OK is returned on success, and MISCERROR is returned on error */

  KBMove *theMove;
  SMovePacket packet1;
  int i;
  KTile *t;


  if ((p == NULL) || (KgetPlayerType(p) != AUTOMATED))
    return(MISCERROR);



  theMove = KAPMove(p->rack);

  if (theMove->type == PLACE)
   {

    KMove(p->playerID,*theMove);
	KsendPlayerMove(currentPlayer);
   }
  else
   {
    /*printf("AP did a swap\n");*/
    packet1.theMove.numMoves = p->rack->length;
    packet1.theMove.type = SWAP;
    t = p->rack->Tiles;
    for (i = 0; i < p->rack->length; i++)
     {
      packet1.theMove.letters[i] = t->letter;
      t = t->link;
     }

    KswapMove(p,&packet1);
   }
  return(OK);
 }

/*-------------------------------------------------*/



