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 and then using those states to do
 * move operations on a paintable object.
 * 
 * This demo is not entirely satisfactory since:
 *   a. there is flicker in the area where we echo the keys
 *      that are pressed
 *   b. the movement of the paintable square is not uniform
 *      ... sometimes there are skips or jumps
 */
public class KeyTrackerDemo1
    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);
    
    
    public static int SIZE = 400;
    
    BufferedPanel window = new BufferedPanel(SIZE, SIZE);
    
    PaintableSequence sequence = window.getPaintableSequence();
    
    XRect shape = new XRect(100, 100, 50, 50);
    
    Paintable paintable = new ShapePaintable
        (shape, PaintMode.FILL, Colors.blue);
    
    
    /** The main panel stuff. */
    private Object[][] mainStuff = { { tilePanel, window } };
    
    /** 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 = 100;
    
    
    /** 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 KeyTrackerDemo1() {
        buildAndFrameGUI();
        startListeners();
    }
    
    
    /** Build the GUI. */
    private void buildAndFrameGUI() {
        // build GUI
        makeTiles();
        initializeWindow();
        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();
            movePaintableViaKey();
            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));
    }
    
    
    void initializeWindow() {
        window.drawGrid(50);
        sequence.addPaintable(paintable);
        
        window.repaint();
    }
    
    
    /** The code to move the paintable using the state
     * of up arrow, down arrow, left arrow, right arrow,
     * and shift.
     */
    void movePaintableViaKey() {
        // set delta to 10 if the shift key is down
        // otherwise set delta to 1
        int small = 1;
        int large = 10;
        
        int delta = (listener.getState(VK_SHIFT)) ? large : small;
        
        // compute x, y shift based on the state of
        // the arrow keys
        int x = 0;
        int y = 0;
        
        if (listener.getState(VK_UP)) {
            y -= delta;
        }
        
        if (listener.getState(VK_DOWN)) {
            y += delta;
        }
        
        if (listener.getState(VK_LEFT)) {
            x -= delta;
        }
        
        if (listener.getState(VK_RIGHT)) {
            x += delta;
        }
        
        if ((x != 0) || (y != 0)) {
            paintable.move(x, y);
            window.repaint();
        }
    }
    
    
    /** The main program that launches the demo. */
    public static void main(String[] args) {
        new KeyTrackerDemo1();
    }
    
}
