/* @(#)Counter.java   1 April 2007 */

/* Useful imports */

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;


public class Counter
    extends DisplayPanel
    implements WindowConstants
{
    static final int FONT_SIZE = 96;
    
    static final String FONT_NAME =
        Fonts.getSansSerifFontFamilyName();
    
    static final Font FONT =
        new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
    
    static final TextPaintable sample =
        new TextPaintable
            ("00000000000", FONT, Colors.black, TextBounds.LOOSE,
              TextAnchor.RIGHT_DESCENTLINE, 0, 0);
    
    static final XRect bounds = sample.getBounds2D();
    
    static final int gap = 10;
    
    
    int number = 0;
    
    TextPaintable paintable =
        new TextPaintable
            ("0", FONT, Colors.black, TextBounds.LOOSE,
              TextAnchor.RIGHT_DESCENTLINE, 0, 0);
        
    Tile tile = new Tile(paintable);
    
    
    String[] speedNames = { "Slow", "Medium", "Fast", "Very Fast" };
    
    TableLayout speedLayout =
        new TableLayout(1, 4, gap, gap, CENTER);
    
    RadioPanel speedPanel =
        new RadioPanel(speedNames, speedLayout);
    
    
    /**
     * The object for animation synchronization.
     * 
     * When a thread synchronizes on this object,
     * the thread can complete its tasks before
     * another thread can change state. 
     */
    Object animationLock = new Object();
    
    volatile boolean running = false;
    
    
    SimpleAction runAction =
        new SimpleAction("Run") {
            public void perform() { runMethod(); }
    };
    
    
    SimpleAction stopAction =
        new SimpleAction("Stop") {
            public void perform() { stopMethod(); }
    };
    
    
    SimpleAction resetAction =
        new SimpleAction("Reset to 0") {
            public void perform() { resetMethod(); }
    };
    
    
    SimpleAction setAction =
        new SimpleAction("Set Counter in Dialog") {
            public void perform() { setMethod(); }
    };
    
    
    ThreadedAction runActionThreaded =
        new ThreadedAction(runAction);
    
    
    Object[] actionStuff =
        { runActionThreaded, stopAction, resetAction, setAction };
    
    HTable actionPanel =
        new HTable(actionStuff, gap, gap, CENTER);
    
    
    Object[] mainStuff = { tile, speedPanel, actionPanel };
    
    VTable mainPanel = new VTable(mainStuff, gap, gap, CENTER);
    
    
    JPTFrame frame = null;
    
    SimpleAction closingAction
        = new SimpleAction("Window Closing Action") {
            public void perform() { closingMethod(); }
    };
    
    
    /** The constructor. */
    public Counter() {
        tile.setDefaultBounds2D(bounds);
        mainPanel.emptyBorder(gap);
        animationState(false);
        
        addObject(mainPanel);
        
        createFrame();
    }
    
    
    /**
     * Returns the animation delay in milliseconds with
     * slow = 1000, medium = 250, fast = 100, very fast = 25.
     */
    int getDelay() {
        int index = speedPanel.getSelectedIndex();
        
        switch(index) {
            case 1:
                return 250;
                
            case 2:
                return 100;
                
            case 3:
                return 25;
                
            default:
                return 1000;
        }
    }
    
    
    /** Run the counter animation. */
    void runMethod() {
        animationState(true);
        
        while (running) {
            number++;
            update();
            
            JPTUtilities.pauseThread(getDelay());
        }
    }
    
    
    /** Stop the counter animation. */
    void stopMethod() {
        animationState(false);
    }
    
    
    /** Reset the counter to 0. */
    void resetMethod() {
        number = 0;
        update();
    }
    
    
    /** Set the counter to a value from a dialog box. */
    void setMethod() {
        synchronized(animationLock) {
            animationState(false);
            
            try {
                number = SimpleDialog.requestInt
                    ("Counter", "Set Counter", number + "");
                
                update();
            }
            catch (CancelledException ex) { };
        }
    }
    
    
    /** Update the paintable that displays the counter. */
    void update() {
        synchronized(animationLock) {
            paintable.setString(number + "");
        }
    }
    
    
    /** Set the animation state and run/stop buttons. */
    void animationState(boolean state) {
        synchronized(animationLock) {
            running = state;
            
            runAction.  setEnabled(! state);
            stopAction. setEnabled(  state);
        }
    }
    
    
    /**
     * The method to create the application frame and
     * install the window closing behavior.
     */
    private void createFrame() {
        frame = frame("Counter");
        setWindowClosingAction();
    }
    
    
    /**
     * The window closing method.
     *
     * This method is required to properly terminate
     * the animation thread if the window is closed
     * while the animation is running.
     */
    private void closingMethod() {
        synchronized (animationLock) {
            running = false;
            
            if (frame != null)
                frame.dispose();
        }        
    }
    
    
    /** The method to set the window closing action. */
    private void setWindowClosingAction() {
        // remove default closing operation
        frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        
        // set special closing operation
        WindowActionAdapter adapter = new WindowActionAdapter(frame);
        adapter.addWindowClosingAction(closingAction);
    }
    
    
    public static void main(String[] args) {
        new Counter();
    }
}

