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 avoids flicker in the area where we echo keys
 * by using a new class PaintableComponentLite.
 */
public class KeyTrackerDemo2
    extends DisplayPanel
{
    
    /** The default tile color. */
    private static final Color tileColor = Colors.burlywood;
    
    /** The highlight tile color. */
    private static final Color highColor = Colors.red;
    
    /** The tile panel background color. */
    private static final Color tilePanelColor = Colors.black;
    
    /** The gap between tiles and between panel items. */
    private 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 PaintableComponentLite tilePanel =
        new PaintableComponentLite(tileSequence);
    
    
    private static int SIZE = 400;
    
    private BufferedPanel window = new BufferedPanel(SIZE, SIZE);
    
    private PaintableSequence sequence = window.getPaintableSequence();
    
    private XRect shape = new XRect(100, 100, 50, 50);
    
    private 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.
     * 
     * The key press-release 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 keyPR_Listener =
        new KeyPressReleaseListener
            (VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_SHIFT);
    
    
    /**
     * The thread delay of the key press-release listener
     * action in milliseconds.
     */
    private static final int keyPR_Delay = 20;
    
    
    /** The key press-release action. */
    private SimpleAction keyPR_Action =
        new SimpleAction() {
            public void perform() { keyPR_Action(); }
    };
    
    
    /** The threaded key press-release action. */
    private ThreadedAction threaded_KeyPR_Action =
        new ThreadedAction(keyPR_Action);
    
    
    /** The move action. */
    private SimpleAction move_Action =
        new SimpleAction() {
            public void perform() { move_Action(); }
    };
    
    
    /** The threaded move action. */
    private ThreadedAction threaded_Move_Action =
        new ThreadedAction(move_Action);
    
    
    /**
     * The thread delay of the move action
     * in milliseconds.
     */
    private static final int move_Delay = 100;
    
    
    /** The frame of this panel. */
    JPTFrame frame = null;
    
    
    /** The constructor. */
    public KeyTrackerDemo2() {
        buildAndFrameGUI();
        startListenersAndThreads();
    }
    
    
    /** 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 keyPR_Listener thread
        frame = frame("Key Tracker Test");
    }
    
    
    /** 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);
    }
    
    
    /** Initialize the graphics window. */
    private void initializeWindow() {
        window.drawGrid(50);
        sequence.addPaintable(paintable);
        window.repaint();
    }
    
    
    /** Enable the key listener and starts the threads. */
    private void startListenersAndThreads() {
        // enable the keyPR_Listener
        mainPanel.setFocusable(true);
        mainPanel.requestFocusInWindow();
        mainPanel.addKeyListener(keyPR_Listener);
        
        // start the threaded actions
        threaded_KeyPR_Action.actionPerformed(null);
        threaded_Move_Action.actionPerformed(null);
    }
    
    
    /**
     * The key press-release action to perform periodically.
     * 
     * The action will in fact be performed in a separate thread.
     * 
     * It is performed only as long as the application frame is
     * showing.
     */
    private void keyPR_Action() {
        while (frame.isShowing()) {
            echoKeys();
            JPTUtilities.pauseThread(keyPR_Delay);
        }
    }
    
    
    /** 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(keyPR_Listener.getState(key));
    }
    
    
    /** Get the arrow pressed count from the key listener. */
    private int getArrowCount() {
        int count = 0;
        
        if (keyPR_Listener.getState(VK_UP))
            count++;
        
        if (keyPR_Listener.getState(VK_DOWN))
            count++;
        
        if (keyPR_Listener.getState(VK_LEFT))
            count++;
        
        if (keyPR_Listener.getState(VK_RIGHT))
            count++;
        
        return count;
    }
    
    
    /**
     * The key move action to perform periodically.
     * 
     * The action will in fact be performed in a separate thread.
     * 
     * It is performed only as long as the application frame is
     * showing.
     * 
     * This action uses a longer pause if it has moved an object
     * than if no arrow key is currently pressed and nothing has
     * been moved.
     */
    private void move_Action() {
        while(frame.isShowing()) {
            int arrowCount = getArrowCount();
                
            if (arrowCount == 0) {
                JPTUtilities.pauseThread(keyPR_Delay);
            }
            else {
                move();
                JPTUtilities.pauseThread(move_Delay);
            }
        }
    }
    
    
    /** The code to move the paintable using the state
     * of up arrow, down arrow, left arrow, right arrow,
     * and shift.
     */
    private void move() {
        // set delta to 10 if the shift key is down
        // otherwise set delta to 1
        int small = 1;
        int large = 10;
        
        int delta = (keyPR_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 (keyPR_Listener.getState(VK_UP)) {
            y -= delta;
        }
        
        if (keyPR_Listener.getState(VK_DOWN)) {
            y += delta;
        }
        
        if (keyPR_Listener.getState(VK_LEFT)) {
            x -= delta;
        }
        
        if (keyPR_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 KeyTrackerDemo2();
    }
    
}
