import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.console.*;
import edu.neu.ccs.filter.*;
import edu.neu.ccs.jpf.*;
import edu.neu.ccs.parser.*;
import edu.neu.ccs.pedagogy.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import java.util.*;
import java.math.*;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.regex.*;
import java.text.ParseException;

/**
 * Demonstrates the tracking of key press-release states by
 * saving states for up arrow, down arrow, left arrow,
 * right arrow, and shift.  This choice is simply for demo
 * purposes since all keyboard keys may be tracked.
 * 
 * The key states of the 5 keys are reflected in the GUI by
 * 5 tiles placed in the shape of a large plus sign.  The
 * center tile corresponds to shift key and the outside
 * tiles to the 4 arrow key directions.
 * 
 * When a key is pressed, its tile becomes red.  When the
 * key is released, its tile returns to the default color
 * which is burlywood.  Multiple keys may be tracked and
 * it does not matter in what order the keys are pressed
 * or released.
 * 
 * If the frame is minimized then the key tracker will not
 * be able to track keys since it will lose input focus.
 * Hence the key tracker state when the frame is restored
 * will not necessarily be an accurate reflection of what
 * is happening on the keyboard.  This problem cannot be
 * avoided because Java does not directly track the keys
 * and the tools used in this demo rely on maintaining the
 * input focus.
 * 
 * To avoid this problem, do not have keys pressed as the
 * frame is minimized or as it is restored.
 */
public class KeyTrackerTest
    extends DisplayPanel
{
    
    /** The default tile color. */
    public static final Color tileColor = Colors.burlywood;
    
    /** The highlight tile color. */
    public static final Color highColor = Colors.red;
    
    /** The tile panel background color. */
    public static final Color tilePanelColor = Colors.black;
    
    /** The gap between tiles and between panel items. */
    public static final int gap = 4;
    
    
    /** The size of a tile. */
    private static final int tileSize = 50;
    
    /** The bounds of a tile. */
    private static final XRect tileBounds =
        new XRect(0, 0, tileSize, tileSize);
    
    
    /** The size of the tile panel. */
    private static final int tilePanelSize = 3 * tileSize + 4 * gap;
    
    /** The bounds of the tile panel. */
    private static final XRect tilePanelBounds =
        new XRect(0, 0, tilePanelSize, tilePanelSize);
    
    
    /** The blank tile cell. */
    private Paintable blank = null;
    
    
    /**
     * The 5 tiles in a 3x3 array.
     *
     * The only tiles will be in the cells with
     * row or col index equal to 1.
     * 
     * There will be a total of 5 non-null tiles
     * in the shape of a large plus sign.
     */
    private TileBox[][] tiles = new TileBox[3][3];
    
    /** The paintable sequence with the 5 tiles. */
    private PaintableSequence tileSequence = new PaintableSequence();
    
    /** The paintable component with the 5 tiles. */
    private PaintableComponent tilePanel = new PaintableComponent(tileSequence);
    
    
    /** The main panel stuff. */
    private Object[][] mainStuff = { { tilePanel } };
    
    /** The main panel. */
    private TablePanel mainPanel = new TablePanel(mainStuff, gap, gap, CENTER);
    
    
    /** Shortand for KeyEvent.VK_UP. */
    private static final int VK_UP    = KeyEvent.VK_UP;
    
    /** Shortand for KeyEvent.VK_DOWN. */
    private static final int VK_DOWN  = KeyEvent.VK_DOWN;
    
    /** Shortand for KeyEvent.VK_LEFT. */
    private static final int VK_LEFT  = KeyEvent.VK_LEFT;
    
    /** Shortand for KeyEvent.VK_RIGHT. */
    private static final int VK_RIGHT = KeyEvent.VK_RIGHT;
    
    /** Shortand for KeyEvent.VK_SHIFT. */
    private static final int VK_SHIFT = KeyEvent.VK_SHIFT;
    
    
    /**
     * The key press-release listener.
     * 
     * This listener is initialized with the keys we plan
     * to track in this demo.
     * 
     * Use the default constructor with no arguments to
     * be able to track all keys.
     */
    private KeyPressReleaseListener listener =
        new KeyPressReleaseListener
            (VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_SHIFT);
    
    
    /** The thread delay of the key action in milliseconds. */
    private static final int threadDelay = 50;
    
    
    /** The key action. */
    private SimpleAction keyAction =
        new SimpleAction() {
            public void perform() { keyAction(); }
    };
    
    
    /** The threaded key action. */
    private ThreadedAction threadedKeyAction =
        new ThreadedAction(keyAction);
    
    
    /** The frame of this panel. */
    JPTFrame frame = null;
    
    
    /** The constructor. */
    public KeyTrackerTest() {
        buildAndFrameGUI();
        startListeners();
    }
    
    
    /** Build the GUI. */
    private void buildAndFrameGUI() {
        // build GUI
        makeTiles();
        add(mainPanel);
        
        // keep track of the GUI frame
        // so we can tell when it is no longer showing
        // so we can terminate the key listener thread
        frame = frame("Key Tracker Test");
    }
    
    
    /** Start the listeners and threads. */
    private void startListeners() {
        // start the key listener
        mainPanel.setFocusable(true);
        mainPanel.requestFocusInWindow();
        mainPanel.addKeyListener(listener);
        
        // start the key listener action thread
        threadedKeyAction.actionPerformed(null);
    }
    
    
    /** Makes the blank block. */
    private void makeBlank() {
        blank = new ShapePaintable();
        blank.setDefaultBounds2D(tileBounds);
    }
    
    
    /** The code to build and space the 5 tiles. */
    private void makeTiles() {
        makeBlank();
        
        int spacing = tileSize + gap;
        
        for (int row = 0; row < 3; row++)
            for (int col = 0; col < 3; col++)
                if ((row == 1) || (col == 1)){
                    tiles[row][col] = new TileBox(blank);
                    tiles[row][col].setBackgroundPaint(tileColor);
                    
                    int x = gap + spacing * col;
                    int y = gap + spacing * row;
                    
                    tiles[row][col].move(x, y);
                    
                    tileSequence.appendPaintable(tiles[row][col]);
                }
        
        tileSequence.setDefaultBounds2D(tilePanelBounds);
        
        tilePanel.setBackground(tilePanelColor);
        tilePanel.setOpaque(true);
    }
    
    
    /**
     * The action to perform periodically based on the key state.
     * 
     * The action will in fact be performed in a separate thread.
     * 
     * It is performed only as long as the frame is showing.
     */
    private void keyAction() {
        while (frame.isShowing()) {
            echoKeys();
            JPTUtilities.pauseThread(threadDelay);
        }
    }
    
    
    /** The action to echo the state of the 5 keys in the GUI. */
    private void echoKeys() {
        tiles[0][1].setBackgroundPaint(getTileColor(VK_UP));
        tiles[1][0].setBackgroundPaint(getTileColor(VK_LEFT));
        tiles[1][2].setBackgroundPaint(getTileColor(VK_RIGHT));
        tiles[2][1].setBackgroundPaint(getTileColor(VK_DOWN));
        tiles[1][1].setBackgroundPaint(getTileColor(VK_SHIFT));
    }
    
    
    /**
     * Returns a tile color based on the given
     * boolean state.
     */
    private Color getTileColor(boolean state) {
        if (state)
            return highColor;
        else
            return tileColor;
    }
    
    
    /**
     * Returns a tile color based on looking up the
     * boolean state corresponding to the given key.
     */
    private Color getTileColor(int key) {
        return getTileColor(listener.getState(key));
    }
    
    
    /** The main program that launches the demo. */
    public static void main(String[] args) {
        new KeyTrackerTest();
    }
    
}
