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

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

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

import net.sf.jaxodraw.object.JaxoHandle;
import net.sf.jaxodraw.object.JaxoObject;
import net.sf.jaxodraw.object.JaxoObjectEditPanel;
import net.sf.jaxodraw.util.JaxoColor;
import net.sf.jaxodraw.util.JaxoConstants;
import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;


/** A box object.
 * @since 2.0
 */
public class JaxoBox extends JaxoFillObject {
    private static final long serialVersionUID = 314159L;
    private transient Rectangle2D box = new Rectangle2D.Float();

    private void readObject(final ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        box = new Rectangle2D.Float();
    }

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

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

        return isCopy;
    }

    /** {@inheritDoc}
     *  This implementation also handles DX/DY.
     */
    @Override
    public final int getGrabbedHandle(final int clickX, final int clickY, final JaxoHandle h) {
        int handle = super.getGrabbedHandle(clickX, clickY, h);

        if (isAroundDX(clickX, clickY, h)) {
            handle = SELECT_DX;
        } else if (isAroundDY(clickX, clickY, h)) {
            handle = SELECT_DY;
        }

        return handle;
    }

    private boolean isAroundDX(final int clickX, final int clickY, final JaxoHandle h) {
        return h.contains(clickX, clickY, getX(), getY2());
    }

    private boolean isAroundDY(final int clickX, final int clickY, final JaxoHandle h) {
        return h.contains(clickX, clickY, getX2(), getY());
    }

    /** {@inheritDoc} */
    @Override
    public boolean canBeSelected(final int handle, final int mode) {
        return ((handle == SELECT_P1) || (handle == SELECT_P2)
        || (handle == SELECT_DX) || (handle == SELECT_DY));
    }

    /** {@inheritDoc} */
    @Override
    public final void paintHandles(final JaxoGraphics2D g2, final JaxoHandle h,
        final int editMode) {
        if (editMode == JaxoConstants.UNGROUP) {
            return;
        }

        h.paint(g2, getX(), getY(), isMarked(), !canBeSelected(SELECT_P1, editMode));
        h.paint(g2, getX2(), getY2(), isMarked(), !canBeSelected(SELECT_P2, editMode));
        h.paint(g2, getX(), getY2(), isMarked(), !canBeSelected(SELECT_DX, editMode));
        h.paint(g2, getX2(), getY(), isMarked(), !canBeSelected(SELECT_DY, editMode));
    }

    /** {@inheritDoc} */
    @Override
    public final float smallestDistanceTo(final int px, final int py) {
        final int x = getX();
        final int y = getY();
        final int w = getWidth();
        final int h = getHeight();

        double distX = (double) (px - x);
        double distY = (double) (py - y);
        final float dist1 = (float) Math.sqrt((distX * distX) + (distY * distY));
        distX = (double) (px - x);
        distY = (double) (py - y - h);
        final float dist2 = (float) Math.sqrt((distX * distX) + (distY * distY));
        distX = (double) (px - x - w);
        distY = (double) (py - y);
        final float dist3 = (float) Math.sqrt((distX * distX) + (distY * distY));
        distX = (double) (px - x - w);
        distY = (double) (py - y - h);
        final float dist4 = (float) Math.sqrt((distX * distX) + (distY * distY));
        return smallest(dist1, dist2, dist3, dist4);
    }

    private float smallest(final float dist1, final float dist2, final float dist3, final float dist4) {
        if (dist1 < dist2) {
            return smallest(dist1, dist3, dist4);
        } else {
            return smallest(dist2, dist3, dist4);
        }
    }

    private float smallest(final float dist1, final float dist2, final float dist3) {
        float dist = 0.f;
        if (dist1 < dist2) {
            if (dist1 < dist3) {
                dist = dist1;
            } else {
                dist = dist3;
            }
        } else {
            if (dist2 < dist3) {
                dist = dist2;
            } else {
                dist = dist3;
            }
        }
        return dist;
    }

    /** {@inheritDoc} */
    public final void paint(final JaxoGraphics2D g2) {
        updateBox();

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

        g2.setColor(getColor());
        g2.setStroke(getStroke());
        g2.draw(box);
    }

    /**
     * Returns the bounding box of this object.
     *
     * @return the bounding box of this object.
     */
    public Rectangle getBounds() {
        updateBox();
        return getStroke().createStrokedShape(box).getBounds();
    }

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

        final Point2D lowerCorner = getLaTexLowerCorner(scale, canvasHeight);
        final Point2D upperCorner = getLaTexUpperCorner(scale, canvasHeight);

        if (((int) lowerCorner.getX() == (int) upperCorner.getX())
                && ((int) lowerCorner.getY() == (int) upperCorner.getY())) {
            return "%";
        }

        String command = "";

        if (isFilled()) {
            if (JaxoColor.isGrayScale(getFillColor())) {
                final float grayScale = JaxoColor.getGrayScaleFloat(getFillColor());

                command =
                    "\\GBox" + "(" + D_FORMAT.format(lowerCorner.getX()) + ","
                    + D_FORMAT.format(lowerCorner.getY()) + ")" + "("
                    + D_FORMAT.format(upperCorner.getX()) + ","
                    + D_FORMAT.format(upperCorner.getY()) + ")" + "{"
                    + GRAY_SCALE_FORMAT.format(grayScale) + "}";
            } else {
                String tlc;
                if (JaxoColor.isGrayScale(getColor())) {
                    tlc = "Gray";
                } else {
                    tlc = JaxoColor.getColorName(getColor());
                }
                final String tfc = JaxoColor.getColorName(getFillColor());

                command =
                    "\\CBox" + "(" + D_FORMAT.format(lowerCorner.getX()) + ","
                    + D_FORMAT.format(lowerCorner.getY()) + ")" + "("
                    + D_FORMAT.format(upperCorner.getX()) + ","
                    + D_FORMAT.format(upperCorner.getY()) + ")" + "{" + tlc
                    + "}" + "{" + tfc + "}";
            }
        } else {
            command =
                "\\EBox" + "(" + D_FORMAT.format(lowerCorner.getX()) + ","
                + D_FORMAT.format(lowerCorner.getY()) + ")" + "("
                + D_FORMAT.format(upperCorner.getX()) + ","
                + D_FORMAT.format(upperCorner.getY()) + ")";
        }

        return command;
    }

    /** {@inheritDoc} */
    public void prepareEditPanel(final JaxoObjectEditPanel editPanel) {
        editPanel.addCenterSizePanel(getX(), getY(), getWidth(), getHeight(),
            0, 0);
        editPanel.addStrokePanel(getStrokeWidth(), 0, 1);
        editPanel.addFillLineColorPanels(getColor(), getFillColor(), isFilled(),
            1, 1, 1, 0);

        editPanel.setTitleAndIcon("Box_parameters", "box.png");
    }

      //
     // private methods
    //

    private Point2D getLaTexLowerCorner(final float scaleFactor, final int canvasHeight) {
        final Point2D lowerCornerVec = new Point2D.Float();

        final float x1 = (getRelw() < 0) ? getX2() : getX();
        final float y1 = (getRelh() < 0) ? getY() : getY2();

        lowerCornerVec.setLocation(x1 / scaleFactor,
            (canvasHeight - y1) / scaleFactor);

        return lowerCornerVec;
    }

    private Point2D getLaTexUpperCorner(final float scaleFactor, final int canvasHeight) {
        final Point2D upperCornerVec = new Point2D.Float();

        final float x1 = (getRelw() < 0) ? getX() : getX2();
        final float y1 = (getRelh() < 0) ? getY2() : getY();

        upperCornerVec.setLocation(x1 / scaleFactor,
            (canvasHeight - y1) / scaleFactor);

        return upperCornerVec;
    }

    private void updateBox() {
        box.setFrameFromDiagonal(getX(), getY(), getX() + getRelw(),
            getY() + getRelh());
    }
}
