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

import java.awt.Dimension;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

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

import net.sf.jaxodraw.object.JaxoObject;
import net.sf.jaxodraw.object.JaxoObjectEditPanel;
import net.sf.jaxodraw.object.JaxoWiggleObject;
import net.sf.jaxodraw.util.JaxoUtils;


/** A gluon line.
 * @since 2.0
 */
public class JaxoGlLine extends JaxoLineObject implements JaxoWiggleObject {
    private static final long serialVersionUID = 314159L;
    private transient float freq;

    private void readObject(final ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        freq = 0.f;
    }

    /** Sets all parameters from the given object to the current one.
     * @param temp The object to copy from.
     */
    public void copyFrom(final JaxoGlLine temp) {
        super.copyFrom(temp);
        this.freq = temp.getFrequency();
    }

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

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

        if (comp instanceof JaxoGlLine) {
            isCopy = super.isCopy(comp);
        }

        return isCopy;
    }

    /** {@inheritDoc} */
    @Override
    public final String latexCommand(final float scale, final Dimension canvasDim) {
        final int canvasHeight = canvasDim.height;

        final Point2D startPoint = getLaTexStartPoint(scale, canvasHeight);
        final Point2D endPoint = getLaTexEndPoint(scale, canvasHeight);

        if (((int) endPoint.getX() == (int) startPoint.getX())
                && ((int) endPoint.getY() == (int) startPoint.getY())) {
            return "%";
        }

        final float amplitude = getAmp() / (2.f * scale);
        final float windings = (float) getWiggles();
        final String options = getAxo4JOptions(scale);

        return "\\Gluon" + options +  "(" + D_FORMAT.format(startPoint.getX()) + ","
            + D_FORMAT.format(startPoint.getY()) + ")" + "("
            + D_FORMAT.format(endPoint.getX()) + ","
            + D_FORMAT.format(endPoint.getY()) + ")" + "{"
            + D_FORMAT.format(amplitude) + "}" + "{"
            + D_FORMAT.format(windings) + "}";
    }

    /** {@inheritDoc} */
    public float getFrequency() {
        return freq;
    }

    /** {@inheritDoc} */
    public void setWigglesFromFrequency(final float frequency) {
        this.freq = frequency;
        setWigglesFromFrequency();
    }

    /** {@inheritDoc} */
    public void setWigglesFromFrequency() {
        final int n = (int) Math.round(length() * getFrequency());
        setWiggles(n);
    }

    /** {@inheritDoc} */
    public void setFrequencyFromWiggles(final int wiggles) {
        setWiggles(wiggles);
        this.freq = (float) (wiggles / length());
    }

    /** {@inheritDoc} */
    @Override
    public void setPreferences() {
        super.setPreferences();
        setPaintArrow(false);
        setArrowPosition(0.f);
        setWigglesFromFrequency(GLUON_FREQ);
    }

    /** {@inheritDoc} */
    public void prepareEditPanel(final JaxoObjectEditPanel editPanel) {
        editPanel.add2PointsPanel(getPoints(), 0, 0);
        editPanel.addWigglePanel(getAmp(), getWiggles(), 1, 0);
        editPanel.addStrokePanel(getStrokeWidth(), 0, 1);
        editPanel.addDoubleLinePanel(isDoubleLine(), getDLSeparation(), 1, 1);
        editPanel.addColorPanel(getColor(), JaxoObjectEditPanel.TYPE_LINE_COLOR, 2, 1);

        editPanel.setTitleAndIcon("Gluon_line_parameters", "gluon.png");
    }

      //
     // private methods
    //

    /** {@inheritDoc} */
    protected GeneralPath getObjectPath() {
        final GeneralPath gp = getGeneralPath();
        gp.reset();

        if (JaxoUtils.zero(freq)) {
            setFrequencyFromWiggles(getWiggles());
        } else {
            setWigglesFromFrequency();
        }

        if (isDoubleLine()) {
            final float sep = getDLSeparation() / 2.f;
            appendFullLine(gp, sep);
            appendFullLine(gp, -sep);
        } else {
            appendFullLine(gp, 0.f);
        }

        return gp;
    }

    // construct a gluon line like axodraw does,
    // see src/doc/algorithms/gluon_algorithm.tex
    private void appendFullLine(final GeneralPath gp, final float sep) {
            final float length = (float) length();
            final int n = getWiggles();
            final float delta = length / (n + 1);
            final float amp = -0.5f * getAmp();
            final float absamp = Math.abs(amp);

            final float sin = getRelh() / length;
            final float cos = getRelw() / length;

            gp.moveTo(getX(), getY() + sep);

            float x1 = 0.05f * delta;
            float x2 = 0.25f * delta;
            float x3 = 0.7f * delta;
            float y1 = 0.5f * amp + sep;
            float y2 = amp + sep;
            float y3 = amp + sep;

            curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);

            x1 = delta + 0.9f * absamp;
            x2 = delta + 0.9f * absamp;
            x3 = delta;
            y1 = amp + sep;
            y2 = -amp + sep;
            y3 = -amp + sep;

            curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);

            float xj;

            for (int j = 1; j < n; j++) {
                xj = j * delta;
                x1 = xj - 0.9f * absamp;
                x2 = xj - 0.9f * absamp;
                x3 = xj + 0.5f * delta;
                y1 = -amp + sep;
                y2 = amp + sep;
                y3 = amp + sep;
                curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);

                x1 = xj + 0.9f * absamp + delta;
                x2 = xj + 0.9f * absamp + delta;
                x3 = xj + delta;
                y1 = amp + sep;
                y2 = -amp + sep;
                y3 = -amp + sep;
                curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);
            }

            x1 = length - delta - 0.9f * absamp;
            x2 = length - delta - 0.9f * absamp;
            x3 = length - 0.7f * delta;
            y1 = -amp + sep;
            y2 = amp + sep;
            y3 = amp + sep;
            curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);

            x1 = length - 0.25f * delta;
            x2 = length - 0.05f * delta;
            x3 = length;
            y1 = amp + sep;
            y2 = 0.5f * amp + sep;
            y3 = 0.f + sep;
            curveTo(gp, x1, y1, x2, y2, x3, y3, cos, sin);
    }

    private void curveTo(final GeneralPath gp, final float x1, final float y1,
            final float x2, final float y2, final float x3, final float y3,
            final float cos, final float sin) {

        gp.curveTo((x1 * cos) - (y1 * sin) + getX(), (x1 * sin) + (y1 * cos) + getY(),
                (x2 * cos) - (y2 * sin) + getX(), (x2 * sin) + (y2 * cos) + getY(),
                (x3 * cos) - (y3 * sin) + getX(), (x3 * sin) + (y3 * cos) + getY());
    }

    // Get the axodraw4j options set for this line

    /** {@inheritDoc} */
    protected String getAxo4JOptions(final float scale) {

        String optioncmd = "";

        if (isDoubleLine()) {
            optioncmd = "[" + "double,sep="
                + D_FORMAT.format(this.getDLSeparation()) + "]";
        }

        return optioncmd;
    }
}
