/**
 *  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.JaxoSymmetricObject;
import net.sf.jaxodraw.object.JaxoWiggleObject;
import net.sf.jaxodraw.util.JaxoUtils;


/** A zig-zag line.
 * @since 2.0
 */
public class JaxoZigZagLine extends JaxoLineObject implements JaxoWiggleObject,
    JaxoSymmetricObject {
    private static final long serialVersionUID = 314159L;
    private transient float freq;
    private boolean symmetric;

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

    /**
     * Returns the symmetric property of this JaxoZigZagLine.
     * Determines whether the wiggles of this zigzag line
     * start and end in the same direction.
     *
     * @return The symmetric property of this JaxoZigZagLine.
     */
    public final boolean isSymmetric() {
        return symmetric;
    }

    /**
     * Sets the symmetric property of this line object.
     *
     * @param newSymm The symmetric property of this line object.
     */
    public final void setSymmetric(final boolean newSymm) {
        final Boolean old = Boolean.valueOf(symmetric);
        this.symmetric = newSymm;
        firePropertyChange("symmetric", old, Boolean.valueOf(symmetric));
    }

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

    /** {@inheritDoc} */
    @Override
    public void setState(final JaxoObject o) {
        if (o instanceof JaxoZigZagLine) {
            copyFrom((JaxoZigZagLine) 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 JaxoZigZagLine) {
            final JaxoZigZagLine line = (JaxoZigZagLine) comp;
            if ((line.isSymmetric() == isSymmetric()) && super.isCopy(line)) {
                isCopy = true;
            }
        }

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

        if (isDoubleLine()) {
            final Point2D offSet = getLatexDLOffSet(scale);

            final String command1 = latexString(startPoint, endPoint, offSet, scale, 1.f);
            final String command2 = latexString(startPoint, endPoint, offSet, scale, -1.f);

            return command1.concat(command2.concat("%%JaxoDrawID:DoubleLine"
                        + "(" + D_FORMAT.format(getDLSeparation()) + ")"));
        } else {
            return latexString(startPoint, endPoint, endPoint, scale, 0.f);
        }
    }

    private String latexString(final Point2D startPoint, final Point2D endPoint, final Point2D offSet,
        final float scale, final float sign) {

        final float amplitude = getAmp() / (2.f * scale);
        final float wiggles = getLaTexWiggleNumber();

        final StringBuffer command = new StringBuffer(32);

        command.append("\\ZigZag(").append(
        D_FORMAT.format(startPoint.getX() + sign * offSet.getX())).append(',').append(
        D_FORMAT.format(startPoint.getY() + sign * offSet.getY())).append(")(").append(
        D_FORMAT.format(endPoint.getX() + sign * offSet.getX())).append(',').append(
        D_FORMAT.format(endPoint.getY() + sign * offSet.getY())).append("){").append(
        D_FORMAT.format(amplitude)).append("}{").append(
        D_FORMAT.format(wiggles)).append('}');

        return command.toString();
    }

    /** {@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);
        setSymmetric(false);
        setWigglesFromFrequency(PHOTON_FREQ);
    }

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

        editPanel.setTitleAndIcon("Zigzag_line_parameters", "zigzag.png");
    }

      //
     // private methods
    //

    private float getLaTexWiggleNumber() {
        float n = (float) getWiggles();
        if (isSymmetric()) {
            n += 0.5f;
        }
        return n;
    }

    /** {@inheritDoc} */
    protected GeneralPath getObjectPath() {
        final GeneralPath gp = getGeneralPath();
        gp.reset();
        float a = (float) Math.round(getAmp() / 1.5f);

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

        int n = 2 * getWiggles();
        if (isSymmetric()) {
            n += 1;
        }

        float length = (float) getRadius();

        if (JaxoUtils.zero(length)) {
            length = 1.f;
        }

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

        final float ts = length / n;

        if (isDoubleLine()) {
            final float dlsep = getDLSeparation() / 2.f;
            gp.moveTo(getX(), getY() + dlsep);
            for (int i = 1; i <= n; i++) {
                x = (((2 * i) - 1) * ts) / 2.f;
                y = -a + dlsep;
                gp.lineTo((x * cos) - (y * sin) + getX(),
                    (x * sin) + (y * cos) + getY());
                x = i * ts;
                y = dlsep;
                gp.lineTo((x * cos) - (y * sin) + getX(),
                    (x * sin) + (y * cos) + getY());
                a = -a;
            }

            gp.moveTo(getX(), getY() - dlsep);
            a = (float) Math.round(getAmp() / 1.5f);
            for (int i = 1; i <= n; i++) {
                x = (((2 * i) - 1) * ts) / 2.f;
                y = -a - dlsep;
                gp.lineTo((x * cos) - (y * sin) + getX(),
                    (x * sin) + (y * cos) + getY());
                x = i * ts;
                y = -dlsep;
                gp.lineTo((x * cos) - (y * sin) + getX(),
                    (x * sin) + (y * cos) + getY());
                a = -a;
            }
        } else {
            gp.moveTo(getX(), getY());

            for (int i = 1; i <= n; i++) {
                x = (((2 * i) - 1) * ts) / 2.f;
                y = -a;
                gp.lineTo((x * cos) - (y * sin) + getX(),
                    (x * sin) + (y * cos) + getY());
                x = i * ts;
                gp.lineTo((x * cos) + getX(), (x * sin) + getY());
                a = -a;
            }
        }
        return gp;
    }

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