/********************************************************************
Class board_panel
 --the game board

Authors: Kristi Garner, Mary Garland    
CS406: Java and Internet Programming
*******************************************************************/
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.util.*;
import java.applet.*;
import java.net.*;
import java.applet.Applet;
import java.io.*;

class board_panel extends Panel
{  

  private game_panel selected_first = null;             // selected tile
  private game_panel selected_empty = null;             // selected empty board square
  private game_panel Grid[][];                          // game board
  private tiles_panel tp;                               // my tiles
  private int row_max, col_max;                         // dimensions of board
  private final int emptypanelheight = 27;              
  private final int emptypanelwidth = 27;

  private boolean play = true;
  private boolean undo = true;
  private boolean done = true;
  private boolean accept = false;
  private boolean challenge = false;

  private int numTilesPlayed = 0;
  private boolean error = false;

  private int me, opponent;
  private String my_word = "", opponent_word = "";
  private int my_points = 0, opponent_points = 0;
  private int player_totals[] = new int[3];  

  private TextArea info, wordlist;
  private TextField my_score, opponent_score;
  private boolean my_turn;

  private Game mygame;
  private String mymove;
  private boolean horizontal;
  private int move_row, move_col;

  private Button undoButton;
  private button_panel ud;
  private button_panel ca;

  private static boolean out_of_tiles = false;
  private static boolean game_over[] = new boolean[3];


  /******************************************************************/
  public board_panel(int rows, int cols, boolean myturn, Game g, TextArea i, TextArea w, TextField s1, TextField 
s2,button_panel undo_done, button_panel chal_accept)
  {

    Grid = new game_panel[rows][cols];
    row_max = rows;
    col_max = cols;
    this.setLayout(null);
    this.setSize(emptypanelwidth*cols, emptypanelheight*rows);
    ud = undo_done;
    ca = chal_accept;
    info = i; 

    my_turn = myturn;
    if(my_turn) {
      me = 1;
      opponent = 2;
      my_score = s1;
      opponent_score = s2;
      info.append("--> Your turn\n");
      info.append("    Place your tiles on the board\n");
      info.append("    then click Done.\n");    
      ca.hide();
    } else {
      me = 2;
      opponent = 1;
      my_score = s2;
      opponent_score = s1;
      info.append("--> Waiting for other player to place\n");
      info.append("  a word.\n");
      ud.hide();
    }
    player_totals[1] = 0;
    player_totals[2] = 0;
    game_over[1] = false;
    game_over[2] = false;

    mygame = g;
    wordlist = w;
    ud.setVisible(true);
    ca.setVisible(true);
  } 

  /*******************************************************************/
  // add_a_panel- adds each board square to the board  
  // parameters: image- image to be displayed on the board square
  //             row, col - location of square
  //             lx - 2 for double letter score, 3 for triple, 1 otherwise
  //             wx - 2 for double word score, 3 for triple, 1 otherwise
  //             t - my tiles          
  public void add_a_panel(Image image, int row, int col, int lx, int wx, tiles_panel t)
  {
    tp = t;
    Grid[row][col] = new game_panel(image, row, col, this, tp, false);
    Grid[row][col].setLetterX(lx);
    Grid[row][col].setWordX(wx);
    this.add(Grid[row][col]);
  } 


  /*******************************************************************/
  // setSelectedEmpty - places tile on selected empty board square
  // parameter: selected- empty board square 
  public void setSelectedEmpty(game_panel selected) 
  {
    Image first_image;
    Image empty_image;
    char first_letter;
    int letter_pts;

    if(!my_turn || !play) {
      return;
    }

    // selected_first holds the selected tile
    selected_first = tp.getSelectedFirst();

    if(selected_first != null && !selected_first.isEmpty()) {

      // selected_empty holds the empty board square
      selected_empty = selected;

      first_image = selected_first.getImage();
      empty_image = selected_empty.getImage();
      first_letter = selected_first.getLetter();
      letter_pts = selected_first.getLetterPtValue();
        
      // set the tile's image, letter, pt value to board square
      // -board square no longer empty, now current
      selected_empty.setImage(first_image);
      selected_empty.setLetter(first_letter);
      selected_empty.setLetterPtValue(letter_pts);
      selected_empty.setEmpty(false);
      selected_empty.setCurrent(true); 
 
      // -tile now empty, now current   
      selected_first.setImage(tp.getEmptyImage());
      selected_first.setEmpty(true);
      selected_first.setCurrent(true);

      numTilesPlayed++;
    }

  } // end  method


  /*******************************************************************/
  // undoTurn - set all board squares that are current back to the original image
  //          - undo tiles
  public void undoTurn() 
  {
    
    int i, j;

    if(!my_turn || !undo) {
      return;
    }

    error = false;
    for(i=0; i < col_max; i++) {
      for(j=0; j < row_max; j++) {
        if( Grid[i][j].getCurrent() ) {
          Grid[i][j].setCurrent(false);
          Grid[i][j].setOrigImage();
          Grid[i][j].setEmpty(true);
        }
      }
    }

    tp.tilesUndoTurn();

    resetMyWord();

  }


  /*******************************************************************/
  // undoOpponentTurn 
  public void undoOpponentTurn() {

    int i, j;

    for(i=0; i < col_max; i++) {
      for(j=0; j < row_max; j++) {
        if( Grid[i][j].getCurrent() ) {
          Grid[i][j].setCurrent(false);
          Grid[i][j].setOrigImage();
          Grid[i][j].setEmpty(true);
        }
      }
    }

    resetOpponentWord();

  }


  /*******************************************************************/
  // doneTurn - called from Done button
  public void doneTurn() 
  {

    if(!my_turn || !done) {
      return;
    }
    /*
    if(out_of_tiles && (numTilesPlayed == 0 || tp.emptyTiles() == 0)) {
      game_over[me] = true;
    }
    if(game_over[me] && game_over[opponent]) {
      info.append("Game Over!\n");
      disableAll();
      return;
    }
    */
    if(numTilesPlayed > 0) {
        error_check();
    }
    if(error) {
      undoTurn();
    } else {
      make_move_string();
     
      if(mymove.equals("pass") ) {
        info.append("You passed.\n");
        replenishTiles();
        resetMyWord();
        resetBoard();
      } else {
        info.append("Your word is " + my_word + ", points = " + my_points + "\n");    
        info.append("--> Waiting for other player\n");
        info.append("    to challenge or accept\n");            
      }

      send(mymove);
    }

  }

  /*******************************************************************/
  // replenishTiles
  public void replenishTiles() 
  {
    tp.tilesNewTurn();

    out_of_tiles = tp.replenish();
    /*
    if(out_of_tiles && tp.emptyTiles() == 0) {
      game_over[me] = true;  
    }
    */
  }

  /*******************************************************************/
  // acceptTurn - called from Accept button
  public void acceptTurn() 
  {

    if(!my_turn || !accept) {
      return;
    }

   info.append("--> You accepted your opponent's move.\n");
   updateOpponentScore();
   send("accepted");

  }

  /******************************************************************/
  public void backPlay() {

   resetMyWord();
   resetBoard();
   info.append("--> Place your tiles to form a word\n");
   info.append("    or click Done to pass.\n");

   enablePlay();       

  }


  /*******************************************************************/
  public void updateMyScore() {
    String sc;
     if(!my_word.equals("") ) {

      wordlist.append("Player " + me + "          " + my_word + "\n");

      // Update my score
      player_totals[me] = player_totals[me] + my_points;
      sc = "Player " + me + " Score = " + player_totals[me];
      my_score.setText(sc);   
    } 
  }

  /*******************************************************************/
  public void updateOpponentScore() {
    String sc;

    if(!opponent_word.equals("") ) {
      wordlist.append("Player " + opponent + "          " + opponent_word + "\n");

      // Update opponent score
      System.out.println("Opponent Total = " + player_totals[opponent] + ", points = " + opponent_points + "\n");
      player_totals[opponent] = player_totals[opponent] + opponent_points;
      sc = "Player " + opponent + " Score = " + player_totals[opponent];
      opponent_score.setText(sc);
    }
  }

  /*******************************************************************/
  // challengeTurn - called from Challenge button
  public void challengeTurn() 
  {

    boolean found;

    if(!my_turn || !challenge) {
      return;
    }

    info.append("--> You challenged your opponent's move.\n");
    found = challengeOpponentWord();

    if(found) {
      updateOpponentScore();

      info.append("Word " + opponent_word + " found\n");
      info.append("You lose your turn.\n");
      resetMyWord();
 
      send("challenge found");

    } else { // word not found
      info.append("Word " + opponent_word + " not found\n");
      info.append("Player " + opponent + " loses his turn\n");
      undoOpponentTurn();
      send("challenge not found");
    }

  }


  /*******************************************************************/
   public void make_move_string() {

    mymove = "";
    char delim = ' ';
    int h;

    if(numTilesPlayed == 0) {
      mymove = "pass";

    } else {

      if(horizontal) {
        h = 1;
      } else {
        h = 0;
      }

      mymove = my_word + delim + move_row + delim + move_col + delim + my_points + delim + h + delim;
    }

  }


  /*******************************************************************/
  public void send(String s) {
    my_turn = false;
    disableAll();
    mygame.send_move(s);
  }


  /*******************************************************************/
  public void getOpponentMove(String s) {
    
    StringTokenizer tokens = new StringTokenizer(s);

    String row1, col1, pt1, h1;
    int opp_row=0, opp_col=0;
    int opp_h=0;

    my_turn = true;
    disableAll();

    if(s.equals("pass") ) {
      resetOpponentWord();
      info.append("\nPlayer " + opponent + " passed\n");
      info.append("--> Your turn\n");
      backPlay();

    } else if(s.equals("challenge found") ) {

      resetOpponentWord();
      info.append("\nPlayer " + opponent + " challenged your word " + my_word + ".\n");
      info.append(my_word + " was found.\n");
      info.append("Player " + opponent + " loses his turn.\n");

      updateMyScore();

      replenishTiles();

      info.append("--> Your turn\n");
      backPlay();

    } else if(s.equals("challenge not found") ) {

      info.append("\nPlayer " + opponent + " challenged your word " + my_word + ".\n");
      info.append(my_word + " was not found.\n");
      info.append("You lose your turn.\n");
      info.append("--> Waiting for other player to place\n");
      info.append("  a word.\n");

      undo = true;
      undoTurn();      
      send("back");

    } else if(s.equals("accepted") ) {
      info.append("\nPlayer " + opponent + " accepted your word.\n");
      info.append("--> Waiting for other player to place\n");
      info.append("  a word.\n");
      updateMyScore();

      replenishTiles();

      send("back");

    } else if(s.equals("back") ) {
      backPlay();

    } else {
    
      resetBoard();

      opponent_word = tokens.nextToken();

      row1 = tokens.nextToken();
      opp_row = Integer.parseInt(row1);

      col1 = tokens.nextToken();
      opp_col = Integer.parseInt(col1);

      pt1 = tokens.nextToken();
      opponent_points = Integer.parseInt(pt1);

      h1 = tokens.nextToken();
      opp_h = Integer.parseInt(h1);
        
      info.append("\nPlayer " + opponent + "s word is " + opponent_word + ", points = " + opponent_points + "\n");   
      display_opponent_move(opponent_word, opp_row, opp_col, opp_h);

      info.append("--> Your turn\n");
      info.append("-->Accept or challenge your opponent's\n");
      info.append("   move.\n");
      enableAcceptChallenge();
  
    }


  }


  /*******************************************************************/
  public void display_opponent_move(String w, int r, int c, int h) {

    int len = w.length();
    int i, wi = 0, x;
    char first_ltr;
    Image ltr_image;
    int ltr_pts;

    if(h == 1) {        // if horizontal
      len = len + c;
      for(i = c; i < len; i++) {

        if(Grid[r][i].isEmpty() ) {

          first_ltr = w.charAt(wi);
          x = (int)first_ltr; x = x - 97;

          ltr_image = tp.getImage(x);
          ltr_pts = tp.getPoints(x);

          Grid[r][i].setImage(ltr_image);
          Grid[r][i].setLetter(first_ltr);
          Grid[r][i].setLetterPtValue(ltr_pts);
          Grid[r][i].setEmpty(false);
          Grid[r][i].setCurrent(true); 
        }
        wi++;

      }
    } else {            // if vertical
      len = len + r;
      for(i = r; i < len; i++) {

        if(Grid[i][c].isEmpty() ) {

          first_ltr = w.charAt(wi);
          x = (int)first_ltr; x = x - 97;

          ltr_image = tp.getImage(x);
          ltr_pts = tp.getPoints(x);

          Grid[i][c].setImage(ltr_image);
          Grid[i][c].setLetter(first_ltr);
          Grid[i][c].setLetterPtValue(ltr_pts);
          Grid[i][c].setEmpty(false);
          Grid[i][c].setCurrent(true); 
        }
        wi++;

    }
  } // end else

}


  /*******************************************************************/
  public void resetBoard() {

    int i,j;

    for(i=0; i < row_max; i++) {
      for(j=0; j < col_max; j++) {
        Grid[i][j].setCurrent(false);
      }
    }

  }


  /*******************************************************************/
  public void resetMyWord() {
    my_word = "";
    my_points = 0;
    error = false;
    numTilesPlayed = 0;
  }


  /*******************************************************************/
  public void resetOpponentWord() {
    opponent_word = "";
    opponent_points = 0;

  }


  /*******************************************************************/
  public void disableAll() {
    play = false;
    undo = false;
    done = false;
    accept = false;
    challenge = false;
    ca.setVisible(false);
    ud.setVisible(false); 
  }



  /*******************************************************************/
  public void enableAcceptChallenge() {
    accept = true;
    challenge = true;
    play = false;
    undo = false;
    done = false;
    ca.setVisible(true);
    ud.setVisible(false); 
  }


  /*******************************************************************/
  public void enablePlay() {
    play = true;
    undo = true;
    done = true;
    accept = false;
    challenge = false;
    ca.setVisible(false);
    ud.setVisible(true); 

  }


  /*******************************************************************/
  public boolean check_red_square() {
    if(Grid[7][7].isEmpty()) {
      return false;
    } 
    return true;
  }


  /*******************************************************************/
  public void error_check() 
  {

    int i, j;
    int total_tiles = 0;
    int crow[], ccol[];
    int rc = 0; // row if horizontal, col if vertical
    int start, end;
    boolean err;
    horizontal = true;
 
    crow = new int[20];
    ccol = new int[20];

    for(i=0; i < row_max; i++) {
      for(j=0; j < col_max; j++) {
        if( Grid[i][j].getCurrent() ) {

          crow[total_tiles] = i;
          ccol[total_tiles] = j;

          total_tiles++;
        }
      }
    }

    // Error checking

    // error check- first move must be made on red square
    if(!check_red_square() && numTilesPlayed > 0) {
      info.append("You must play the first word on the red square.\n");
      error = true;
      return;
    }

    // error check- must play 2+ tiles
    if(total_tiles < 2) {
      info.append("You must play at least 2 tiles.\n");
      error = true;
      return;
    }

    // error check- tiles not placed in same row or column
    if(crow[1] != crow[0] && ccol[1] != ccol[0]) {
      info.append("Tiles must be placed in the same row or column.\n");
      error = true;
      return;
    }

    // Determine if horizontal or vertical word
    if(crow[1] == crow[0]) {
      horizontal = true;
      rc = crow[0];
    } else if(ccol[1] == ccol[0]) {
      horizontal = false;
      rc = ccol[0];
    }

    if(horizontal) {

      // error check- check all tiles in same row
      err = check_in_line(rc, crow);
      if(err) {
        error = true;
        return;
      }

      // determine beginning and ending column of word
      start = word_start(true, ccol[0], rc);
      end = word_end(true, ccol[total_tiles-1], rc);

      // error check- word must be at least 2 letters
      if(end - start + 1 < 2) {
        info.append("The word must be at least 2 letters long.\n");
        error = true;
        return;
      }

      // error check- tiles must touch each other & a previously placed tile
      err = check_tile_touch(true, start, end, rc);
      if(err) {
        error = true;
        return;
      }

      // determine the word and its point value
      determine_word(true, start, end, rc);
           
    // else vertical word
    } else {

      // error check- check all tiles in same column
      err = check_in_line(rc, ccol);
      if(err) {
        error = true;
        return;
      }

      // determine beginning and ending row of word
      start = word_start(false, crow[0], rc);
      end = word_end(false, crow[total_tiles-1], rc);

      // error check- word must be at least 2 letters
      if(end - start + 1 < 2) {
        info.append("The word must be at least 2 letters long.\n");
        error = true;
        return;
      }

      // error check- tiles must touch each other & a previously placed tile
      err = check_tile_touch(false, start, end, rc);
      if(err) {
        error = true;
        return;
      }

      // determine the word and its point value
      determine_word(false, start, end, rc);

    } // end else horizontal
  }


  /*******************************************************************/
  public void determine_word(boolean h, int xstart, int xend, int y) {

    int j;
    my_word = "";
    my_points = 0;
    char l; int pt;             // This board square's letter & its points
    int lx, wx;                 // This board square letter score, word score
    int wx_total = 1;

    if(h) {

      for(j = xstart; j <= xend; j++) {
        l = Grid[y][j].getLetter();
        pt = Grid[y][j].getLetterPtValue();
        if(Grid[y][j].getCurrent() ) {
          lx = Grid[y][j].getLetterX();
          wx = Grid[y][j].getWordX();
        } else {
          lx = 1;
          wx = 1;
        }
        my_word = my_word + l;
        my_points = my_points + (pt * lx);
        wx_total = wx_total * wx;
      }
      move_row = y;
      move_col = xstart;
      

    // else vertical word
    } else {
      for(j = xstart; j <= xend; j++) {
        l = Grid[j][y].getLetter();
        pt = Grid[j][y].getLetterPtValue();
        if(Grid[j][y].getCurrent() ) {
          lx = Grid[j][y].getLetterX();
          wx = Grid[j][y].getWordX();
        } else {
          lx = 1;
          wx = 1;
        }

        my_word = my_word + l;
        my_points = my_points + (pt * lx);
        wx_total = wx_total * wx;
      }
      move_row = xstart;
      move_col = y;
    }

    if(numTilesPlayed == 7) {
      my_points = my_points * wx_total + 50;
    } else {
      my_points = my_points * wx_total;
    }

  }


  /*******************************************************************/
  public int word_start(boolean h, int x, int y) {
  
    int i, start;

    if(h) {
      start = x;
      for(i = x; i >= 0; i--) {
        if( !Grid[y][i].isEmpty() ) {
          start = i;
        } else {
          break;
        }
      }
    } else {
      start = x;
      for(i = x; i >= 0; i--) {
        if( !Grid[i][y].isEmpty() ) {
          start = i;
        } else {
          break;
        }
      }
    }
    return start;
  }


  /*******************************************************************/
  public int word_end(boolean h, int x, int y) {
  
    int i, end;

    if(h) {
      end = x;
      for(i = x; i < 15; i++) {
        if( !Grid[y][i].isEmpty() ) {
          end = i;
        } else {
          break;
        }
      }
    } else {
      end = x;
      for(i = x; i < 15; i++) {
        if( !Grid[i][y].isEmpty() ) {
          end = i;
        } else {
          break;
        }
      }
    }
    return end;
  }


  /*******************************************************************/
  public boolean check_in_line(int x, int current[]) {
  
    int i;

    for(i = 0; i < numTilesPlayed; i++) {
      if (current[i] != x) {
        info.append("Tiles must be placed in the same row or column.\n");
        return true;
      }
    } // end for
    return false;
  }


  /*******************************************************************/
  public boolean check_tile_touch(boolean h, int xstart, int xend, int y) {

    int i, j, total = 0;
    boolean touchPrev = false;
  
    for(i=0; i < 15; i++) {
      for(j=0; j < 15; j++) {
        if( !Grid[i][j].isEmpty() && !Grid[i][j].getCurrent() ) {
          total++;
        }
      }
    }
        
    if(h) {
      for(i = xstart; i <= xend; i++) {
        if(Grid[y][i].isEmpty()) {
          info.append("Tiles must be placed next to each other.\n");
          return true;
        }
        if( !Grid[y][i].getCurrent() ) {
          touchPrev = true;
        }
      }
    } else {
      for(i = xstart; i <= xend; i++) {
        if(Grid[i][y].isEmpty()) {
          info.append("Tiles must be placed next to each other.\n");
          return true;
        }
        if( !Grid[i][y].getCurrent() ) {
          touchPrev = true;
        }
      }
    }
    
    if(touchPrev) {
      return false;
    } else if(total == 0) {
      return false;
    } else {
      info.append("Tiles must touch a previously played tile.\n");
      return true;
    }
  }


  /******************************************************************/
public boolean challengeOpponentWord() {

      URL url;
      BufferedReader input;
      String line;
      StringBuffer buffer = new StringBuffer();

      boolean word_found = false;
      char first_letter = opponent_word.charAt(0);
      String location = "http://zansiv.millersv.edu/~megarlan/cs406/Scrabble/words" + first_letter + ".dat";

      info.append("Searching the dictionary...\n");
      try {
         url = new URL( location );
         input = new BufferedReader(
            new InputStreamReader( url.openStream() ) );

         while ( ( line = input.readLine() ) != null ) {
            buffer.append( line ).append( '\n' );
            if(line.equals(opponent_word)) {
              word_found = true;
              break;
            }
         }

         input.close();
      }
      catch ( MalformedURLException mal ) {
         System.out.println("Invalid URL format.");
      }
      catch ( IOException io ) {
         System.out.println( io.toString() );
      }
      catch ( Exception ex ) {
         System.out.println("This browser only allows opening a file on the applet's server" );
      }
     return word_found;
   }


} // end class board_panel


