#include "scr_Board.h"
#include <fstream>

#define PLAY_RESULT_OK 0
#define PLAY_RESULT_BOGUS_WORD 1
#define PLAY_RESULT_MALFORMED 2
#define PLAY_RESULT_WRONG_STRING 3
#define PLAY_RESULT_TILE_CONFLICT 4
#define PLAY_RESULT_BOUNDARY 5

//0: Play is ok
//1: A word is not in the dictionary
//2: Play is exectuted incorrectly
//3: Move has the wrong input string
//4: Move is trying to put a tile on a square already having a tile
//5: Move exceeds boundary of board

scr_Board::scr_Board(char* filename, scr_Bag* BagIn, scr_Dictionary* DictIn)
{
char x, y, z;
ifstream loadboard(filename, ios::in|ios::binary);

 Bag=BagIn; Dict=DictIn;
 nummoves=0;
 loadboard.get(xwide);
 loadboard.get(ywide);
 loadboard.get(zwide);
 bdim.point[0]=xwide; bdim.point[1]=ywide; bdim.point[2]=zwide;
 if(xwide>ywide)
 {
  if(xwide>zwide) BSIZE = xwide;
  else BSIZE = zwide;
 }
 else
 {
  if(ywide > zwide) BSIZE = ywide;
  else BSIZE = zwide;
 }

 Board = new Cube**[xwide];
 for(x=0; x<xwide; x++) Board[x] = new Cube*[ywide];
 for(x=0; x<xwide; x++)
  for(y=0; y<ywide; y++) Board[x][y] = new Cube[zwide];
 
 for(x=0; x<xwide; x++)
  for(y=0; y<ywide; y++)
   for(z=0; z<zwide; z++)
   {
    loadboard.get(Board[x][y][z].letter);
    loadboard.get(Board[x][y][z].wmult);
    loadboard.get(Board[x][y][z].smult);
    loadboard.get(Board[x][y][z].hasblank);
    loadboard.get(Board[x][y][z].adjacent);
    loadboard.get(Board[x][y][z].hasneighbor);
    loadboard.get(Board[x][y][z].value[0]);
    loadboard.get(Board[x][y][z].value[1]);
    loadboard.get(Board[x][y][z].value[2]);
    loadboard.read(&Board[x][y][z].flags[0], sizeof(unsigned long int));
    loadboard.read(&Board[x][y][z].flags[1], sizeof(unsigned long int));
    loadboard.read(&Board[x][y][z].flags[2], sizeof(unsigned long int));
   }
 loadboard.close();
}

scr_Board::scr_Board(char xw, char yw, char zw)
{
 int x, y, z;
  xwide = xw; ywide = yw; zwide = zw;
 bdim.point[0]=xwide; bdim.point[1]=ywide; bdim.point[2]=zwide;
 nummoves=0;
 if(xwide>ywide)
 {
  if(xwide>zwide) BSIZE = xwide;
  else BSIZE = zwide;
 }
 else
 {
  if(ywide > zwide) BSIZE = ywide;
  else BSIZE = zwide;
 }

 Board = new Cube**[xwide];
 for(x=0; x<xwide; x++) Board[x] = new Cube*[ywide];
 for(x=0; x<xwide; x++)
  for(y=0; y<ywide; y++) Board[x][y] = new Cube[zwide];
  
 for(x=0; x<xwide; x++)
  for(y=0; y<ywide; y++)
   for(z=0; z<zwide; z++)
   {
    Board[x][y][z].letter = 0;
    Board[x][y][z].wmult = 0;
    Board[x][y][z].smult = 0;
    Board[x][y][z].adjacent = 0;
    Board[x][y][z].hasblank = 0;
    Board[x][y][z].hasneighbor = 0;
    Board[x][y][z].flags[0] = Board[x][y][z].flags[1] = Board[x][y][z].flags[2] = 0;
    Board[x][y][z].value[0] = Board[x][y][z].value[1] = Board[x][y][z].value[2] = 0;
   }
}

//0 is vertical,   1 is horizontal,   2 is depth
void scr_Board::Integrate(Coordinate coords, char direction, char* stringer)
{
char backwards=(direction + 3);
char a, b, val, rval=0, p, *x, *y, *z, *fhook, *middle, *bhook;
char input[BSIZE+1],  spot[BSIZE+1],  count, waszero, zero, allow, empty=0;
Coordinate iterator=coords,  temp, fronthook, backhook;
char *x1,  *y1,  *z1;
unsigned long int change;

 x=((char*) &iterator);
 y=((char*) &iterator)+1;
 z=((char*) &iterator)+2;
 
 x1=((char*) &temp);
 y1=((char*) &temp)+1;
 z1=((char*) &temp)+2;
 for(a=0; stringer[a]; a++){
  input[a]=(stringer[a]&63);
  if(input[a]<31) input[a]+=64;
  else input[a] -=32;
 }
 input[a]=0;
 for(a=0; input[a]; a++, iterator.whole+=(1<<(direction<<3))){
  rval+=Bag->Value(input[a]);
  if(Letter(*x, *y, *z)==0){
   if(input[a]>63){
    input[a]-=64;
    Board[*x][*y][*z].hasblank=1;
   }
   Board[*x][*y][*z].letter=input[a];
   Board[*x][*y][*z].adjacent=0;
   Board[*x][*y][*z].wmult=1;
   Board[*x][*y][*z].smult=1;

   for(count=0, change=1; count<3; count++, change<<=8){
    val=Bag->Value(input[a]);
    Board[*x][*y][*z].flags[count]=0;
    if(count!=direction){
     for(b=0; b<BSIZE+1; b++) spot[b]=0;
     spot[iterator.point[count]]=input[a];

     //traverse forwards through board to find front hooks and do other odds and ends
     p = zero = 0;
     allow = 1;
     fhook = bhook = &empty;
     middle = spot + iterator.point[count];
     temp=iterator;
     while(HasNeighbor(*x1, *y1, *z1, count)){
      temp.whole+=change;
      if((spot[temp.point[count]]=Letter(*x1,  *y1,  *z1))==0) zero++;
      else p+=Value(*x1, *y1, *z1, count);
      if(zero&&allow){
       Board[*x1][*y1][*z1].adjacent|=(1<<count);
       val+=p;
       Board[*x1][*y1][*z1].value[count]-=p;
       allow = 0;
       fronthook=temp;
       fhook = spot + fronthook.point[count];
       if(HasNeighbor(*x1, *y1, *z1, count)) fhook++;      
      }
      if(zero==2) break;
     }
     waszero = zero;
     temp=iterator;

     //traverse backwords through board to find back hooks and do other odds and ends
     p = zero = 0;
     allow=1;
     while(HasNeighbor(*x1, *y1, *z1, count + 3)){
      temp.whole-=change;
      if((spot[temp.point[count]]=Letter(*x1,  *y1,  *z1))==0) zero++;
      else p+=Value(*x1, *y1, *z1, count);

      if(zero&&allow){
       Board[*x1][*y1][*z1].adjacent|=(8<<count);
       middle=spot + temp.point[count] + 1;
       val+=p;
       Board[*x1][*y1][*z1].value[count]-=p;
       allow = 0;
       backhook=temp;
      }
      if(zero==2){
       bhook = spot + temp.point[count]+1;
       break;
      }
     }
     if(spot[0]) bhook = spot;
     if(zero==0) backhook=temp;

     temp=backhook;
     if(zero){
      Board[*x1][*y1][*z1].flags[count]=Dict->Bridge(bhook,middle);
      Board[*x1][*y1][*z1].value[count]+=val;
     }
     temp=fronthook;
     if(waszero){
      Board[*x1][*y1][*z1].flags[count]=Dict->Bridge(middle,fhook);
      Board[*x1][*y1][*z1].value[count]+=val;
     }
    }
    Board[*x][*y][*z].value[count]=Bag->Value(input[a]);
   }//end for
  }//end if
 }//end for
 
 change=(1<<(direction<<3));
 for(a=0; a<BSIZE+1; a++) spot[a]=0;
 iterator.whole-=change;
 //iterator ended on the cube with the last letter of input
 //check for neighbor, see if any bridging or adjacency is necessary
 p = zero = 0; 
 allow = 1;
 temp=iterator;
 fhook=bhook=&empty;
 while(HasNeighbor(*x1, *y1, *z1, direction)){
  temp.whole+=change;
  if((spot[temp.point[direction]]=Letter(*x1, *y1, *z1))==0) zero++;
  else p+=Value(*x1, *y1, *z1, direction);

  if(zero&&allow){
   Board[*x1][*y1][*z1].adjacent|=(1<<direction);
   allow = 0;
   fronthook = temp;
   fhook = spot + fronthook.point[direction];
   if(HasNeighbor(*x1, *y1, *z1, direction)) fhook++;      
  }
  if(zero==2) break;
 }
 
 if(zero){
  temp=fronthook;
  Board[*x1][*y1][*z1].flags[direction]=Dict->Bridge(input,fhook);
  Board[*x1][*y1][*z1].value[direction]+=(rval-p);
 }

 temp=coords;
 p = zero = 0;
 allow = 1;
 while(HasNeighbor(*x1, *y1, *z1, backwards)){
  temp.whole-=change;
  if((spot[temp.point[direction]]=Letter(*x1, *y1, *z1))==0) zero++;
  else p+=Value(*x1, *y1, *z1, direction);

  if(zero&&allow){
   Board[*x1][*y1][*z1].adjacent|=(1<<backwards);
   allow = 0;
   backhook = temp;
  }
  if(zero==2){
   bhook = spot + temp.point[direction] + 1;
   break;
  }
 }
 if(spot[0]) bhook = spot;
 if(zero){
  temp=backhook;
  Board[*x1][*y1][*z1].flags[direction]=Dict->Bridge(bhook,input);
  Board[*x1][*y1][*z1].value[direction]+=(rval-p);
 }
 nummoves++; 
}

//Error codes for Valid:
//0: Play is ok
//1: A word is not in the dictionary
//2: Play is exectuted incorrectly
//3: Move has the wrong input string
//4: Move is trying to put a tile on a square already having a tile
//5: Move exceeds boundary of board

int scr_Board::Valid(Coordinate coords, char direction,  char* stringer){
char a,  p=0, other;
Coordinate iterator=coords;
char *x, *y, *z, backwards=(direction + 3);
unsigned long int change=(1<<(direction<<3));
char input[BSIZE+1];
 int falsity = 0;

 for(a=0; stringer[a]; a++) input[a]=(stringer[a]&31);
 input[a]=0;

 x = ((char*)&iterator);
 y = ((char*)&iterator) + 1;
 z = ((char*)&iterator) + 2;

 if(HasNeighbor(*x, *y, *z, backwards)){
  iterator.whole-=change;
  if(Letter(*x, *y, *z)) return PLAY_RESULT_WRONG_STRING;
  iterator=coords;
 }

 for(a=0; input[a]; a++, iterator.whole+=change){
  if((HasNeighbor(*x, *y, *z, direction)==0)&&(input[a+1])) return PLAY_RESULT_BOUNDARY;
  if(Letter(*x, *y, *z)){
   if(Letter(*x, *y, *z)!=input[a]) return PLAY_RESULT_TILE_CONFLICT;
  }
  else{
   for(other=0; other<3; other++){
    if(other!=direction){
     if(((1<<(input[a]-1))&Flags(*x, *y, *z, other))==0) falsity = 1;
    }
   }
   if(Board[*x][*y][*z].adjacent) p=1;
  }
 }
 
 if(p==0) return PLAY_RESULT_MALFORMED_WORD;

 if(iterator.point[direction]<bdim.point[direction])
  if(Letter(*x, *y, *z)) return PLAY_RESULT_WRONG_STRING;

 if(a==1){
  if(nummoves==0) return PLAY_RESULT_BOGUS_WORD;
 }
 else if(Dict->Verify(input)==0) return PLAY_RESULT_BOGUS_WORD;
 
 if(p) return PLAY_RESULT_OK;
 else return PLAY_RESULT_MALFORMED;
}

//pretty self explanatory what this function does
int scr_Board::Score(Coordinate coords, char direction, char* stringer){
char a;
char input[BSIZE+1];

 for(a=0;stringer[a];a++)
 {
  input[a]=(stringer[a]&63);
  if(input[a]<31) input[a]+=64;
  else input[a]-=32;
 }

 input[a]=0;

 return(RawScore(coords, direction, input));
}

int scr_Board::RawScore(Coordinate coords, char direction, char* input){
char a, mult=1;
int total=0, count, foo;
unsigned long int change=(1<<(direction<<3));
Coordinate iterator=coords;
char *x, *y, *z;

 x = ((char*)&iterator);
 y = ((char*)&iterator) + 1;
 z = ((char*)&iterator) + 2;

 for(a=0;input[a];a++, iterator.whole+=change){
  mult*=Board[*x][*y][*z].wmult;
  if(Letter(*x, *y, *z)){
   if(HasBlank(*x, *y, *z)==0) total+=Bag->Value(input[a]);
  }
  else{
   foo=1;
   for(count=0; count<3; count++){
    if(count!=direction){
     total+=Board[*x][*y][*z].value[count] * Board[*x][*y][*z].wmult;
     foo+=((Board[*x][*y][*z].value[count]>0) ? 1 : 0);
    }
   }
   total+=Bag->Value(input[a]) * Board[*x][*y][*z].smult * foo;
  }
 }
 return(total*mult);
}

/* allocate memory and stringify the board 
   We really treat the char ** as a pointer to a buffer; there is no guarantee
   that there won't be non-printable characters in the generated string.

   Returns length of  memory allocated.

   Function originally implemented by Blake Scholl. Adapted for use by David Bender
 */
int scr_Board::Stringify(char **buf)
{
  
  // The number of squares is the square of the size along an edge

  // for portability, use sizeof rather than predetermined conventions
  //   for sizes
  int uLIsize = sizeof(unsigned long int);
  int Csize = sizeof(char);
  int cubeSize = (3*uLIsize+9*Csize);
  // square size * number of squares + null terminator
  char * boardBuf = new char[xwide * ywide * zwide * cubeSize];

  char * p = boardBuf; // point to the beginning of the buffer

  // p always points to the next available byte in the buffer.  
  for(int x =0; x < xwide; x++)
   for(int y = 0; y < ywide; y++)
    for(int z = 0; z < zwide; z++)
     {
	  // copy the data from the mult-byte fields into the buffer,
	  // increment p appropriately
	  memcpy(p, (char*)&Board[x][y][z].letter, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].wmult, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].smult, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].hasblank, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].adjacent, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].hasneighbor, Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].value[0], Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].value[1], Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].value[2], Csize); p += Csize;
	  memcpy(p, (char*)&Board[x][y][z].flags[0], uLIsize); p += uLIsize;
	  memcpy(p, (char*)&Board[x][y][z].flags[1], uLIsize); p += uLIsize;
	  memcpy(p, (char*)&Board[x][y][z].flags[2], uLIsize); p += uLIsize;
     }

  // buf should be a pointer to the buffer we allocated 
  // (this is a "pointer-pointer" string)
  *buf  = boardBuf;
  /* return the ammount allocated */
  return (xwide * ywide * zwide * cubeSize);
}

void scr_Board::GetCopyOfBoard(char*** representation){
char x, y, z;
 for(x=0; x<xwide; x++)
  for(y=0; y<ywide; y++)
   for(z=0; z<zwide; z++)
   {
    representation[x][y][z]='-';
    if(Letter(x, y, z)){
     representation[x][y][z]=TranslateBoardLetter(x, y, z);
    }
    else{
     if(WordMult(x, y, z)>1) representation[x][y][z]=((WordMult(x, y, z)==2) ? '@' : '#');
     else{
      if(LetterMult(x, y, z)>1) representation[x][y][z]=((LetterMult(x, y, z)==2) ? '[' : ']');
     }
    }
   }
}

char scr_Board::FitLetter(char x, char y, char z, char direction, char letter){
char count;
unsigned long int flags=0xFFFFFFFF;
 if(Letter(x,y,z)) return(letter==Letter(x,y,z));
 else{
  for(count=0; count<3; count++)
   if(count!=direction) flags&=Flags(x,y,z,count);
  return((flags&(1<<(letter-1)))>0);
 }
}
