/**
 *  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;

import net.sf.jaxodraw.object.arrow.JaxoArrow;
import net.sf.jaxodraw.object.arrow.JaxoDefaultArrow;
import net.sf.jaxodraw.util.JaxoPrefs;
import net.sf.jaxodraw.util.JaxoUtils;
import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;


/** The mother of all particle objects (arcs, lines and loops).
 * @since 2.0
 */
public abstract class JaxoParticleObject extends JaxoExtendedObject {

    /** Default frequency for photon objects. */
    public static final float PHOTON_FREQ = 0.05f;

    /** Default frequency for gluon objects. */
    public static final float GLUON_FREQ = 0.08f;

    /** Default dash for scalar objects. */
    public static final float SCALAR_DASH = 10.f;

    /** Default dash for ghost objects. */
    public static final float GHOST_DASH = 2.f;

    private static final long serialVersionUID = 2L;

    /** The dash size of this particle object (only for ghosts and scalars).*/
    private float dash;

    /** The amplitude for this particle object.
     * (only used for photons and gluons and zigzag lines).
     */
    private int amp;

    /** The number of wiggles of this particle object
     * (only used for photons and gluons and zigzag lines).
     */
    private int wiggles;

    /** A boolean variable that indicates whether to draw an arrow on
     * this particle object or not (not used for photons and gluons).
     */
    private boolean paintArrow;

    /** A boolean variable that indicates whether the arrow should be
     * flipped with respect to the initial orientation or not
     * (only used for fermions, ghosts and scalars).
     */
    private boolean flip;

    /** A float between 0 and 1 that indicates where along this
     * particle object the arrow should be positioned.
     */
    private float arrowPosition;

    /** A boolean variable that indicates whether this particle object
     * should be drawn with double lines.
     */
    private boolean doubleLine;

    /** The separation of the double lines. */
    private float dlSeparation;

    /** The arrow of this object. */
    private JaxoArrow arrow = new JaxoDefaultArrow();

    /**
     * Constructor. Adds a default arrow as a PropertyChangeListener.
     */
    public JaxoParticleObject() {
        super();
        this.addPropertyChangeListener(arrow);
    }


    /** {@inheritDoc} */
    @Override
    public JaxoObject copy() {
        final JaxoParticleObject copy = (JaxoParticleObject) super.copy();

        copy.arrow = arrow.copy();
        copy.addPropertyChangeListener(copy.arrow);

        return copy;
    }

    // Bean getter and setter methods

    /** Returns the wiggles property of this particle object.
     * @return The wiggles property of this particle object.
     */
    public final int getWiggles() {
        return wiggles;
    }

    /** Sets the wiggles property of this particle object.
     * @param newWiggles The wiggles property of this particle object.
     */
    public final void setWiggles(final int newWiggles) {
        final Integer old = Integer.valueOf(wiggles);
        this.wiggles = newWiggles;
        firePropertyChange("wiggles", old, Integer.valueOf(wiggles));
    }

    /** Returns the dash property of this particle object.
     * @return  The dash property of this particle object.
     */
    public final float getDash() {
        return dash;
    }

    /** Sets the dash property of this particle object.
     * @param newDash The dash property of this particle object.
     */
    public final void setDash(final float newDash) {
        final Float old = Float.valueOf(dash);
        this.dash = newDash;
        firePropertyChange("arrowPosition", old, Float.valueOf(dash));

        resetStroke();
    }

    /** Returns the amp property of this particle object.
     * @return The amp property of this particle object.
     */
    public final int getAmp() {
        return amp;
    }

    /** Sets the amp property of this particle object.
     * @param newAmp The amp property of this particle object.
     */
    public final void setAmp(final int newAmp) {
        final Integer old = Integer.valueOf(amp);
        this.amp = newAmp;
        firePropertyChange("amp", old, Integer.valueOf(amp));
    }

    /** Determines whether the arrow property of this particle object is set or not.
     * @return The arrow property of this particle object.
     */
    public final boolean isPaintArrow() {
        return paintArrow;
    }

    /** Sets the arrow property of this particle object.
     * @param arr The arrow property  of this particle object.
     */
    public final void setPaintArrow(final boolean arr) {
        final Boolean old = Boolean.valueOf(paintArrow);
        this.paintArrow = arr;
        firePropertyChange("paintArrow", old, Boolean.valueOf(paintArrow));
    }

    /** Returns the flip property  of this particle object.
     * @return The flip property  of this particle object.
     */
    public final boolean isFlip() {
        return flip;
    }

    /** Sets the  flip property of this particle object.
     * @param newFlip The flip property of this particle object.
     */
    public final void setFlip(final boolean newFlip) {
        final Boolean old = Boolean.valueOf(flip);
        this.flip = newFlip;
        firePropertyChange("flip", old, Boolean.valueOf(flip));
    }

    /** Returns the arrowPosition property  of this particle object.
     * @return The arrowPosition property  of this particle object.
     */
    public final float getArrowPosition() {
        return arrowPosition;
    }

    /** Sets the arrowPosition property of this particle object.
     * Throws IllegalArgumentException if the parameter is not between 0 and 1.
     * @param newArrowPosition The new arrowPosition property
     * measured as a float number between 0 and 1.
     */
    public final void setArrowPosition(final float newArrowPosition) {
        if ((newArrowPosition < -0.0001f) || (newArrowPosition > 1.0001f)) {
            throw new IllegalArgumentException();
        }

        final Float old = Float.valueOf(arrowPosition);
        this.arrowPosition = newArrowPosition;

        if (arrowPosition < 0.f) {
            arrowPosition = 0.f;
        }

        if (arrowPosition > 1.f) {
            arrowPosition = 1.f;
        }

        firePropertyChange("arrowPosition", old, Float.valueOf(arrowPosition));
    }

    /** Sets the double line property.
     * @param dline The double line boolean variable.
     */
    public final void setDoubleLine(final boolean dline) {
        final Boolean old = Boolean.valueOf(doubleLine);
        this.doubleLine = dline;
        firePropertyChange("doubleLine", old, Boolean.valueOf(doubleLine));
    }

    /** Returns the double line property.
     * @return The double line boolean variable of this object.
     */
    public final boolean isDoubleLine() {
        return doubleLine;
    }

    /** Sets the double line separation property.
     * @param dlsep The double line separation to be set.
     */
    public final void setDLSeparation(final float dlsep) {
        final Float old = Float.valueOf(dlSeparation);
        this.dlSeparation = dlsep;
        firePropertyChange("dlSeparation", old, Float.valueOf(dlSeparation));
    }

    /** Returns the double line separation of this object.
     * @return The double line separation.
     */
    public final float getDLSeparation() {
        return dlSeparation;
    }

    /** Returns the arrow of this particle object.
     * @return  The arrow of this particle object.
     */
    public final JaxoArrow getArrow() {
        return arrow;
    }

    /** Sets the dash property of this particle object.
     * @param newArrow The arrow of this particle object.
     */
    public final void setArrow(final JaxoArrow newArrow) {
        this.removePropertyChangeListener(arrow);

        final JaxoArrow old = arrow;
        this.arrow = newArrow;
        firePropertyChange("arrow", old, arrow);

        this.addPropertyChangeListener(arrow);
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCopy(final JaxoObject comp) {
        boolean isCopy = false;

        if (comp instanceof JaxoParticleObject) {
            final JaxoParticleObject tmp = (JaxoParticleObject) comp;
            if ((tmp.dashIs(getDash())) && (tmp.getAmp() == getAmp())
                    && (tmp.getWiggles() == getWiggles())
                    && (tmp.isPaintArrow() == isPaintArrow())
                    && (tmp.isFlip() == isFlip())
                    && (tmp.arrowPositionIs(getArrowPosition()))
                    && (tmp.isDoubleLine() == isDoubleLine())
                    && (tmp.getArrow().isCopy(arrow))
                    && (tmp.dlSepIs(getDLSeparation())) && super.isCopy(tmp)) {
                isCopy = true;
            }
        }

        return isCopy;
    }

    /** Sets all parameters from the given object to the current one.
     * @param temp The object to copy from.
     */
    public void copyFrom(final JaxoParticleObject temp) {
        super.copyFrom(temp);
        setDash(temp.getDash());
        setAmp(temp.getAmp());
        setWiggles(temp.getWiggles());
        setPaintArrow(temp.isPaintArrow());
        setFlip(temp.isFlip());
        setArrowPosition(temp.getArrowPosition());
        setArrow(temp.getArrow());
        setDoubleLine(temp.isDoubleLine());
        setDLSeparation(temp.getDLSeparation());
    }

    /** {@inheritDoc} */
    @Override
    public void setState(final JaxoObject o) {
        if (o instanceof JaxoParticleObject) {
            copyFrom((JaxoParticleObject) o);
        } else {
            throw new UnsupportedOperationException("Cannot copy from super type!");
        }
    }

    /** {@inheritDoc} */
    @Override
    public void setPreferences() {
        super.setPreferences();
        setPaintArrow(JaxoPrefs.getBooleanPref(JaxoPrefs.PREF_ARROW));
        setFlip(false);
        setArrowPosition(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_ARROWPOSITION));
        setAmp(JaxoPrefs.getIntPref(JaxoPrefs.PREF_AMPLITUDE));
        setDoubleLine(false);
        setDLSeparation(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_DLSEP));
    }

      //
     // comparison methods for float properties
    //

    /** Compares the dash of this JaxoParticleObject to the
     * given float, taking into account a 0.1% error margin.
     * @param comp The float to compare to.
     * @return True, if the dash matches within 0.1%.
     */
    public final boolean dashIs(final float comp) {
        return JaxoUtils.equal(comp, dash);
    }

    /** Compares the dlSeparation of this JaxoParticleObject to the
     * given float, taking into account a 0.1% error margin.
     * @param comp The float to compare to.
     * @return True, if the dlSeparation matches within 0.1%.
     */
    public final boolean dlSepIs(final float comp) {
        return JaxoUtils.equal(comp, dlSeparation);
    }

    /**
     * Compares the arrowPosition of this JaxoParticleObject to the
     * given float, taking into account a 0.1% error margin.
     *
     * @param comp The float to compare to.
     *
     * @return True, if the arrowPosition matches within 0.1%.
     */
    public final boolean arrowPositionIs(final float comp) {
        return JaxoUtils.equal(comp, arrowPosition);
    }

    /**
     * Paints an arrow on this object.
     *
     * @param g2 The graphics context.
     */
    protected void paintArrow(final JaxoGraphics2D g2) {
        arrow.paint(g2, arrowCoordinates());
    }

    /**
     * Returns the coordinates of the arrow of the current object.
     *
     * @return The coordinates of the arrow of the current object.
     */
    public abstract JaxoArrow.Coordinates arrowCoordinates();
}
