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

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

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

        if (comp instanceof JaxoPLoop) {
            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 wiggles = (float) getWiggles();
        final String options = getAxo4JOptions(scale);

        return "\\PhotonArc" + 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(wiggles) + "}";
    }

    /** {@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(PHOTON_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("Photon_loop_parameters", "photonloop.png");
    }

      //
     // private methods
    //

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

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

        final float radius = (float) getRadius();

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

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

            appendFullLoop(gp, radius, ds);
            appendFullLoop(gp, radius, -ds);
        } else {
            appendFullLoop(gp, radius, 0.f);
        }

        return gp;
    }

    private void appendFullLoop(final GeneralPath gp, final float radius, final float sep) {
        final int n = 2 * getWiggles();
        final float delta = (float) (2.d * Math.PI / n);
        float amp = 0.5f * getAmp();
        final float r = radius + sep;

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

        final float cp = (float) Math.cos(delta);
        final float sp = (float) Math.sin(delta);
        final float cp2 = (float) Math.cos(delta / 2.f);
        final float sp2 = (float) Math.sin(delta / 2.f);

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

        float thetaj;
        float cs, sn, beta, tt, amp1;
        float x1, y1, x2, y2, x3, y3;

        for (int j = 1; j <= n; j++) {
            thetaj = (j - 1) * delta;
            amp = -amp;

            cs = (float) Math.cos(thetaj);
            sn = (float) Math.sin(thetaj);

            beta = r * 2.f / amp / n;
            tt = (sp - cp * beta) / (cp + sp * beta);
            amp1 = (8.f * (r + amp) * (beta * cp2 - sp2)
                    - (beta * (4.f + cp) + 3.f * tt * cp - 4.f * sp) * r)
                    / (3.f * (beta - tt));
            x1 = (8.f * (r + amp) * cp2 - (1.f + cp) * r) / 3.f - amp1;
            y1 = (x1 - r) * beta;
            x2 = amp1;
            y2 = (amp1 - r * cp) * tt + r * sp;
            x3 = r * cp;
            y3 = r * sp;

            curveTo(gp, x1 * cs - y1 * sn, x1 * sn + y1 * cs,
                x2 * cs - y2 * sn, x2 * sn + y2 * cs,
                x3 * cs - y3 * sn, x3 * sn + y3 * cs, 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;
    }
}
