/**
 *  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.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;

import java.beans.PropertyChangeEvent;

import java.io.IOException;
import java.io.ObjectInputStream;

import net.sf.jaxodraw.gui.panel.edit.JaxoEditPanel;
import net.sf.jaxodraw.util.JaxoPrefs;
import net.sf.jaxodraw.util.JaxoUtils;


/**
 * The default arrow for JaxoDraw.
 *
 * @since 2.0
 */
public class JaxoDefaultArrow extends AbstractBaseArrow {
    private static final long serialVersionUID = 314159L;
    private transient GeneralPath arrowPath
            = new GeneralPath(GeneralPath.WIND_NON_ZERO, 5);

    private float latexLineWidth = 0.5f;
    private boolean isDoubleLine = false;
    private float dlSep = 2.f;

    /** Constructor: initialize the fields. */
    public JaxoDefaultArrow() {
        super();
        setColor(Color.BLACK);
        setFillColor(Color.BLACK);
        setFilled(true);
        setStroke(new BasicStroke(1.f));
        setArrowLength(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_ARROWLENGTH));
        setArrowWidth(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_ARROWWIDTH));
        setArrowInset(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_ARROWINSET));
    }

    private void readObject(final ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();

        setStroke(new BasicStroke(1.f));
        arrowPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 5);
    }

    /** {@inheritDoc} */
    public void paint(final Graphics2D g2, final Coordinates coords) {
        resetPath(coords);

        g2.setStroke(getStroke());

        if (isFilled()) {
            g2.setColor(getFillColor());
            g2.fill(arrowPath);
        }

        g2.setColor(getColor());
        g2.draw(arrowPath);
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCopy(final JaxoArrow testArrow) {
        if (testArrow instanceof JaxoDefaultArrow) {
            return super.isCopy(testArrow);
        }

        return false;
    }

    /** {@inheritDoc} */
    public String latexCommand(final float arPos, final float scale) {

        //Not sure how to rescale

        final float arWidth = this.getArrowWidth();
        final float arLength = this.getArrowLength();
        final float arInset = this.getArrowInset();

        return "arrow,arrowpos=" + D_FORMAT.format(arPos)
                + ",arrowlength=" + D_FORMAT.format(arLength)
                + ",arrowwidth=" + D_FORMAT.format(arWidth)
                + ",arrowinset=" + D_FORMAT.format(arInset);
    }

    /** {@inheritDoc} */
    public Rectangle getBounds(final Coordinates coords) {
        resetPath(coords);
        return getStroke().createStrokedShape(arrowPath).getBounds();
    }

    /** {@inheritDoc} */
    public JaxoEditPanel getEditPanel() {
        return new JaxoDefaultArrowEditPanel(this, true);
    }

    /** {@inheritDoc} */
    public void propertyChange(final PropertyChangeEvent evt) {
        if ("color".equals(evt.getPropertyName())) {
            final Color newColor = (Color) evt.getNewValue();
            setColor(newColor);
            setFillColor(newColor);
        } else if ("strokeWidth".equals(evt.getPropertyName())) {
            float old = ((Float) evt.getOldValue()).floatValue();
            latexLineWidth = ((Float) evt.getNewValue()).floatValue();
            if (JaxoUtils.zero(old)) {
                old = latexLineWidth;
            }
            final float sep = sep();
            setArrowLength(len(sep, latexLineWidth) / len(sep, old) * getArrowLength());
            setArrowWidth(wid(sep, latexLineWidth) / wid(sep, old) * getArrowWidth());
        } else if ("doubleLine".equals(evt.getPropertyName())) {
            isDoubleLine = ((Boolean) evt.getNewValue()).booleanValue();
        } else if ("dlSeparation".equals(evt.getPropertyName())) {
            float old = ((Float) evt.getOldValue()).floatValue();
            dlSep = ((Float) evt.getNewValue()).floatValue();
            final float sep = sep();
            if (JaxoUtils.zero(old)) {
                old = sep;
            }
            setArrowLength(len(sep, latexLineWidth) / len(old, latexLineWidth) * getArrowLength());
            setArrowWidth(wid(sep, latexLineWidth) / wid(old, latexLineWidth) * getArrowWidth());
        }
    }

      //
     // private
    //

    private void resetPath(final Coordinates coords) {
        resetPath();

        final AffineTransform at = new AffineTransform();
        at.translate(coords.getX(), coords.getY());
        at.rotate(coords.getAngle());
        arrowPath.transform(at);
    }

    private void resetPath() {
        arrowPath.reset();

        final float arrowlength = getArrowLength();
        final float arrowwidth = getArrowWidth();
        final float arrowinset = getArrowInset() * arrowlength;

        arrowPath.moveTo(arrowlength / 2.f, 0.f);
        arrowPath.lineTo(-arrowlength / 2.f, arrowwidth);
        arrowPath.lineTo((arrowinset - (arrowlength / 2.f)), 0.f);
        arrowPath.lineTo(-arrowlength / 2.f, -arrowwidth);
        arrowPath.closePath();
    }

    private float wid(final float sep, final float stroke) {
        return 1.2f * (2.f + 0.7f * sep + stroke);
    }

    private float len(final float sep, final float stroke) {
        return 2.5f * (2.f + 0.7f * sep + stroke);
    }

    private float sep() {
        return (isDoubleLine ? dlSep : 0.f);
    }
}
