import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.Animator.RepeatBehavior;
import org.jdesktop.animation.timing.interpolation.Interpolator;
import org.jdesktop.animation.timing.interpolation.KeyFrames;
import org.jdesktop.animation.timing.interpolation.KeyTimes;
import org.jdesktop.animation.timing.interpolation.KeyValues;
import org.jdesktop.animation.timing.interpolation.PropertySetter;
import org.jdesktop.animation.timing.interpolation.SplineInterpolator;
import org.jdesktop.animation.timing.triggers.ActionTrigger;
/*
 * MultiStepRace.java
 *
 * Created on May 3, 2007, 2:45 PM
 *
 * Copyright (c) 2007, Sun Microsystems, Inc
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the TimingFramework project nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * The full-blown demo with all of the bells and whistles.  This one uses
 * the facilities shown in all of the other variations, but adds 
 * both multi-step and non-linear interpolation.  It does this by
 * creating a KeyFrames object to hold the times/values/splines
 * used for each segment of the race.  It also adds an animation for
 * the rotation of the car (since the car should turn as it goes around the
 * curves) and sound effects (just to go completely overboard).
 *
 * @author Chet
 */
public class MultiStepRace {
    
    protected Animator animator;
    private SoundEffects soundEffects;
    public static final int RACE_TIME = 10000;
    
    
    /** Creates a new instance of BasicRace */
    public MultiStepRace(String appName) {
        RaceGUI basicGUI = new RaceGUI(appName);
        
        // We're going to need a more involved PropertyRange object
        // that has all curves of the track in it, as well as 
        // non-linear movement around the curves
        Point values[] = {
            TrackView.START_POS,
            TrackView.FIRST_TURN_START, TrackView.FIRST_TURN_END,
            TrackView.SECOND_TURN_START, TrackView.SECOND_TURN_END,
            TrackView.THIRD_TURN_START, TrackView.THIRD_TURN_END,
            TrackView.FOURTH_TURN_START, 
            TrackView.START_POS};
        KeyValues keyValues = KeyValues.create(values);
        // Calculate the keyTimes based on the distances that must be
        // traveled on each leg of the journey
        double totalDistance = 0;
        double segmentDistance[] = new double[values.length];
        for (int i = 0; i < (values.length - 1); ++i) {
            segmentDistance[i] = values[i].distance(values[i + 1]);
            totalDistance += segmentDistance[i];
        }
        segmentDistance[(values.length-1)] = 
                values[(values.length - 1)].distance(values[0]);
        totalDistance += segmentDistance[(values.length-1)];
        float times[] = new float[values.length];
        float elapsedTime = 0.0f;
        times[0] = 0.0f;
        times[values.length - 1] = 1.0f;
        for (int i = 0; i < (values.length - 2); ++i) {
            times[i + 1] = elapsedTime + (float)(segmentDistance[i] / totalDistance);
            elapsedTime = times[i + 1];
        }
        KeyTimes keyTimes = new KeyTimes(times);

        // For realistic movement, we want a big acceleration
        // on the straightaways
        Interpolator initialSpline = new SplineInterpolator(1.00f, 0.00f, 0.2f, .2f);
        Interpolator straightawaySpline = new SplineInterpolator(0.50f, 0.20f, .50f, .80f);
        Interpolator curveSpline = new SplineInterpolator(0.50f, 0.20f, .50f, .80f);
        Interpolator finalSpline = new SplineInterpolator(0.50f, 0.00f, .50f, 1.00f);
        KeyFrames keyFrames = new KeyFrames(keyValues, keyTimes,
                initialSpline, curveSpline, straightawaySpline, curveSpline,
                straightawaySpline, curveSpline,
                straightawaySpline, finalSpline);
        // This PropertySetter enables the animation for the car movement all 
        // the way around the track
        PropertySetter modifier = new PropertySetter(basicGUI.getTrack(), 
                "carPosition", keyFrames);
        animator = new Animator(RACE_TIME, Animator.INFINITE,
                RepeatBehavior.LOOP, modifier);
        
        // Now create similar keyframes for rotation of car
        keyValues = KeyValues.create(360, 315, 270, 225, 180, 135, 90, 45, 0);
        Interpolator straightawayTurnSpline = new SplineInterpolator(1.0f, 0.0f, 1.0f, 0.0f);
        Interpolator curveTurnSpline = new SplineInterpolator(0.0f, 0.5f, 0.5f, 1.0f);
        keyFrames = new KeyFrames(keyValues, keyTimes, 
                straightawayTurnSpline, curveTurnSpline, 
                straightawayTurnSpline, curveTurnSpline, 
                straightawayTurnSpline, curveTurnSpline, 
                straightawayTurnSpline, curveTurnSpline);
        modifier = new PropertySetter(basicGUI.getTrack(), "carRotation", 
                keyFrames);
//        animator.addTarget(modifier);
        
        // Finally, add sound effects, triggered by the same animator
        soundEffects = new SoundEffects(keyFrames);
        animator.addTarget(soundEffects);
        
        // Instead of manually tracking the events, have the framework do
        // the work by setting up a trigger
        JButton goButton = basicGUI.getControlPanel().getGoButton();
        JButton stopButton = basicGUI.getControlPanel().getStopButton();
        ActionTrigger trigger = ActionTrigger.addTrigger(goButton, animator);
        stopButton.addActionListener(new Stopper(animator));
    }

    /**
     * Handle clicks on the Stop button to stop the race
     */
    private class Stopper implements ActionListener {
        Animator timer;
        Stopper(Animator timer) {
            this.timer = timer;
        }
        public void actionPerformed(ActionEvent ae) {
            timer.stop();
        }
    }

    public static void main(String args[]) {
        Runnable doCreateAndShowGUI = new Runnable() {
            public void run() {
                MultiStepRace race = new MultiStepRace("Multi Step Race");
            }
        };
        SwingUtilities.invokeLater(doCreateAndShowGUI);
    }
    
}
