#include "sjmEngine.h"
#include <string.h>

sjmEngine::sjmEngine(sjmParameters startup){
  int count;
  numplayers= startup.getNumPlayers();
  ready = over = 0;
  TheBag = startup.getBag();
  TheBoard = startup.getBoard();
  TheDictionary = startup.getDictionary();
  if(numplayers){
    firstplayer = new sjmPlayer(TILENUM , startup.GenerateCode(0));
    for(curplayer = firstplayer, count = 1; count < numplayers; count++){
      curplayer->next = new sjmPlayer(TILENUM , startup.GenerateCode(count));
      curplayer = curplayer->next;
    }
  }
  curplayer = firstplayer;
}

sjmEngine::sjmEngine(char* filename){
  //coming soon...
}

sjmEngine::~sjmEngine(){
  delete firstplayer;
}


//also modifies the player's tiles; use RevertTiles to get original tiles back

char* sjmEngine::DeterminePlayedTiles(sjmMove* move){
  Coordinate iterator;
  int count;
  char *x, *y, *z;
  char a, b, c=0, ismatch, compare;
  char *used = new char[TILENUM+1];
  unsigned long int temp[TILENUM];
  char original[TILENUM];
  unsigned long int change = (1<<(move->getDirection()<<3)); 
 
  move->getXYZ(iterator.x, iterator.y, iterator.z);
 
  x = ((char*)&iterator);
  y = ((char*)&iterator) + 1;
  z = ((char*)&iterator) + 2;
  
  temp = getPlayer(move->getPlayerID())->getTiles();
  for(count = 0; temp[count]; count++) original[count] = TheBag->Translate(temp[count]);
  original[count] = 0;

  for(a = 0; move->input[a]; a++, iterator.whole += change){
    
    if(TheBoard->TranslateBoardLetter(*x, *y, *z) != move->input[a]){
      
      if(TheBoard->TranslateBoardLetter(*x, *y, *z)){
	used[0] = 0;
	return used;
      }
      
      if(move->input[a]<96) compare = '*';
      else compare = move->input[a];
      
      for(b = 0, ismatch = 1; ismatch && (b < TILENUM); b++){
	if(compare == original[b]){
	  used[c] = compare;
	  original[b] = 0;
	  c++;
	  ismatch = 0;
	}
      }
      
      if(ismatch){
	delete used;
	used=0;
	return(used);
      }
      
    }
  } 
 return used;
}

//returns what score of move would be
int sjmEngine::JudgeMove(sjmMove* move, int& result){
  char* used;
  int works;
  int result = 0;

  Coordinate movepos;
  move->getXYZ(movepos.x, movepos.y, movepos.z);

  //check to see if the move is valid before we do anything
  //check the positioning, and check that the proper tiles were used
  works = TheBoard->Valid(movepos, move->getMoveDirection(), move->getWord());
  if(works == 0) used = DeterminePlayedTiles(move);
  if((works < 2) && used){
    if(strlen(used) == TILENUM) result += BONUS;
    delete [] used;
    result += TheBoard->Score(move->position, move->direction, move->input);
  }
  else if(used == 0) works = 8;
  return works;
}

int sjmEngine::ExchangePlayerTiles(int playerid, char* remove){
  int count = 0;
  char original[TILENUM];
  temp = getPlayer(playerid)->getTiles();
  for(tcount = 0; temp[tcount]; tcount++) original[tcount] = TheBag->Translate(temp[tcount]);
  original[tcount] = 0;
  while(remove[count]){
    for(tcount = 0; original[tcount]; tcount++){
      if(original[tcount] == remove[count]);
    }
    if(remove[count] == 0);
    else return 0;
    count++;
  }    
}

void sjmEngine::SubmitMove(sjmMove* TheMove, char* result){

  //by the odd chance that we have no players, do nothing
  if(numplayers==0) return 0;
  if(over == 1) return 0;
  sjmPlayer* mover;

  //before we even consider the move, make sure this isn't fraud
  if(mover = getPlayer(TheMove->getPlayerCode())){
    if(ActivePlayerId(TheMove->getPlayerCode())){
      switch(TheMove->getType()){
	
      case READY_TO_PLACE:
	if(ready == 0){
	  int score, error;
	  error = JudgeMove(TheMove, score);
	  getPlayer(TheMove->getPlayerID())->RevertTiles();
	  if(error > 1) result[0] = MOVE_FAILURE;
	  else{
	    result[0] = MOVE_SUCCESS;
	    for(int count = 0; count < 3; count++)
	      result[1 + count] = (score >> (count << 3))&255;
	    result[4]=0;
	    ready = 1;
	  }
	}
	break;
	
      case PLAY_MOVE:
	if(ready){
	  int score, error;
	  error = JudgeMove(TheMove, score);
	  if(error < 2){ //if the play is structurally but maybe not lexically valid ie no one challenged
	    getPlayer(TheMove->getPlayerId())->AddScore(score);
	    //integrate a valid move on the board
	    Coord movepos;
	    TheMove->getXYZ(movepos.x, movepos.y, movepos.z);
	    TheBoard->Integrate(movepos, move->getMoveDirection(), move->getData());
	    for(int count = getPlayer(TheMove->getPlayerId())->TileCount(); count < TILENUM && TheBag->HowMany(); count++)
	      getPlayer(TheMove->getPlayerId())->AddTile(TheBag->GetTile());
	    if(getPlayer(TheMove->getPlayerId())->NumTiles()){
	      result[0] = NEXT_PLAYER;
	      for(int count = 0; count < 3; count++)
		result[1 + count] = (score >> (count << 3))&255;
	      result[4]=0;
	      passcount = 0;
	      NextPlayer();
	    }
	    else{
	      over = 1;
	      result[0] = GAME_OVER;
	    }
	  }
	  ready = 0;
	}
	break;
	
      case SCORE_MOVE:
	int score, error;
	error = JudgeMove(TheMove, score);
	getPlayer(TheMove->getPlayerID())->RevertTiles();
	if(error > 1) result[0] = MOVE_FAILURE;
	else{
	  result[0] = MOVE_SUCCESS;
	  for(int count = 0; count < 3; count++)
	    result[1 + count] = (score >> (count << 3))&255;
	  result[4]=0;
	}
	break;
	
      case LOOKUP_WORD:
	result[0] = MOVE_FAILURE;
	break;
	
      case SUGGEST_MOVE:
	result[0] = MOVE_FAILURE;
	break;
	
      case DISCARD_TILES:
	if(TheBag->HowMany() >= TILENUM && strlen(TheMove->getData()) <= TILENUM){
	    if(ExchangePlayerTiles(TheMove->getPlayerID(), TheMove->getPlayerData())){
	      result[0] = MOVE_SUCCESS;
	      passcount = 0;
	      NextPlayer();
	    }
	    else result[0] = MOVE_FAILURE;
	}
	else result[0] = MOVE_FAILURE;
	break;
	
      case RATE_MOVE:
	result[0] = MOVE_FAILURE;
	break;
	
      case PASS:
	passcount++;
	NextPlayer();
	break;
	
      case RESIGN:
	KillPlayer(getActivePlayerId());
	passcount = 0;
	result[0] = MOVE_SUCCESS;
	break;
	
      default:
	result[0] = MOVE_FAILURE;
	break;
      }
    }
    else{
      switch(TheMove->getType()){
	
      case LOOKUP_WORD:
	result[0] = MOVE_FAILURE;
	break;
	
      case CHALLENGE:
	if(ready){
	}
	break;
	
      case RESIGN:
	KillPlayer(TheMove->getPlayerId());
	result[0] = MOVE_SUCCESS;
	passcount = 0;
	break;
	
      default:
	result[0] = MOVE_FAILURE;
	break;
	
      }
    }
  }
}









