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

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 loop.
 * @since 2.0
 */
public class JaxoGlLoop extends JaxoLoopObject 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 JaxoGlLoop temp) {
        super.copyFrom(temp);
        this.freq = temp.getFrequency();
    }

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

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

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

        return isCopy;
    }

    /** {@inheritDoc} */
    @Override
    public final String latexCommand(final float scale, final Dimension canvasDim) {
        final float radius = getLaTexRadius(scale);

        if ((int) radius == 0) {
            return "%";
        }

        final Point2D center = getLaTexCenter(scale, canvasDim.height);
        final Point2D angles = getLaTexAngles();
        final float amplitude = getAmp() / (2.f * scale);
        final float windings = (float) getWiggles();
        final String options = getAxo4JOptions(scale);

        return "\\GluonArc" + options + "(" + D_FORMAT.format(center.getX()) + ","
            + D_FORMAT.format(center.getY()) + ")" + "("
            + D_FORMAT.format(radius) + ","
            + D_FORMAT.format(angles.getX() - 180) + ","
            + D_FORMAT.format(angles.getY() - 180) + ")" + "{"
            + 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 double r = getRadius();
        final int n = (int) Math.round((2 * r * Math.PI) * getFrequency());
        setWiggles(n);
    }

    /** {@inheritDoc} */
    public void setFrequencyFromWiggles(final int wiggles) {
        setWiggles(wiggles);
        final double r = getRadius();
        this.freq = (float) (wiggles / (2 * r * Math.PI));
    }

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

    /** {@inheritDoc} */
    public void prepareEditPanel(final JaxoObjectEditPanel editPanel) {
        final int r = (int) Math.round(getRadius());

        editPanel.addXYRPanel(getX(), getY(), r, 0, 0);
        editPanel.addWigglePanel(getAmp(), getWiggles(), 1, 0);
        editPanel.addDoubleLinePanel(isDoubleLine(), getDLSeparation(), 2, 0);
        editPanel.addStrokePanel(getStrokeWidth(), 0, 1);
        editPanel.addColorPanel(getColor(), JaxoObjectEditPanel.TYPE_LINE_COLOR, 1, 1);

        editPanel.setTitleAndIcon("Gluon_loop_parameters", "gluonloop.png");
    }

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

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

        final int wiggles = getWiggles();
        final float amp = 0.5f * getAmp();
        final float radius = (float) getRadius();

        if (radius <= 1.f) {
            return null;
        }

        if (isDoubleLine()) {
            final float ds = getDLSeparation() / 2.f;

            appendFullLoop(path, radius, ds, wiggles, amp);
            appendFullLoop(path, radius, -ds, wiggles, amp);
        } else {
            appendFullLoop(path, radius, 0.f, wiggles, amp);
        }

        return path;
    }

    private void appendFullLoop(final GeneralPath gp, final float rad, final float ds, final int n, final float a) {
        final float cos = getRelw() / rad;
        final float sin = getRelh() / rad;
        final float r = rad + ds;

        final float absa = Math.abs(a);
        float thetaj;
        final float delta = (float) (2.f * Math.PI / n);

        gp.moveTo((r - a) * cos + getX(), (r - a) * sin + getY());

        for (int j = 1; j <= n; j++) {
            thetaj = (j - 1) * delta;
            polarCurveTo(gp,
                    (float) ((r - a) / Math.cos(0.9d * absa / r)),
                    thetaj - 0.9f * absa / r,
                    (float) ((r + a) / Math.cos(0.5d * delta + 0.9d * absa / r)),
                    thetaj - 0.9f * absa / r,
                    r + a,
                    thetaj + 0.5f * delta,
                    cos, sin);
            polarCurveTo(gp,
                    (float) ((r + a) / Math.cos(0.5d * delta + 0.9d * absa / r)),
                    thetaj + delta + 0.9f * absa / r,
                    (float) ((r - a) / Math.cos(0.9d * absa / r)),
                    thetaj + delta + 0.9f * absa / r,
                    r - a,
                    thetaj + delta,
                    cos, sin);
        }
    }

    private void polarCurveTo(final GeneralPath gp, final float r1, final float th1, final float r2, final float th2,
            final float r3, final float th3, final float cos, final float sin) {
        final float x1 = (float) (r1 * Math.cos(th1));
        final float y1 = (float) (r1 * Math.sin(th1));
        final float x2 = (float) (r2 * Math.cos(th2));
        final float y2 = (float) (r2 * Math.sin(th2));
        final float x3 = (float) (r3 * Math.cos(th3));
        final float y3 = (float) (r3 * Math.sin(th3));

        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;
    }
}
