/*
 * Copyright (c) 2005-2009 Laf-Widget Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o 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. 
 *     
 *  o Neither the name of Laf-Widget Kirill Grouchnikov 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. 
 */
package org.jvnet.lafwidget.animation;

/**
 * Information on the state of the faded component.
 * 
 * @author Kirill Grouchnikov
 */
public class FadeState {
	/**
	 * Is used to create unique value for the {@link #id} field.
	 */
	protected static long counter;

	/**
	 * Unique ID.
	 */
	protected long id;

	/**
	 * Fade cycle.
	 */
	private float fadePosition;

	/**
	 * Indication whether it's a fade-in or fade-out.
	 */
	private boolean isFadingIn;

	/**
	 * Indication whether it's looping (infinite).
	 */
	private boolean isLooping;

	/**
	 * Indication whether the looping fade starts fading out after reaching the
	 * maximum value. Relevant only when {@link #isLooping} is <code>true</code>.
	 */
	private boolean isLoopingReverse;

	/**
	 * Indication how many loop cycles are left. Relevant only when
	 * {@link #isLooping} is <code>true</code>. Contains -1 for endless loop.
	 */
	private int loopCount;

	/**
	 * Indication whether the looping fade should stop at reaching the end of
	 * the fade-out cycle. Relevant only when {@link #isLooping} is
	 * <code>true</code>.
	 */
	private boolean toStopAtCycleBreak;

	/**
	 * Indication whether the component parent should be repainted on each fade
	 * cycle.
	 */
	private boolean toRepaintParent;

	/**
	 * The optional callback to be called on each fade cycle.
	 */
	private FadeTrackerCallback callback;

	/**
	 * Fade animation step.
	 */
	public FadeStep fadeStep;

	/**
	 * Fade animation kind.
	 */
	public FadeKind fadeKind;

	/**
	 * Simple constructor.
	 * 
	 * @param fadeKind
	 *            Fade animation kind.
	 * @param fadePosition
	 *            Fade cycle.
	 * @param isFadingIn
	 *            Indication whether it's a fade-in or fade-out.
	 * @param toRepaintParent
	 *            Indication whether the component parent should be repainted on
	 *            each fade cycle.
	 */
	public FadeState(FadeKind fadeKind, float fadePosition, boolean isFadingIn,
			boolean toRepaintParent) {
		super();
		this.fadeKind = fadeKind;
		this.fadePosition = fadePosition;
		this.isFadingIn = isFadingIn;
		this.toRepaintParent = toRepaintParent;
		this.id = FadeState.getId();
		this.isLooping = false;
		this.toStopAtCycleBreak = false;
		this.loopCount = -1;
	}

	/**
	 * Private constructor - to prevent creation from outer packages.
	 */
	private FadeState() {
		super();
	}

	/**
	 * Returns a copy.
	 * 
	 * @return Fade state copy.
	 */
	public FadeState getCopy() {
		FadeState result = new FadeState();
		result.fadeKind = fadeKind;
		result.fadePosition = fadePosition;
		result.isFadingIn = isFadingIn;
		result.toRepaintParent = toRepaintParent;
		result.id = this.id;
		result.isLooping = this.isLooping;
		result.isLoopingReverse = this.isLoopingReverse;
		result.toStopAtCycleBreak = this.toStopAtCycleBreak;
		result.fadeStep = this.fadeStep;
		result.callback = this.callback;
		result.loopCount = this.loopCount;
		return result;
	}

	/**
	 * Sets indication that looping fade should stop at the end of the fade-out
	 * cycle.
	 */
	public void toStopAtCycleBreak() {
		if (!this.isLooping)
			throw new IllegalArgumentException(
					"Supported only on looping fades");
		this.toStopAtCycleBreak = true;
	}

	/**
	 * Returns a unique ID.
	 * 
	 * @return Unique ID.
	 */
	protected static synchronized long getId() {
		return counter++;
	}

	/**
	 * Sets the callback.
	 * 
	 * @param callback
	 *            Callback.
	 */
	public void setCallback(FadeTrackerCallback callback) {
		if (FadeTracker.DEBUG_MODE) {
			System.out.println("Setting callback of " + this.id + " to "
					+ callback);
		}
		this.callback = callback;
	}

	/**
	 * Returns the application callback. The result may be <code>null</code>.
	 * 
	 * @return Possible <code>null</code> application callback.
	 */
	public FadeTrackerCallback getCallback() {
		return callback;
	}

	/**
	 * Sets the current fade position. Valid range is 0.0-1.0.
	 * 
	 * @param fadePosition
	 *            New fade position.
	 */
	public void setFadePosition(float fadePosition) {
		this.fadePosition = fadePosition;
	}

	/**
	 * Returns the current fade position.
	 * 
	 * @return The current fade position.
	 */
	public float getFadePosition() {
		return fadePosition;
	}

	/**
	 * Sets fade-in / fade-out indication.
	 * 
	 * @param isFadingIn
	 *            <code>true</code> if the current animation is fade-in,
	 *            <code>false</code> if the current animation is fade-out.
	 */
	public void setFadingIn(boolean isFadingIn) {
		this.isFadingIn = isFadingIn;
	}

	/**
	 * Returns the fade-in / fade-out indication.
	 * 
	 * @return <code>true</code> if the current animation is fade-in,
	 *         <code>false</code> if the current animation is fade-out.
	 */
	public boolean isFadingIn() {
		return isFadingIn;
	}

	/**
	 * Sets the looping indication.
	 * 
	 * @param isLooping
	 *            New value for the looping indication.
	 */
	public void setLooping(boolean isLooping) {
		this.isLooping = isLooping;
	}

	/**
	 * Returns the looping indication.
	 * 
	 * @return <code>true</code> if the animation is looping,
	 *         <code>false</code> otherwise.
	 */
	public boolean isLooping() {
		return isLooping;
	}

	/**
	 * Sets indication whether the looping fade starts fading out after reaching
	 * the maximum value.
	 * 
	 * @param isLoopingReverse
	 *            <code>true</code> if the looping animation should start
	 *            fading out after reaching the maximum value,
	 *            <code>false</code> otherwise.
	 */
	public void setLoopingReverse(boolean isLoopingReverse) {
		this.isLoopingReverse = isLoopingReverse;
	}

	/**
	 * Returns indication whether the looping fade starts fading out after
	 * reaching the maximum value.
	 * 
	 * @return <code>true</code> if the looping animation should start fading
	 *         out after reaching the maximum value, <code>false</code>
	 *         otherwise.
	 */
	public boolean isLoopingReverse() {
		return isLoopingReverse;
	}

	/**
	 * Sets indication whether the looping fade should stop at reaching the end
	 * of the fade-out cycle.
	 * 
	 * @param toStopAtCycleBreak
	 *            <code>true</code> if the looping indication should stop at
	 *            the next fade-out cycle end, <code>false</code> otherwise.
	 */
	public void setToStopAtCycleBreak(boolean toStopAtCycleBreak) {
		this.toStopAtCycleBreak = toStopAtCycleBreak;
	}

	/**
	 * Returns indication whether the looping fade should stop at reaching the
	 * end of the fade-out cycle.
	 * 
	 * @return <code>true</code> if the looping indication should stop at the
	 *         next fade-out cycle end, <code>false</code> otherwise.
	 */
	public boolean isToStopAtCycleBreak() {
		return toStopAtCycleBreak;
	}

	/**
	 * Sets indication whether the component parent should be repainted on each
	 * fade cycle.
	 * 
	 * @param toRepaintParent
	 *            <code>true</code> if the component parent should be
	 *            repainted on each fade cycle, <code>false</code> otherwise.
	 */
	public void setToRepaintParent(boolean toRepaintParent) {
		this.toRepaintParent = toRepaintParent;
	}

	/**
	 * Returns indication whether the component parent should be repainted on
	 * each fade cycle.
	 * 
	 * @return <code>true</code> if the component parent should be repainted
	 *         on each fade cycle, <code>false</code> otherwise.
	 */
	public boolean isToRepaintParent() {
		return toRepaintParent;
	}

	public int getLoopCount() {
		return loopCount;
	}

	public void setLoopCount(int loopCount) {
		this.loopCount = loopCount;
	}
}