/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.ui.region.curves;

import com.lightcrafts.utils.xml.XMLException;
import com.lightcrafts.utils.xml.XmlNode;

import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;

/**
 * This class models the "clone point" associated with some Curves.  It
 * holds the coordinates of the point, does hit testing for mouse events on
 * the point, and it renders the point.
 */
class ClonePoint implements Cloneable {

    private static final int NominalRadius = 8;    // rendering size at 1-1

    private Stroke foregroundStroke = new BasicStroke(1f);
    private Stroke backgroundStroke = new BasicStroke(3f);

    private float radius;       // The actual rendering size, after scaling
    private Point2D point;      // The special point location
    private GeneralPath shape;  // The Shape to draw, centered on the point

    // Default constructor is for save/restore.
    ClonePoint() {
        this(new Point2D.Double());
    }

    ClonePoint(Point2D p) {
        radius = NominalRadius;
        setPoint(p);
    }

    void setPoint(Point2D p) {
        point = (Point2D) p.clone();
        updateShape();
    }

    Point2D getPoint() {
        return (Point2D) point.clone();
    }

    void translate(double dx, double dy) {
        point.setLocation(point.getX() + dx, point.getY() + dy);
        updateShape();
    }

    void paint(Graphics2D g) {
        AffineTransform xform = g.getTransform();
        float scale = (float) Math.sqrt(Math.abs(xform.getDeterminant()));
        scale = Math.min(scale, 1);
        float r = NominalRadius / scale;
        if (r != radius) {
            radius = r;
            updateShape();
        }
        Color oldColor = g.getColor();
        Stroke oldStroke = g.getStroke();
        g.setColor(Color.black);
        g.setStroke(backgroundStroke);
        g.draw(shape);
        g.setColor(Color.white);
        g.setStroke(foregroundStroke);
        g.draw(shape);
        g.setStroke(oldStroke);
        g.setColor(oldColor);
    }

    boolean isAt(Point2D p) {
        return shape.getBounds2D().contains(p);
    }

    Rectangle2D getPaintBounds() {
        Shape stroked = backgroundStroke.createStrokedShape(shape);
        return stroked.getBounds2D();
    }

    public Object clone() {
        return new ClonePoint(point);
    }

    void save(XmlNode node) {
        node.setAttribute("x", Double.toString(point.getX()));
        node.setAttribute("y", Double.toString(point.getY()));
    }

    void restore(XmlNode node) throws XMLException {
        double x = Double.parseDouble(node.getAttribute("x"));
        double y = Double.parseDouble(node.getAttribute("y"));
        Point2D p = new Point2D.Double(x, y);
        setPoint(p);
    }

    private void updateShape() {
        foregroundStroke = new BasicStroke(radius / 8f);
        backgroundStroke = new BasicStroke(3f * radius / 8f);

        float x = (float) point.getX();
        float y = (float) point.getY();

        shape = new GeneralPath();
        shape.moveTo(x, y - radius);
        shape.lineTo(x, y - radius / 2);
        shape.moveTo(x, y + radius / 2);
        shape.lineTo(x, y + radius);
        shape.moveTo(x - radius, y);
        shape.lineTo(x - radius / 2, y);
        shape.moveTo(x + radius / 2, y);
        shape.lineTo(x + radius, y);
    }
}
