// TT4DApplet.java
 
//so that I can invoke applet viewer on the source file
//<APPLET code="TT4DApplet.class" width=400 height=430></APPLET>

import java.awt.*;
import java.applet.*;

/** 
 *
 * @author  Sean Bridges
 * @version 1.0.1
 *
 * The TT4DApplet allows you to play a game of 4D Tic Tac Toe.
 * 
 * Applet size should be width = 400, height = 430.
 *
 */
public class TT4DApplet extends Applet 
{
  
//---------------------------------------
    //class variables

    final static String ABOUT = "4D Tic Tac Toe, created by Sean Bridges, www.oocities.org/sbridges.geo";
    
    final static String L1 = "100%";
    final static String L2 = "80%";
    final static String L3 = "50%";
    final static String L4 = "20%";
    final static String L5 = "10%";

//---------------------------------------
    //instance variables
  
    private Button restart;
    private Choice levels;
    private ImagePanel imagePanel;

    private AllPossibleMovesPlayer computer;
    private AsynchronousPlayer human; 
    private TT4DBoard board;
    private GameMaster gameMaster;
  
  
    
//---------------------------------------
    //instance methods
  
  
//---------------------------------------
    //applet methods

    /** Initializes the applet TT4DApplet */
    public void init() 
    {
        System.out.println("TT4DApplet initializing");
        System.out.println(ABOUT);
        
        //if the init method is called more than once, 
        //then remove all will remove the components that were 
        //previously contained on the applet.
        //Done mainly so that the appletviewer restart works.
        removeAll();
    
        //create the game
        computer = new AllPossibleMovesPlayer( TT4DBoard.O_PLAYER_STRING, TT4DBoard.O_PLAYER_NUMBER);
        human = new AsynchronousPlayer(TT4DBoard.X_PLAYER_STRING, TT4DBoard.X_PLAYER_NUMBER);
        board = new TT4DBoard();
        Player[] players = new Player[2];
        players[0] = human;
        players[1] = computer;
    
        gameMaster = new GameMaster(board, players);
    
    
        //lay out the components
    
        this.setLayout(new BorderLayout());
        
    
        //the control panel has the level drop down list and
        //the restart button
    
        Panel controlPanel = new Panel();
        controlPanel.setLayout(new FlowLayout() );

        restart = new Button("restart");
        levels = new Choice();
        levels.add(L1);
        levels.add(L2);
        levels.add(L3);
        levels.add(L4);
        levels.add(L5);

        controlPanel.add(restart);
        controlPanel.add(new Label("              Level"));
        controlPanel.add(levels);
     
        this.add(controlPanel, BorderLayout.SOUTH);
    
        //the image panel draws the board.
        imagePanel = new ImagePanel(gameMaster, board, computer, human);
        this.add(imagePanel, BorderLayout.CENTER);
   
    }

    public void start()
    {
        System.out.println("TT4DApplet starting");
        gameMaster.startGame();
        this.repaint();
    }
  
    public void stop()
    {
        System.out.println("TT4DApplet stopping");
        gameMaster.stopGame();
    }

    public String getAppletInfo()   
    {
        return ABOUT;
    }

//------------------------------------------------
    //event processing
    
    public boolean action (Event evt, Object arg)
    {
        if(arg.equals("restart"))
        {
            gameMaster.restartGame();   
        }
        else if(evt.target.equals(levels))
        {
            if(levels.getSelectedItem() == L1)
            {
                computer.setFractionOfMovesTried(1.0f);
            }
            else if(levels.getSelectedItem() == L2)
            {
                computer.setFractionOfMovesTried(0.8f);
            }
            else if(levels.getSelectedItem() == L3)
            {
                computer.setFractionOfMovesTried(0.5f);
            }
            else if(levels.getSelectedItem() == L4)
            {
                computer.setFractionOfMovesTried(0.2f);
            }
            else if(levels.getSelectedItem() == L5)
            {
                computer.setFractionOfMovesTried(0.1f);
            }
        
        }
        else
        {
            return super.action(evt,arg);
        }
        return true;

    }




}//end class TT4DApplet

/**
 * The image panel is responsible for drawing the game board.
 */
 
class ImagePanel extends Panel implements GameEventListener
{

//--------------------------------
  //class variables
    public final static int ORIGIN_X = 30;
    public final static int ORIGIN_Y = 50;

    public final static int SCORE_X = 20;
    public final static int SCORE_Y = 30;

    public final static int SCORE_WIDTH = 420;
    public final static int SCORE_HEIGHT = 30;

    public final static int BOX_HEIGHT = 30;
    public final static int BOX_WIDTH = 30;
  
    public final static int BOARD_WIDTH = 3 * BOX_HEIGHT;
    public final static int BOARD_HEIGHT = 3 * BOX_WIDTH;

    public final static int BOARD_VERTICAL_GAP = 30;
    public final static int BOARD_HORIZONTAL_GAP = 30;

    final static int X_OFFSET = 11;
    final static int Y_OFFSET = 20;

    final static Color LINE_COLOR = Color.black;
    final static Color X_COLOR = Color.black;
    final static Color O_COLOR = Color.blue;
    final static Color EMPHASIS_COLOR = Color.red;
    final static Color TEXT_COLOR = Color.black;
  
    final static Font TOKEN_FONT = new Font("Serif", Font.PLAIN, 16);
    final static Font SCORE_FONT = new Font("Serif", Font.PLAIN, 22);

//----------------------------------------
    //instance variables
  
    //drawing is done to a buffer offscreen so that the user doesnt see 
    //the screen flicker
    private Image offscreenImage;
    private Graphics offscreenGraphics;
  
    private TT4DMove lastOMove;
  
    private GameMaster game;
    private Player computer;
    private AsynchronousPlayer human;
    private Board board;
  
  
//----------------------------------------
    //constructors

    ImagePanel(GameMaster aGame, Board aBoard, Player computer, AsynchronousPlayer human)
    {
        super();
        game = aGame;
        board = aBoard;
        this.computer = computer;
        this.human = human;
        game.addListener(this);
    }

  
//----------------------------------------
    //instance methods
  
  

    public void paint(Graphics g)
    { 
        g.drawImage(offscreenImage,0,0, this);
    }
  
    public void update()
    {
        Graphics g = this.getGraphics();
        g.drawImage(offscreenImage,0,0, this);
    }

//----------------------------------------
    //mouse interaction
  
    /**
     * If the user clicked on a square, find out where,
     * and inform the appropiate player.
     */
    public boolean mouseDown(java.awt.Event event, int mouseX, int mouseY)
    {
        for(int x = 0; x<3; x++)
        {
            for(int y = 0; y <3; y++ )
            {
                for(int z = 0; z <3; z++ )
                {
                    for(int w = 0; w <3; w++ )
                    {
                        //check to see if the mouse was clicked in a box
              
                        int leftX = positionToX(x,w);
                        int topY = positionToY(y,z);
                        int rightX = leftX + BOX_WIDTH;
                        int bottomY = topY + BOX_HEIGHT;
                
                        if( ( mouseX > leftX && mouseX < rightX) &&
                            (mouseY > topY && mouseY < bottomY))
                        {
                            TT4DPoint p = new TT4DPoint(x,y,z,w);
                            TT4DMove m = new TT4DMove(human, p);
                            human.makeMove(m);
                            return true;
                
                        }
            
        
                    }//end for w
                }//end for z
            }//end for y
        }//end for x
    
        return true;
    }

  
  
//----------------------------------------
    //drawing
  
    private void resetOffScreen()
    {
        offscreenImage = createImage(this.getSize().width, this.getSize().height);
        offscreenGraphics = offscreenImage.getGraphics(); 
        drawBoard();
        drawScore();
        update();
    }

    private int positionToX(int x, int w)
    {
        return ORIGIN_X +
        (BOX_WIDTH * x) +
        ((BOARD_WIDTH + BOARD_VERTICAL_GAP) * w); 
    
    }
  
    private int positionToY(int y,  int z)
    {
        return ORIGIN_Y +
        (BOX_HEIGHT * y) +
        ((BOARD_HEIGHT + BOARD_HORIZONTAL_GAP) * z);   
    }
  
    /**
     * Returns the top left corner x position of the point.
     */
    private int pointToX(TT4DPoint p)
    {
        return positionToX(p.getX(), p.getW() );
    }
  
    /**
     * Returns the top left corner y position of the point.
     */
    private int pointToY(TT4DPoint p)
    {
        return positionToY(p.getY(), p.getZ() );
    }
  
    private void clearPoint(TT4DPoint p)
    {
        offscreenGraphics.clearRect(pointToX(p) + 1, pointToY(p) + 1, BOX_HEIGHT - 1, BOX_WIDTH -1);
    }
  
  
    
    /**
     * Draw the given string at the given point.  Draw to offscreen, 
     * must call update before it is visible.
     */
    private void drawToken(TT4DPoint p, String s, Color c )
    {
        offscreenGraphics.setFont(TOKEN_FONT);
        offscreenGraphics.setColor(c);
        offscreenGraphics.drawString(s,pointToX(p) + X_OFFSET, pointToY(p) + Y_OFFSET);
    }
  
    /**
     * Draws the board offscreen.  Must call update before it is visible
     */
    private void drawBoard()
    {
        offscreenGraphics.setColor(LINE_COLOR);
        draw4dBoard(ORIGIN_X,ORIGIN_Y);    
    }
  
    private void draw4dBoard(int x, int y)
    {
        draw3dBoard(x,y);
        draw3dBoard(x + BOARD_WIDTH + BOARD_HORIZONTAL_GAP, y);
        draw3dBoard(x + (2* (BOARD_WIDTH + BOARD_HORIZONTAL_GAP) ), y);
    }

    private void draw3dBoard(int x, int y) 
    {
        draw2dBoard(x,y);
        draw2dBoard(x, y + BOARD_HEIGHT + BOARD_VERTICAL_GAP);
        draw2dBoard(x, y + (2* (BOARD_HEIGHT + BOARD_VERTICAL_GAP) ));
    }


    private void draw2dBoard(int x, int y)
    {
        //draw vertical lines

        offscreenGraphics.drawLine(x + BOX_WIDTH,y, 
                                   x + BOX_WIDTH, y + (3 * BOX_HEIGHT) );
        offscreenGraphics.drawLine(x + (2* BOX_WIDTH) ,y, 
                                   x + (2*BOX_WIDTH), y + (3 * BOX_HEIGHT) );

        //draw horizontal lines

        offscreenGraphics.drawLine(x, y + BOX_HEIGHT, 
                                   x + (3 * BOX_WIDTH), y + BOX_HEIGHT );
        offscreenGraphics.drawLine(x ,y + (2 * BOX_HEIGHT), 
                                   x + (3*BOX_WIDTH), y + (2 * BOX_HEIGHT) );

    }
  
    /**
     * Erases the old score and draws the new score offscreen.
     * Must call update before it's seen.
     */
    private void drawScore()
    {
    
        offscreenGraphics.clearRect(SCORE_X, SCORE_Y - SCORE_HEIGHT, SCORE_WIDTH, SCORE_HEIGHT);
    
        BoardStats stats = board.getBoardStats();   
        offscreenGraphics.setFont(SCORE_FONT);
        offscreenGraphics.setColor(TEXT_COLOR);
        offscreenGraphics.drawString( 
             "   X's Score " + stats.getScore(human) + "            " +
             "O's Score " + stats.getScore(computer), SCORE_X, SCORE_Y);
 
    }

//-----------------------------------------------
    //GameEventListenerMethods

  /** The game has started.
   */
    public void gameStarted() 
    {
        lastOMove = null; 
        resetOffScreen();
    }
    /** The game has stopped
    */
    public void gameStoped() 
    {//we just dont really care
    }

    /**
     * The game has been restarted.
     */
    public void gameRestarted() 
    {
        lastOMove = null; 
        resetOffScreen();
    }
    
    /**
     * A player has moved.
     */
    public void moveMade(Move aMove) 
    {
        TT4DMove move = (TT4DMove) aMove;
    
    
        if(move.maker() == computer)
        {
            if(lastOMove != null)
            {
                //clear the highlited square, redraw the old token, draw the new one
                this.clearPoint(lastOMove.getPoint() );
                this.drawToken(lastOMove.getPoint(), lastOMove.maker().getName(), O_COLOR);
            }
       
            this.drawToken(move.getPoint(), move.maker().getName(), EMPHASIS_COLOR);
            lastOMove = move;
        }
        else //x Moved
        {
            this.drawToken(move.getPoint(), move.maker().getName(), X_COLOR);
        }
    
        drawScore();
    
        update();
    
    }
}//end class ImagePanel