/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.object.arrow;

import java.awt.Color;
import java.awt.Stroke;

import net.sf.jaxodraw.util.JaxoUtils;


/**
 * An arrow for a JaxoObject.
 *
 * <p>
 *   <b>Note</b>:
 *   Implementing sub-classes need to initialize the Stroke property during
 *   de-serialization if a default value is set in the constructor.
 * </p>
 *
 * @since 2.0
 */
public abstract class AbstractBaseArrow implements JaxoArrow {
    private static final long serialVersionUID = 2L;

    private Color color;
    private Color fillColor;
    private boolean filled;
    private transient Stroke stroke;
    private float arrowWidth;
    private float arrowLength;
    private float arrowInset;

    /** Returns the color of this arrow.
     * @return The color of this arrow.
     */
    public final Color getColor() {
        return color;
    }

    /** Sets the color of this arrow.
     * @param c The color of this arrow.
     */
    public final void setColor(final Color c) {
        this.color = c;
    }

    /** Returns the fill color of this arrow.
     * @return The fill color of this arrow.
     */
    public final Color getFillColor() {
        return fillColor;
    }

    /** Sets the fill color of this arrow.
     * @param c The fill color of this arrow.
     */
    public final void setFillColor(final Color c) {
        this.fillColor = c;
    }

    /** Determines if the arrow is filled.
     * @return True if the fillColor is used.
     */
    public final boolean isFilled() {
        return filled;
    }

    /** Determines if the arrow should be filled.
     * @param value True if the fillColor should be used.
     */
    public final void setFilled(final boolean value) {
        this.filled = value;
    }

    /** Returns the stroke of this arrow.
     * @return The stroke of this arrow.
     */
    public final Stroke getStroke() {
        return stroke;
    }

    /** Sets the stroke of this arrow.
     * @param newStroke The stroke property of this arrow.
     */
    public final void setStroke(final Stroke newStroke) {
        this.stroke = newStroke;
    }

    /** Returns the width of this arrow.
     * @return The width of this arrow.
     */
    public final float getArrowWidth() {
        return arrowWidth;
    }

    /** Sets the width of this arrow.
     * @param newWidth The new width, must be positive
     * otherwise an IllegalArgumentException is thrown.
     */
    public final void setArrowWidth(final float newWidth) {
        if ((newWidth < -0.0001f)) {
            throw new IllegalArgumentException("Arrow width negative!");
        }

        if (newWidth < 0.f) {
            this.arrowWidth = 0.f;
        } else {
            this.arrowWidth = newWidth;
        }
    }

    /** Returns the length of this arrow.
     * @return The length of this arrow.
     */
    public final float getArrowLength() {
        return arrowLength;
    }

    /** Sets the length of this arrow.
     * @param newLength The new length, must be positive
     * otherwise an IllegalArgumentException is thrown.
     */
    public final void setArrowLength(final float newLength) {
        if ((newLength < -0.0001f)) {
            throw new IllegalArgumentException("Arrow length negative!");
        }

        if (newLength < 0.f) {
            this.arrowLength = 0.f;
        } else {
            this.arrowLength = newLength;
        }
    }

    /** Returns the tail length of this arrow.
     * @return The tail length of this arrow, a number between 0 and 1.
     */
    public final float getArrowInset() {
        return arrowInset;
    }

    /** Sets the inset ratio of this arrow. This is the ratio of the inset
     * (or tail length) to the length of the arrow.
     * @param newInset The inset ratio, needs to be between 0 and 1,
     * otherwise an IllegalArgumentException is thrown.
     */
    public final void setArrowInset(final float newInset) {
        if ((newInset < -0.0001f) || (newInset > 1.0001f)) {
            throw new IllegalArgumentException(
                "Arrow inset out of range: " + newInset);
        }

        if (newInset < 0.f) {
            this.arrowInset = 0.f;
        } else if (newInset > 1.f) {
            this.arrowInset = 1.f;
        } else {
            this.arrowInset = newInset;
        }
    }

    /**
     * Returns an exact copy of the given JaxoArrow.
     * This implementation uses Object.clone(), so the only thing to
     * do in subclasses is to recursively clone mutable object properties.
     *
     * @return The copy of the given JaxoArrow.
     */
    public JaxoArrow copy() {
        try {
            return ((AbstractBaseArrow) super.clone());
        } catch (CloneNotSupportedException e) {
            throw new Error(e);
        }
    }

    /**
     * Checks if this Arrow is a copy of the given test Arrow.
     *
     * @param testArrow the Arrow to test against.
     * @return True if the two arrows are equal.
     */
    public boolean isCopy(final JaxoArrow testArrow) {
        boolean isCopy = false;

        if (!(testArrow instanceof AbstractBaseArrow)) {
            return false;
        }

        final AbstractBaseArrow baseArrow = (AbstractBaseArrow) testArrow;

        final boolean equalColors = (getColor() == null
                    ? baseArrow.getColor() == null
                    : getColor().equals(baseArrow.getColor()));

        final boolean equalFillColors = (getFillColor() == null
                    ? baseArrow.getFillColor() == null
                    : getFillColor().equals(baseArrow.getFillColor()));

        final boolean equalStroke = (getStroke() == null
                    ? baseArrow.getStroke() == null
                    : getStroke().equals(baseArrow.getStroke()));

        if (equalColors && equalFillColors && equalStroke
            && baseArrow.isFilled() == isFilled()
            && JaxoUtils.equal(baseArrow.getArrowWidth(), getArrowWidth())
            && JaxoUtils.equal(baseArrow.getArrowInset(), getArrowInset())
            && JaxoUtils.equal(baseArrow.getArrowLength(), getArrowLength())) {
            isCopy = true;
        }

        return isCopy;
    }
}
