/*
 * @(#)BezierScaleHandle.java  1.0  16. Juni 2006
 *
 * Copyright (c) 1996-2006 by the original authors of JHotDraw
 * and all its contributors.
 * All rights reserved.
 *
 * The copyright of this software is owned by the authors and  
 * contributors of the JHotDraw project ("the copyright holders").  
 * You may not use, copy or modify this software, except in  
 * accordance with the license agreement you entered into with  
 * the copyright holders. For details see accompanying license terms. 
 */

package org.jhotdraw.draw;

import org.jhotdraw.util.*;
import org.jhotdraw.undo.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import org.jhotdraw.geom.*;
/**
 * A Handle to scale and rotate a BezierFigure.
 * Pressing the alt key or the shift key while manipulating the handle restricts
 * the handle to only rotate the BezierFigure.
 *
 * @author Werner Randelshofer.
 * @version 1.0 16. Juni 2006 Created.
 */
public class BezierScaleHandle extends AbstractHandle {
    private Point location;
    private Object restoreData;
    private AffineTransform transform;
    private Point2D.Double center;
    private double startTheta;
    private double startLength;
    
    /** Creates a new instance. */
    public BezierScaleHandle(BezierFigure owner) {
        super(owner);
    }
    
    public boolean isCombinableWith(Handle h) {
        return false;
    }
    
    
    /**
     * Draws this handle.
     */
    public void draw(Graphics2D g) {
        drawCircle(g, Color.yellow, Color.black);
    }
    
    protected Rectangle basicGetBounds() {
        Rectangle r = new Rectangle(getLocation());
        r.grow(getHandlesize() / 2, getHandlesize() / 2);
        return r;
    }
    
    public Point getLocation() {
        if (location == null) {
            return  /*location =*/ view.drawingToView(getOrigin());
        }
        return location;
    }
    
    private BezierFigure getBezierFigure() {
        return (BezierFigure) getOwner();
    }
    
    private Point2D.Double getOrigin() {
        // find a nice place to put handle
        // Need to pick a place that will not overlap with point handle
        // and is internal to polygon
        int handlesize = getHandlesize();
        
        // Try for one handlesize step away from outermost toward center
        Point2D.Double outer = getBezierFigure().getOutermostPoint();
        Point2D.Double ctr = getBezierFigure().getCenter();
        double len = Geom.length(outer.x, outer.y, ctr.x, ctr.y);
        if (len == 0) { // best we can do?
            return new Point2D.Double(outer.x - handlesize/2, outer.y + handlesize/2);
        }
        
        double u = handlesize / len;
        if (u > 1.0) { // best we can do?
            return new Point2D.Double((outer.x * 3 + ctr.x)/4, (outer.y * 3 + ctr.y)/4);
        } else {
            return new Point2D.Double(outer.x * (1.0 - u) + ctr.x * u,
                    outer.y * (1.0 - u) + ctr.y * u);
        }
    }
    public void trackStart(Point anchor, int modifiersEx) {
        location = new Point(anchor.x, anchor.y);
        restoreData = getBezierFigure().getTransformRestoreData();
        transform = new AffineTransform();
        center = getBezierFigure().getCenter();
        Point2D.Double anchorPoint = view.viewToDrawing(anchor);
        startTheta = Geom.angle(center.x, center.y, anchorPoint.x, anchorPoint.y);
        startLength = Geom.length(center.x, center.y, anchorPoint.x, anchorPoint.y);
    }
    
    public void trackStep(Point anchor, Point lead, int modifiersEx) {
        location = new Point(lead.x, lead.y);
        Point2D.Double leadPoint = view.viewToDrawing(lead);
        double stepTheta = Geom.angle(center.x, center.y, leadPoint.x, leadPoint.y);
        double stepLength = Geom.length(center.x, center.y, leadPoint.x, leadPoint.y);
        double scaleFactor = (modifiersEx & (InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)) != 0 ? 1d : stepLength / startLength;
        transform.setToIdentity();
        transform.translate(center.x, center.y);
        transform.scale(scaleFactor, scaleFactor);
        transform.rotate(stepTheta - startTheta);
        transform.translate(-center.x, -center.y);
        getOwner().willChange();
        getOwner().restoreTransformTo(restoreData);
        getOwner().transform(transform);
        getOwner().changed();
    }
    /*
     *	public  void scaleRotate(Point anchor, Polygon originalPolygon, Point p) {
                willChange();
     
                // use center to determine relative angles and lengths
                Point ctr = center(originalPolygon);
                double anchorLen = Geom.length(ctr.x, ctr.y, anchor.x, anchor.y);
     
                if (anchorLen > 0.0) {
                        double newLen = Geom.length(ctr.x, ctr.y, p.x, p.y);
                        double ratio = newLen / anchorLen;
     
                        double anchorAngle = Math.atan2(anchor.y - ctr.y, anchor.x - ctr.x);
                        double newAngle = Math.atan2(p.y - ctr.y, p.x - ctr.x);
                        double rotation = newAngle - anchorAngle;
     
                        int n = originalPolygon.npoints;
                        int[] xs = new int[n];
                        int[] ys = new int[n];
     
                        for (int i = 0; i < n; ++i) {
                                int x = originalPolygon.xpoints[i];
                                int y = originalPolygon.ypoints[i];
                                double l = Geom.length(ctr.x, ctr.y, x, y) * ratio;
                                double a = Math.atan2(y - ctr.y, x - ctr.x) + rotation;
                                xs[i] = (int)(ctr.x + l * Math.cos(a) + 0.5);
                                ys[i] = (int)(ctr.y + l * Math.sin(a) + 0.5);
                        }
                        setInternalPolygon(new Polygon(xs, ys, n));
                }
                changed();
        }
     */
    
    public void trackEnd(Point anchor, Point lead, int modifiersEx) {
        view.getDrawing().fireUndoableEditHappened(
                new RestoreDataEdit(getOwner(), restoreData));
        location = null;
    }
    
}
