#include "internal.h"
#include <strings.h>

/*-----------------------*/
/* Global Variables      */
/*-----------------------*/

int             sd;
KPlayer         *currentPlayer;
KPlayer         *player1;
KPlayer         *player2;
KPlayer         *player3;
KPlayer         *player4;
KPlayer         *host=NULL;
KTileList       *Bag;

KWordList       *newWords;

pthread_mutex_t p1Lock;
pthread_mutex_t p2Lock;
pthread_mutex_t p3Lock;
pthread_mutex_t p4Lock;
pthread_mutex_t bagLock;
pthread_mutex_t currentPlayerLock;
pthread_mutex_t canChallengeLock;
pthread_mutex_t numberOfAPLock;
pthread_mutex_t numberOfPlayersLock;

pthread_mutex_t hostLock;

pthread_t       getPlayersTid;
pthread_t       timerTid;
int             numberOfPlayers=0;
int             gameStarted=0;
int             gameFinished=0;
int             canChallenge=0;
int             numberOfAP=0;
int             lastPersonNotBMove=0;
int             port_to_pci;
int             gameRestored = 0;
int             noMoreGame = 0;

sem_t           finishedGettingPlayers;
sem_t           finishedCurrentPlayerMove;
sem_t           waitForChallenge;


void
ReinitGame()
 {
  KPlayer *p;
  int     i;
  pthread_mutex_t *lock;


  gameStarted = 0;
  gameFinished = 0;
  canChallenge = 0;
  numberOfAP = 0;
  lastPersonNotBMove = 0;
  numberOfPlayers = 0;
  gameRestored = 0;
  noMoreGame = 0;
  host = NULL;
 

  pthread_mutex_init(&bagLock,NULL);
  pthread_mutex_init(&currentPlayerLock,NULL);
  pthread_mutex_init(&canChallengeLock,NULL);
  pthread_mutex_init(&numberOfAPLock,NULL);
  pthread_mutex_init(&numberOfPlayersLock,NULL);
  pthread_mutex_init(&hostLock,NULL);
  sem_init(&finishedGettingPlayers,0,0);

  KdestroyWholeList(Bag);
  
  for (i = 0; i < MAXPLAYERS; i++)
   {
    if (i == 0)
      p = player1;
    else if (i == 1)
      p = player2;
    else if (i == 2)
      p = player3;
    else if (i == 3)
      p = player3;
	  
    lock = KgetLockForPlayer(p);

    if (lock == NULL)
      continue;
	  
    pthread_mutex_lock(lock);
	
    p->score = 0;
    p->howManyMoves = 0;
    p->alive = ACTIVE;
    p->lostturn = 0;
	
    KdestroyWholeList(p->rack);
    p->rack = KcreateGroup(RACK);

    if (p->type == HUMAN)
      numberOfPlayers++;

    pthread_mutex_unlock(lock);

   }

  pthread_mutex_init(&p1Lock,NULL);
  pthread_mutex_init(&p2Lock,NULL);
  pthread_mutex_init(&p3Lock,NULL);
  pthread_mutex_init(&p4Lock,NULL);

 }  

/*------------------------------------------------------------------------------------*/

void *
KreadInput(void *a)
 {
  char buffer[500];

  while (1)
   {
    scanf("%s",buffer);
    if (strcmp(buffer,"x") == 0)
      exit(1);
   }
 }

/*------------------------------------------------------------------------------------*/

void *
Ktimer()
 {
  /* Allow other threads to kill this one */
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

  sleep(10); 

  /* No challenges have been successfull yet */
  if (canChallenge == 1)
    sem_post(&waitForChallenge);	

  return(NULL); 
 }

/*------------------------------------------------------------------------------------*/

void
KaddAPPlayers()
 {
  int i=0;


  while ((i < numberOfAP) && (i < MAXPLAYERS))
   {
    i++;

    if (numberOfPlayers == 0)
     {
      player1 = KallocPlayer();

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      KsetPlayerStatus(player1,ACTIVE);
      KsetPlayerType(player1,AUTOMATED);
	  
      player1 -> playerID = -1;


     }
    else if (numberOfPlayers == 1)
     {
      player2 = KallocPlayer();

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      KsetPlayerStatus(player2,ACTIVE);
      KsetPlayerType(player2,AUTOMATED);
      player2 -> playerID = -1;
     }
    else if (numberOfPlayers == 2)
     {
      player3 = KallocPlayer();

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      KsetPlayerStatus(player3,ACTIVE);
      KsetPlayerType(player3,AUTOMATED);
      player3 -> playerID = -1;	  
     }
    else if (numberOfPlayers == 3)
     {
      player4 = KallocPlayer();

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      KsetPlayerStatus(player4,ACTIVE);
      KsetPlayerType(player4,AUTOMATED);
	  
      player4 -> playerID = -1;	  
     }
   }

 }

/*-------------------------------------------------------------------------------*/

void *
KgetPlayers()
 {
  /* Loop to accept new connections */
  while (numberOfPlayers < MAXPLAYERS)
   {
    if (gameStarted == 1)
     {
      break;
     }

    else if (numberOfPlayers == 0)
     {
      player1 = KallocPlayer();

      /* Accept a new connectiona and return the new socket */
      if ((player1->playerID = accept(sd, (struct sockaddr *)&(player1->client), &(player1->client_len))) == -1)
       {
        perror("Can't accept client\n");
        exit(1);
       }
    
      if (gameStarted == 1)
       {
        close(player1->playerID);
        KdeallocPlayer(player1);
        player1 = NULL;
        break;
       }

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      pthread_create(&(player1->tid),NULL,KplayerThread,(void *)player1);

      KsetPlayerStatus(player1,ACTIVE);
      KsetPlayerType(player1,HUMAN);


     }
    else if (numberOfPlayers == 1)
     {
      player2 = KallocPlayer();

      /* Accept a new connectiona and return the new socket */
      if ((player2->playerID = accept(sd, (struct sockaddr *)&(player2->client), &(player2->client_len))) == -1)
       {
        perror("Can't accept client\n");
        exit(1);
       }

      if (gameStarted == 1)
       {
        close(player2->playerID);
        KdeallocPlayer(player2);
        player2 = NULL;
        break;
       }

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);


      pthread_create(&(player2->tid),NULL,KplayerThread,(void *)player2);

      KsetPlayerStatus(player2,ACTIVE);
      KsetPlayerType(player2,HUMAN);


     }
    else if (numberOfPlayers == 2)
     {
      player3 = KallocPlayer();

      /* Accept a new connectiona and return the new socket */
      if ((player3->playerID = accept(sd, (struct sockaddr *)&(player3->client), &(player3->client_len))) == -1)
       {
        perror("Can't accept client\n");
        exit(1);
       }

      if (gameStarted == 1)
       {
        close(player3->playerID);
        KdeallocPlayer(player3);
        player3 = NULL;
        break;
       }

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      pthread_create(&(player3->tid),NULL,KplayerThread,(void *)player3);

      KsetPlayerStatus(player3,ACTIVE);
      KsetPlayerType(player3,HUMAN);


     }
    else if (numberOfPlayers == 3)
     {
      player4 = KallocPlayer();

      /* Accept a new connectiona and return the new socket */
      if ((player4->playerID = accept(sd, (struct sockaddr *)&(player4->client), &(player4->client_len))) == -1)
       {
        perror("Can't accept client\n");
        exit(1);
       }

      if (gameStarted == 1)
       {
        close(player4->playerID);
        KdeallocPlayer(player4);
        player4 = NULL;
        break;
       }

      pthread_mutex_lock(&numberOfPlayersLock);
      numberOfPlayers++;
      pthread_mutex_unlock(&numberOfPlayersLock);

      pthread_create(&(player4->tid),NULL,KplayerThread,(void *)player4);

      KsetPlayerStatus(player4,ACTIVE);
      KsetPlayerType(player4,HUMAN);


     }
   }
   
  return(NULL); 
/*  if (gameStarted == 0)
    sem_post(&finishedGettingPlayers);*/
 }

/*--------------------------------------------------------------------------*/

int
KstartGame()
 {
  int i;


  KPlayer *first;
  KPlayer *p1;
  KTile   *t;

  int    packet_size;
  double temp;
  int    temp2;
  int    n;
  int    totalremove=0;

  packet_size = sizeof(GeneralPacket);
  sem_init(&finishedCurrentPlayerMove,0,0);
  sem_init(&waitForChallenge,0,0);

  /*numberOfAP = MAXPLAYERS - numberOfPlayers;*/
  
  KinitializeBag();

  KaddAPPlayers();

  pthread_mutex_lock(&currentPlayerLock);

  if (KgetPlayerStatus(player1) == ACTIVE)
    currentPlayer = player1;
  else if (KgetPlayerStatus(player2) == ACTIVE)
    currentPlayer = player2;
  else if (KgetPlayerStatus(player3) == ACTIVE)
    currentPlayer = player3;
  else if (KgetPlayerStatus(player4) == ACTIVE)
    currentPlayer = player4;

  pthread_mutex_unlock(&currentPlayerLock);

  /* Find a random player to start */
  temp = drand48();                /* Generates 0.0 to 1.0 */
  n = temp*(numberOfPlayers);      /* Generates 0 to length of bag */
  if (n == numberOfPlayers)
    n = numberOfPlayers-1;


  if (n == 0)
    first = player1;
  else if (n == 1)
    first = player2;
  else if (n == 2)
    first = player3;
  else if (n == 3)
    first = player4;
 
  pthread_mutex_lock(&currentPlayerLock); 
  currentPlayer = first;
  pthread_mutex_unlock(&currentPlayerLock);

  KsendGameStarted(host);
  
  /* Players Fill Up their rack */
  for (i = 0; i < numberOfPlayers; i++)
   {
    KfillUpRack(currentPlayer);
    currentPlayer = KgetNextPlayer(currentPlayer);
   }

  pthread_mutex_lock(&currentPlayerLock); 
  currentPlayer = first;
  pthread_mutex_unlock(&currentPlayerLock);

  /*----------------------------------------------------------*/
  /* MAIN LOOP OF GAME                                        */
  /*----------------------------------------------------------*/

  while(1)
   {

    if (KgetPlayerType(currentPlayer) == AUTOMATED)
     {
      /*printf("Calling AP\n");*/
      KcallAP(currentPlayer);
      KincrementMovesPlayer(currentPlayer);
      KfillUpRack(currentPlayer);
	  
	  /* Game is finished if someone does not have any tiles*/
      if (currentPlayer->rack->length == 0)
       {
        gameFinished = 1;
       }
	  
     }
    else if (KgetPlayerLostTurn(currentPlayer) == 0)
     {
      KsendYourTurn(currentPlayer);
      sem_wait(&finishedCurrentPlayerMove);
	  
	  if (noMoreGame == 1)
	   {
	    return(0);
	   }	

      if (gameRestored == 1)
       {
	    lastPersonNotBMove = 0;
        gameRestored = 0;
        continue;
       }

      /* If the move was not a resign, send the move then ask for challenge */
      if (lastPersonNotBMove == 0)
       {
        KsendPlayerMove(currentPlayer);

        if ((numberOfPlayers - numberOfAP) > 1)
         {
          pthread_mutex_lock(&canChallengeLock);  
          canChallenge=1; 
          pthread_mutex_unlock(&canChallengeLock);  

          KsendChallengeRequest(currentPlayer);

          pthread_create(&timerTid,NULL,Ktimer,NULL);
		
          sem_wait(&waitForChallenge);
          pthread_cancel(timerTid);		

          pthread_mutex_lock(&canChallengeLock);
          canChallenge=0;
          pthread_mutex_unlock(&canChallengeLock);
         }

        KincrementMovesPlayer(currentPlayer);
		
		

        KfillUpRack(currentPlayer);


        /* Game is finished if someone does not have any tiles*/
        if (currentPlayer->rack->length == 0)
         {
          gameFinished = 1;
         }
       }
      else
       {
        lastPersonNotBMove = 0;
        if (KgetPlayerStatus(currentPlayer) != RESIGNED)
         {
          KincrementMovesPlayer(currentPlayer);
          KfillUpRack(currentPlayer);
		  
 		  /* Game is finished if someone does not have any tiles*/
          if (currentPlayer->rack->length == 0)
           {
            gameFinished = 1;
           }

		  
         }
       }
     }
    else
     {
      temp2 = KgetPlayerLostTurn(currentPlayer);
      KsetPlayerLostTurn(currentPlayer,temp2-1);
     }
  
    

    /* Check to see if game is finished because of all tiles gone or if last person resigned */
    pthread_mutex_lock(&numberOfPlayersLock);
    if ((gameFinished == 1) || (numberOfPlayers <= 1))
     {
      pthread_mutex_unlock(&numberOfPlayersLock);
      break;
     }

    currentPlayer = KgetNextPlayer(currentPlayer);
    pthread_mutex_unlock(&numberOfPlayersLock); 
   }

  /* Update the scores */
  p1 = KgetNextPlayer(currentPlayer);
  for (i = 0; i < numberOfPlayers-1; i++)
   {
    while (p1->rack->length > 0)
     {
      t = KdequeueHead(p1->rack);
      p1->score = p1->score - t->score;
      totalremove = totalremove + t->score;
     }
    p1 = KgetNextPlayer(p1);
   }
 
  currentPlayer->score = currentPlayer->score + totalremove;

  KsendGameFinished();
  
  

  return(0);
 }


/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

int
main(int argc, char *argv[])
 {
  int                  port;
  int                  on=1;
  struct sockaddr_in   server;
  int                  readInputTid;
  char                 *pname;

  pname = argv[0];
/*  if (argc != 3)
   {
    printf("Usage %s <port 1> <port 2>\n", pname);
    exit(1);
   }*/

  printf("e-Scrabble server has started\n");
  printf("Press  x <enter> to end the program\n");
  system("rm savegame");

  pthread_create(&readInputTid,NULL,KreadInput,NULL);

  pthread_mutex_init(&p1Lock,NULL);
  pthread_mutex_init(&p2Lock,NULL);
  pthread_mutex_init(&p3Lock,NULL);
  pthread_mutex_init(&p4Lock,NULL);
  pthread_mutex_init(&bagLock,NULL);
  pthread_mutex_init(&currentPlayerLock,NULL);
  pthread_mutex_init(&canChallengeLock,NULL);
  pthread_mutex_init(&numberOfAPLock,NULL);
  pthread_mutex_init(&numberOfPlayersLock,NULL);
  pthread_mutex_init(&hostLock,NULL);
  sem_init(&finishedGettingPlayers,0,0);

  /* Initialize variables */
/*  port = atoi(argv[1]);
  port_to_pci = atoi(argv[2]);*/
  port = 5432;
  port_to_pci = 7890;

  /* Create a socket to accept connections on */
  if ((sd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
   {
    perror("Can't create a socket\n");
    exit(1);
   }

  /* Initialize server info */
  bzero((char *)&server,sizeof(struct sockaddr_in));
  server.sin_family = AF_INET;
  server.sin_port = htons(port);  
  server.sin_addr.s_addr = htonl(INADDR_ANY);

  setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 

  /* Bind the port to accept connections on */
  if (bind(sd,(struct sockaddr *)&server,sizeof(server)) == -1)
   {
    perror("Can't bind\n");
    exit(1);
   }

  /* Listen to the port for up to MAXPLAYERS connections */
  listen(sd,MAXPLAYERS);

  KinitialiseAP();

  while(1)
   {
    KInitializeBoard();

    pthread_create(&getPlayersTid,NULL,KgetPlayers,NULL);

    sem_wait(&finishedGettingPlayers);
  
    gameStarted = 1;

    pthread_cancel(getPlayersTid);

    KstartGame();
	
    ReinitGame();
   }	

  if ((player1 != NULL) && (KgetPlayerStatus(player1) != NOTEXIST))
   {
    pthread_cancel(player1->tid);
    close(player1->playerID);
   }

  if ((player2 != NULL) && (KgetPlayerStatus(player2) != NOTEXIST))
   {
    pthread_cancel(player2->tid);
    close(player2->playerID);
   }

  if ((player3 != NULL) && (KgetPlayerStatus(player3) != NOTEXIST))
   {
    pthread_cancel(player3->tid);
    close(player3->playerID);
   }

  if ((player4 != NULL) && (KgetPlayerStatus(player4) != NOTEXIST))
   {
    pthread_cancel(player4->tid);
    close(player4->playerID);
   }

  close(sd);
  exit(0);
 }



