/** (C) 1999 World Xiangqi League, Confidential, All Rights Reserved */

import java.awt.*;
import java.util.*;

class GamePanel extends Panel {
  private Club club;
  private FormLayout layout;
  private Panel buttonPanel; // vertical panel to right of gameCanvas
  public GameCanvas gameCanvas;

  // Should have a local player field to simplify all the code here
  // which tries to access the current player.
  ///public PlayerCanvas player;
  public int role = 2; // 0 for red, 1 for blue, 2 or greater for observer
  private int resetRole;	// Game->Reset restriction?

  // -----------------------------------------------------------------
  GamePanel(Club club) {
    this.club = club;
    role = 2;
    //hide();
    gameCanvas = new GameCanvas(this,club);
    gameCanvas.loadPieceImages(null);
    // Button panel Toolbar
    buttonPanel = new Panel(); {
      buttonPanel.setLayout(new GridLayout(0,1,8,5)); // rows/cols/vgap/hgap
      buttonPanel.add(new ImageButton("home","Club->Lobby"));
      buttonPanel.add(new ImageButton("who","Member->Here"));
      buttonPanel.add(new ImageButton("new","Club->New Table"));
      buttonPanel.add(new ImageButton("goto","Club->Join or Observe Table"));
      // Add the VCR buttons:
      buttonPanel.add(new Label());// padding hack; should use FormLayout
      buttonPanel.add(new ImageButton("first","Display opening position"));
      buttonPanel.add(new ImageButton("prev","Display previous position"));
      buttonPanel.add(new ImageButton("next","Display next position"));
      buttonPanel.add(new ImageButton("last","Display current position"));
      buttonPanel.add(new Label("")); // padding hack
      buttonPanel.add(new ImageButton("save","Game->Save to your homepage"));
      buttonPanel.add(new ImageButton("web","Open page in browser"));
    }
    setupGame(null,null,true);
  }

  public void setupGame(String player1, String player2, boolean ready) {
    if (isMe(player1))
      role = 0;
    else if (isMe(player2))
      role = 1;
    else
      role = 2;
    club.title();
    gameCanvas.initState(role,ready);
    if (practiceCheckbox!=null)
      practiceCheckbox.setState(false);
    club.redPlayer.gamePanel(this);
    club.bluePlayer.gamePanel(this);
    hide();
    removeAll();
    layout = new FormLayout(this,2,3);
    layout.add(((role==1) ? club.redPlayer : club.bluePlayer),
	       1,1, 2,1, 'H', 1.0,0.0, 'W');
    layout.add(gameCanvas,  1,2, 1,1, 'B', 1.0,1.0, 'C');
    layout.add(buttonPanel, 2,2, 1,1, 'N', 0.0,1.0, 'C');
    layout.add(((role==1) ? club.bluePlayer : club.redPlayer),
	       1,3, 2,1, 'H', 1.0,0.0, 'W');
    validate();
    show();
  }
  public void gotoLobby() {
    if (canLeave()) {
      role = 2;
      show();
      club.send("observe Lobby");
      club.table = "Lobby";
      club.redPlayer.setupPlayer(null);
      club.bluePlayer.setupPlayer(null);
      club.title();
      gameCanvas.initState(role,true);
      practiceCheckbox.setState(false);
    }
  }

  private long syncTimers = 0;
  private int syncMoves = 0;
  private void gameSync() {
    if (role==0) syncTimers = club.redPlayer.getTimers();
    if (role==1) syncTimers = club.bluePlayer.getTimers();
    syncMoves = gameCanvas.gameReset();
    club.send("list @"+club.table);
    // have processReady look at saved timer values
    // and moves made; if same number of moves made, restore
    // saved timer values!
  }
  

  // -----------------------------------------------------------------
  // Size Management.

  /* Called by Club. Param x is requested split position.
   * Make a new split, and return actual split position.
   */
  public int slider(int x) {
    if (x==0) {
      // Return where slider cursor should be shown.
      return size().width - (buttonPanel.size().width / 2);
    } else {
      int border = x + (buttonPanel.size().width / 2);
      preferredSize = new Dimension(border, size().height);
      validate();
      //return size().width + (buttonPanel.size().width / 2);
      return border;
    }
  }
  public int boardWidth() {
    return size().width - (buttonPanel.size().width);
  }
  private Dimension preferredSize = null;
  public Dimension preferredSize() {
    return (preferredSize==null) ? size() : preferredSize;
  }

  // -----------------------------------------------------------------

  public boolean canJoin(String table) {
    if (role<2 && club.table.equals(table)) {
      club.state("You are already playing here.");
      return false;
    }
    return true;
  }
  
  public boolean canLeave() {
    //Club.debug("GamePanel.canLeave "+club.table);
    if (club.hasGame() &&
	role<2 && gameCanvas!=null && gameState()=='r') {
      club.state("You must first finish the game.");
      return false;
    }
    return true;
  }

  public char gameState() {
    return gameCanvas.gameState;
  }

  // -----------------------------------------------------------------
  // Handle server broadcasts about table state changes:

  /** Are both players here? */
  public boolean bothPlayers() {
    return club.redPlayer.isHere() && club.bluePlayer.isHere();
  }
  private boolean isMe(String player) {
    return club.isMe(player);
  }

  public void processReady(String table) {
    int numMoves = gameCanvas.setReady();
    if (syncMoves>0 && numMoves==syncMoves) {
      if (role==0) club.redPlayer.restoreTimers(syncTimers);
      if (role==1) club.bluePlayer.restoreTimers(syncTimers);
    }
    club.myTable(table,"ready");
    show();
    repaint();
    syncMoves = 0;
    syncTimers = 0;
  }
  public void processPractice(String table, String state) {
    if (!club.myTable(table,"practice"))
      return;
    gameCanvas.practice = state.equalsIgnoreCase("on");
    practiceCheckbox.setState(gameCanvas.practice);
  }
  public synchronized void processMove(int moveNum,
				       String piece,String pos1,
				       String pos2,String piece2,
				       int seconds) {
    gameCanvas.processMove(moveNum,piece,pos1,pos2,piece2,
			   seconds,true); // realtime animation
  }

  public void processEndGame(String table) {
    if (club.myTable(table,null))
      gameCanvas.endGame();
  }

  // -----------------------------------------------------------------
  /** One of the player timers just timed out. */
  public void timeout(String player) {
    Club.debug("timeout "+player+", "+club.username+"="+role);
    if (role<2 && player.equalsIgnoreCase(club.username)) {
      resign();
    }
  }
  /** Handle timer menu command */
  private void handleTimerMenu(RadioMenu menu) {
    if (!bothPlayers()) {
      club.state("Need both players.");
      club.sound("cmderr");
      menu.reset();
      return;
    }
    boolean done = false;
    if (club.hasGame() && role==0 && gameState()=='b') {
      int gameMinutes = gameTimerMenu.selectedInt();
      int moveMinutes = moveTimerMenu.selectedInt();
      club.state("Setting timers "+gameMinutes+" "+moveMinutes+"...");
      club.send("timer "+club.table+" "+gameMinutes+" "+ moveMinutes);
      //Club.debug("Setting timers "+gameMinutes+" "+moveMinutes);
      club.redPlayer.setTimers(gameMinutes,moveMinutes);
      club.bluePlayer.setTimers(gameMinutes,moveMinutes);
      club.state("OK.");
      done = true;
    } else {
      club.state("Red player must set timers before game starts.");
    }
    if (!done) {
      menu.reset();
      club.sound("cmderr");
    }
  }

  public boolean defaultTimers() {
    return (gameTimerMenu.selectedInt()==30 &&
	    moveTimerMenu.selectedInt()==3);
  }

  /** Server timer broadcast. */
  public void processTimer(String table,
			   String gameMinutes, String moveMinutes,
			   String p1seconds, String p2seconds) {
    if (!club.myTable(table,"timer"))
      return;

    boolean running = gameState()=='r';
    gameTimerMenu.setChecked(gameMinutes);
    moveTimerMenu.setChecked(moveMinutes);
    Club.debug("setupTimers "+running+" "+role);
      
    if (!running || role!=0)
      club.redPlayer.setTimers(gameMinutes,moveMinutes,p1seconds);
    if (!running || role!=1)
      club.bluePlayer.setTimers(gameMinutes,moveMinutes,p2seconds);
  }
  
  
  /** Current player just resigned. */
  private void resign() {
    if (!club.hasGame()) {
      club.state("No game to resign.");
      club.sound("cmderr");
    } else if (gameState()=='b') {
      club.state("Game has not yet begun.");
      club.sound("cmderr");
    } else {
      club.state("Sending resignation...");
      club.send("resign "+club.table); // sync bug?
      club.state("OK.");
      gameCanvas.endGame();
    }
  }

  // Invoked via Game->Start or gameCanvas.mouseDown
  public boolean startGame() {
    if (role!=1)
      club.state("Blue player must issue Game Start.");
    else if (!bothPlayers())
      club.state("Both players must be here to start.");
    else if (gameState()=='r')
      club.state("Current game must be completed.");
    else if (gameState()=='e')
      club.state("Current game must be Reset.");
    else {
      club.send("start "+club.table);
      club.state("Sent Start request.");
      return true;
    }
    club.sound("cmderr");
    return false;
  }
  public void processStart(String table) {
    if (club.myTable(table,"start")) {
      gameCanvas.startGame();
    }
  }
  
  /** Game canvas just signaled end of game.
   * Else setup got resign in move list.
   */
  public void endGame() {
    gameCanvas.gameState = 'e'; // end
    club.redPlayer.stop(-1);
    club.bluePlayer.stop(-1);
  }
  
  protected void finalize() {
    gameCanvas=null;
    //dispose();
  }

  // ----------------------------------------------------------------
  // Use action() instead to save time
  public boolean handleEvent(Event event) {
    // Event fields: id, arg, target
    if (event.id==Event.KEY_ACTION || event.id==Event.KEY_PRESS)
      return club.handleKey(event);
    if (event.id==Event.ACTION_EVENT) {
      if (event.arg=="home")
	club.gotoLobby();
      else if (event.arg=="who")
	club.who(true);		// use selection
      else if (event.arg=="new")
	club.requestOpen();
      else if (event.arg=="goto")
	club.tables(true);
      else if (event.arg=="first")	// VCR button?
	gameCanvas.posFirst();
      else if (event.arg=="prev")
	gameCanvas.posPrev();      
      else if (event.arg=="next")
	gameCanvas.posNext();      
      else if (event.arg=="last")
	gameCanvas.posLast();
      else if (event.arg=="save")
	club.saveGame();
      else if (event.arg=="web")
	club.webOpen();
      return true;
    }
    else
      return false; //super.handleEvent(event);
  }

  // -----------------------------------------------------------------
  private MenuItem menuItemDisabled(String label) {
    MenuItem menu = new MenuItem(label);
    menu.disable();
    return menu;
  }

  private RadioMenu pieceMenu, backgrMenu, 
    gameTimerMenu, moveTimerMenu, languageMenu;
  private CheckboxMenuItem practiceCheckbox, soundsCheckbox, hintsCheckbox;

  private static String[] pieceMenuChoices = {
    "Chinese", "Inverted", "Graphic", "Roman"
    //"Beijing","Alternate","Common","Graphic",
    //"Western","International","English"
  };
  private static String[] backgrMenuChoices = {
    "Plain",
    "Cloth", "Sand", "Wood", "Marble", "Rock", "Paper", "Canvas", "Plaster",
    "Stucco", /// "Water" crashes?!
    "Mystery"
  };
  private static String[] languageMenuChoices = {
    "English", "China", "Taiwan"
  };
  private static String[] gameTimerMenuChoices = {
    "5", "10", "15", "20", "30", "45", "60", "90", "120"
  };
  private static String[] moveTimerMenuChoices = {
    "1", "3", "6", "9", "15", "30"
  };
  
  public Menu buildGameMenu() {
    Menu menu = new Menu(Club.msg("gGame")); {
      menu.add(new MenuItem(Club.msg("mStart")));
      menu.add(new MenuItem(Club.msg("mReset")));
      menu.add(new MenuItem(Club.msg("mDraw")));
      menu.add(new MenuItem(Club.msg("mResign")));
      menu.addSeparator();      
      //menu.add(new MenuItem(Club.msg("mFlip")));
      //menu.add(new MenuItem(Club.msg("mList")));
      menu.add(new MenuItem(Club.msg("mSync")));
      //menu.addSeparator();      
      menu.add(new MenuItem(Club.msg("mSave")));
    }
    return menu;
  }

  public boolean practice() {
    return gameCanvas.practice;
  }

  public Menu buildOptionsMenu() {
    Menu menu = new Menu(Club.msg("gOptions"));
    gameTimerMenu = new RadioMenu(Club.msg("gGameTimer"),
				  gameTimerMenuChoices,4,menu); 
    moveTimerMenu = new RadioMenu(Club.msg("gMoveTimer")
				  ,moveTimerMenuChoices,1,menu); 
    pieceMenu = new RadioMenu(Club.msg("gPieces"),
			      pieceMenuChoices, 0, menu);
    backgrMenu = new RadioMenu(Club.msg("gBoard"),
			       backgrMenuChoices, 0, menu);
    languageMenu = new RadioMenu(Club.msg("gLanguage"),
				 languageMenuChoices, 0, menu);

    hintsCheckbox = new CheckboxMenuItem(Club.msg("mHints"));
    menu.add(hintsCheckbox);
    soundsCheckbox = new CheckboxMenuItem(Club.msg("mSounds"));
    menu.add(soundsCheckbox);
    practiceCheckbox = new CheckboxMenuItem(Club.msg("mPractice"));
    menu.add(practiceCheckbox);
    return menu;
  }

  // -----------------------------------------------------------------
  private static final String NOGAME = "Use the Club menu to start a game.";

  /** Handle Game and Options menus.
   * Called from Club.handleEvent()
   */
  public boolean handleMenu(Event event) {
    // Game: Reset, Draw, Resign, List.
    //Club.debugEvent("GamePanel handleMenu",event);
    String sound="cmderr";	// assume error
    if (event.arg==Club.msg("mStart")) {
      startGame();
      return true;
    }
    if (event.arg==Club.msg("mReset")) {
      if (!club.hasGame())
	club.state(NOGAME);
      else if (gameState()=='r')
	club.state("Cannot Reset while game in progress.");
      else if (gameState()=='b' && role!=0)
	club.state("Only red player can Reset before a game.");
      else if (!bothPlayers())
	club.state("Can only Reset with two players.");
      else {
	String p1 = club.redPlayer.getName(); // current player1
	String p2 = club.bluePlayer.getName(); // current player2
	club.state("Sending reset request...");
	// Reset must always switch turns!
	club.send("reset "+club.table+" "+p2+" "+p1);
	club.state("OK");
	return true;
      }
      club.sound("cmderr");
      return true;
    }
    if (event.arg==Club.msg("mDraw")) {
      if (!club.hasGame())
	club.state(NOGAME);
      else if (role>=2)
	club.state("Observer, cannot request Draw.");
      else if (gameState()!='r')
	club.state("Game is not running.");
      else {
	club.state("Requesting draw...");
	club.send("draw "+club.table);
	// 1st broadcast to table: draw request paul at 1
	// 2nd broadcast to all:   draw karl vs. paul at 1
	sound = null;
      }
      club.sound(sound);
      return true;
    }
    if (event.arg==Club.msg("mList")) {
      if (!club.hasGame())
	club.state(NOGAME);
      else if (gameState()=='b')
	club.state("Game has not yet begun.");
      else {
	//Stickup.list(club,"Move List - Table "+club.table,"list "+club.table);
	sound = null;
      }
      club.sound(sound);
      return true;
    }
    if (event.arg==Club.msg("mSync")) {
      if (!club.hasGame()) {
	club.state(NOGAME);
	club.sound(sound);
      } else {
	gameSync();
      }
      return true;
    }
    if (event.arg==Club.msg("mSave")) {
      club.saveGame();
      return true;
    }
    if (event.arg==Club.msg("mResign")) {
      if (role>=2) {
	club.state("Observer, cannot Resign.");
	club.sound("cmderr");
      } else
	resign();
      return true;
    }
    if (event.arg==Club.msg("mFlip")) {
      if (!club.hasGame()) {
	club.state(NOGAME);
	club.sound(sound);
      } else
	gameCanvas.flip();
      return true;
    }

    // -----------------------------------------------------------------
    // Options: Timer, Pieces, Board
    //  TBD: Level, Rated, Rules, Hints, Private
    if (gameTimerMenu.handle(event)) {
      handleTimerMenu(gameTimerMenu);
      return true;
    }
    if (moveTimerMenu.handle(event)) {
      handleTimerMenu(moveTimerMenu);
      return true;
    }
    if (pieceMenu.handle(event)) {
      if (!club.hasGame()) {
	club.state(NOGAME);
	club.sound("cmderr");
      } else {
	gameCanvas.loadPieceImages(pieceMenu.selected());
      }
      return true;
    }
    if (backgrMenu.handle(event)) {
      /* IE4 bug, event.arg is true:
      gameCanvas.loadBackground((String)event.arg);
      */
      gameCanvas.loadBackground(backgrMenu.selected());
      return true;
    }
    if (languageMenu.handle(event)) {
      Club.msgLanguage(languageMenu.selected());
      return true;
    }
    if (event.target==soundsCheckbox) {
      club.sounds = soundsCheckbox.getState();
      if (club.sounds)
	club.sound("hello");
      else
	club.soundClips.clear(); // hack clear cache
      club.status("Sound effects are now: "+
		  ((club.sounds) ? "ON" : "OFF"));
      return true;
    }
    if (event.target==hintsCheckbox) {
      gameCanvas.hints = hintsCheckbox.getState();
      club.status("Move hints are now: "+
		  ((gameCanvas.hints) ? "ON" : "OFF"));
      return true;
    }
    if (event.target==practiceCheckbox) {
      if (role>0 || gameState()!='b') { 
	practiceCheckbox.setState(!practiceCheckbox.getState());      
	club.state("Practice mode must be set by red player at game start.");
	club.sound("cmderr");
      } else {
	gameCanvas.practice = practiceCheckbox.getState();
	club.send("practice @"+club.table+
		  (gameCanvas.practice ? " on" : " off"));
      }
    }
    return false;
  }
}
