#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 sem_t           finishedGettingPlayers;
extern sem_t           finishedCurrentPlayerMove;
extern sem_t           waitForChallenge;


void
KprintPlayer(KPlayer *p)
 {
  printf("playerID: %i\n",p->playerID);
  printf("tid:      %i\n",p->tid);
  printf("type:     %i\n",p->type);
  printf("alive:    %i\n",p->alive);
  printf("score:    %i\n",p->score);
  printf("moves:    %i\n",p->howManyMoves);
  printf("lostturns %i\n",p->lostturn);
  printf("Rack:\n");
  KprintList(p->rack);
  return;
 }

/*----------------------------------------------------*/

KPlayer *
KallocPlayer()
 {
  /* Builds a new KPlayer
   * Pre:  
   * Post: Returns a pointer to the new player */

  KPlayer *newPlayer;  /* A new node */

  /* Allocate space for the new node */
  newPlayer = (KPlayer *)malloc(sizeof(KPlayer));
  if (newPlayer == NULL)
    printf("Error in allocPlayer");
  else
   {
    newPlayer->playerID = NOTEXIST;
    newPlayer->type     = NOTEXIST;
    newPlayer->alive    = NOTEXIST;
    newPlayer->score    = 0;
    newPlayer->howManyMoves = 0;
    newPlayer->lostturn = 0;
    newPlayer->rack     = KcreateGroup(RACK);
   }

  return(newPlayer);
 }


/*--------------------------------------------------------------*/

int
KdeallocPlayer(KPlayer *p)
 {
  /* Frees up the memory taken by a player
   * Pre: *p must be defined and must be resigned
   * Post: Returns 1 if success, or 0 if *p points to nothing or if the player is alive*/

  pthread_mutex_t *lock;

  if (p == NULL)
    return(MISCERROR);

  lock = KgetLockForPlayer(p);
  if (lock == NULL)
    return(MISCERROR);
  pthread_mutex_lock(lock);

  if (KgetPlayerStatus(p) == ACTIVE)
    return(MISCERROR);

  /* Free the memory */
  free(p);
  p = NULL;

  pthread_mutex_unlock(lock);

  return(OK);
 }

/*--------------------------------------------------------------*/

KScore
KgetPlayerScore(KPlayer *p)
 {
  /* Returns the score of a KPlayer
   * Pre: p must be defined
   * Post: Returns score of player if player exists, otherwise returns MISCERROR */

  pthread_mutex_t *lock;
  KScore temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);
  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
    temp = p -> score;
  else
    temp = MISCERROR;

  pthread_mutex_unlock(lock);
  return(temp);
 }

/*--------------------------------------------------------------*/

int
KaddPlayerScore(KPlayer *p, KScore score)
 {
  /* Adds score to player p's score
   * Pre: p must be defined, and score must be positive.
   * Post: score is added to the players score and OK is returned, on error MISCERROR is returned */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST) && (score >= 0))
   {
    p->score = p->score + score;
    pthread_mutex_unlock(lock);
    return(OK);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }
 }

/*--------------------------------------------------------------*/

int
KsubPlayerScore(KPlayer *p, KScore score)
 {
  /* Subtracts score to player p's score
   * Pre: p must be defined, and score must be positive.
   * Post: score is subtracted to the players score and 1 is returned, on error 0 is returned */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(0);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST) && (score > 0))
   {
    p->score = p->score - score;
    if (p->score < 0)
      p->score = 0;
    pthread_mutex_unlock(lock);
    return(1);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(0);
   }
 }

/*--------------------------------------------------------------*/

int
KnumberOfMovesPlayer(KPlayer *p)
 {
  /* Determines the number of moves player p has played
   * Pre: p must be defined
   * Post: the number of moves is returned, if p is undefined MISCERRO will be returned */

  pthread_mutex_t *lock;
  int             temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
    temp = p->howManyMoves;
  else 
    temp = MISCERROR;
  pthread_mutex_unlock(lock);
  return(temp);
 
 }

/*--------------------------------------------------------------*/

int 
KincrementMovesPlayer(KPlayer *p)
 {
  /* Increments the number of moves player p has played
   * Pre: p must be defined
   * Post: the number of moves player p has played is incremented by 1, OK is returned on success
           MISCERROR is returned on failure */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(-1);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
   {
    (p->howManyMoves)++;
    pthread_mutex_unlock(lock);
    return(OK);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }
 }
 
/*--------------------------------------------------------------*/

KPlayerType
KgetPlayerType(KPlayer *p)
 {
  /* Returns the playertype for p
   * Pre: p must be defined
   * Post: returns the type of p, ie either human, automated or noplayer */

  pthread_mutex_t *lock;
  KPlayerType      temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(NOPLAYER);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
    temp = p->type;
  else
    temp = NOPLAYER;

  pthread_mutex_unlock(lock);
  return(temp);
  
 }

/*--------------------------------------------------------------*/

int
KsetPlayerType(KPlayer *p, KPlayerType type)
 {
  /* Sets the playertype for p as type
   * Pre: p and type must be defined
   * Post: returns OK if successful and MISCERROR if unsuccesful */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
   {
    p->type = type;
    pthread_mutex_unlock(lock);
    return(OK);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }
 }

/*--------------------------------------------------------------*/

KPlayerStatus
KgetPlayerStatus(KPlayer *p)
 {
  /* Returns the playerstatus for p
   * Pre: p must be defined
   * Post: returns the status of p */

  pthread_mutex_t *lock;
  KPlayerStatus    temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(NOTEXIST);

  pthread_mutex_lock(lock);

  if (p != NULL)
    temp = p->alive;
  else
    temp = NOTEXIST;

  pthread_mutex_unlock(lock);
  return(temp);
 }

/*--------------------------------------------------------------*/

int
KsetPlayerStatus(KPlayer *p, KPlayerStatus status)
 {
  /* Sets the playerstatus for p as type
   * Pre: p and status must be defined
   * Post: returns OK if successful and MISCERRIR if unsuccesful */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  if (p != NULL)
   {
    p->alive = status;
    pthread_mutex_unlock(lock);
    return(OK);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }
 }

/*--------------------------------------------------------------*/

int
KgetPlayerLostTurn(KPlayer *p)
 {
  /* Returns if the player lost a turn due to a challenge or not
   * Pre: p must be defined
   * Post: Returns 1 if they lost their turn, and 0 if not
           Retunrs MISCERROR on failure */

  pthread_mutex_t *lock;
  KScore temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(0);
  pthread_mutex_lock(lock);

  if ((p != NULL) && (p->alive != NOTEXIST))
    temp = p -> lostturn;
  else
    temp = MISCERROR;

  pthread_mutex_unlock(lock);
  return(temp);
 }

/*--------------------------------------------------------------*/

int
KsetPlayerLostTurn(KPlayer *p, int lostTurn)
 {
  /* Sets the players lostturn value
   * Pre: p and lostTurn must be defined
   * Post: returns OK if successful and MISCERROR if unsuccesful */

  pthread_mutex_t *lock;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(MISCERROR);

  pthread_mutex_lock(lock);

  if (p != NULL)
   {
    p->lostturn = lostTurn;
    pthread_mutex_unlock(lock);
    return(OK);
   }
  else
   {
    pthread_mutex_unlock(lock);
    return(MISCERROR);
   }
 }

/*--------------------------------------------------------------*/


int
KgetPlayerID(KPlayer *p)
 {
  /* Returns the playerID for p
   * Pre: p must be defined
   * Post: returns the ID of p or -1 if p does not exist*/

  pthread_mutex_t *lock;
  int    temp;

  lock = KgetLockForPlayer(p);

  if (lock == NULL)
    return(-1);

  if ((p != NULL) && (p->alive != NOTEXIST))
    temp = p->playerID;
  else
    temp = -1;

  pthread_mutex_unlock(lock);
  return(temp);
 }

int
KswapPlayers(KPlayer *p1, KPlayer *p2)
 {
  KPlayer *temp;
  pthread_mutex_t *lock1;
  pthread_mutex_t *lock2;

  lock1 = KgetLockForPlayer(p1);
  lock2 = KgetLockForPlayer(p2);

  if ((lock1 == NULL) || (lock2 == NULL))
     return(0);

  pthread_mutex_lock(lock1);
  pthread_mutex_lock(lock2);

  temp = KallocPlayer();
  
  temp->playerID   = p1->playerID;
  temp->tid        = p1->tid;
  temp->type       = p1->type;
  temp->alive      = p1->alive;
  temp->score      = p1->score;
  temp->client_len = p1->client_len;  
  temp->rack       = p1->rack;
  temp->howManyMoves = p1->howManyMoves;
  bcopy(&(p1->client),&(temp->client),sizeof(struct sockaddr_in));


  p1->playerID   = p2->playerID;
  p1->tid        = p2->tid;
  p1->type       = p2->type;
  p1->alive      = p2->alive;
  p1->score      = p2->score;
  p1->client_len = p2->client_len;
  p1->rack       = p2->rack;
  p1->howManyMoves = p2->howManyMoves;
  bcopy(&(p2->client),&(p1->client),sizeof(struct sockaddr_in));

  p2->playerID   = temp->playerID;
  p2->tid        = temp->tid;
  p2->type       = temp->type;
  p2->alive      = temp->alive;
  p2->score      = temp->score;
  p2->client_len = temp->client_len;
  p2->rack       = temp->rack;
  p2->howManyMoves = temp->howManyMoves;
  bcopy(&(temp->client),&(p2->client),sizeof(struct sockaddr_in));

  KdeallocPlayer(temp);

  pthread_mutex_unlock(lock2);
  pthread_mutex_unlock(lock1);
  return(1);
 }

