// Charles A. Loomis, Jr., and University of California, Santa Cruz,
// Copyright (c) 2000
package org.freehep.swing.graphics;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.Arrays;

import org.freehep.swing.images.FreeHepImage;

/**
 * Selects a rectangular screen region.  The sides of the rectangle
 * are parallel to the x and y-axes.
 *
 * @author Charles Loomis
 * @author Mark Donszelmann
 * @version $Id: RectangularSelectionPanel.java,v 1.8 2002/06/12 23:15:15 duns Exp $ */
public class RectangularSelectionPanel
    extends AbstractRegionSelectionPanel {

    /**
     * Creates a RectangularSelectionPanel. */
    public RectangularSelectionPanel() {
        super();
    }

    /**
     * Returns the number of control points for the rectangle (4).
     *
     * @return 4, the control points at the corner of the rectangle */
    public int getNumberOfControlPoints() {
        return 4;
    }

    public Cursor getControlPointCursor(int index) {
        if (index >= 0) {
            int k = (index+2) % 4;
            return compassCursor("Resize", xCtrlPts[index] - xCtrlPts[k],
                                           yCtrlPts[index] - yCtrlPts[k], 4, true);
        }
        return FreeHepImage.getCursor("RectangularCursor");
    }

    /**
     * Initialize the control points based on the given starting point
     * (x,y).
     *
     * @param x x-coordinate (in pixels) of the starting point
     * @param y y-coordinate (in pixels) fo the starting point */
    public void initializeControlPoints(int x, int y) {
        activeCtrlPt = 2;
        Arrays.fill(xCtrlPts,x);
        Arrays.fill(yCtrlPts,y);
    }

    /**
     * Move the active control point to the point (x,y).
     *
     * @param x x-coordinate of the new point
     * @param y y-coordinate of the new point */
    public void updateActiveControlPoint(int x, int y) {

        // Bring the location within bounds.
        x = forceXCoordinateWithinBounds(x);
        y = forceYCoordinateWithinBounds(y);

        // Update the active control point.
        xCtrlPts[activeCtrlPt] = x;
        yCtrlPts[activeCtrlPt] = y;

        // Get the opposite control point.
        int oppCtrlPt = (activeCtrlPt+2)%nCtrlPts;
        int xOpp = xCtrlPts[oppCtrlPt];
        int yOpp = yCtrlPts[oppCtrlPt];

        // Now choose the next control point so that we maintain a
        // clockwise order to the points.
        int otherCtrlPt =
            ((x<xOpp && y>yOpp)||(x>xOpp && y<yOpp)) ? 1 : 3;
        otherCtrlPt = (activeCtrlPt+otherCtrlPt)%4;

        // Set the other two control points.
        xCtrlPts[otherCtrlPt] = x;
        yCtrlPts[otherCtrlPt] = yOpp;
        otherCtrlPt = (otherCtrlPt+2)%nCtrlPts;
        xCtrlPts[otherCtrlPt] = xOpp;
        yCtrlPts[otherCtrlPt] = y;

        repaintPanel();
    }

    public void paintComponent(Graphics g) {

        // Allow parent to draw any custom painting.
        super.paintComponent(g);

        // If the selection region is visible, paint it.
        if (visible) {

            // Make a 2D graphics context.
            Graphics2D g2d = (Graphics2D) g;

            // Draw a rectangle on top the the image.
            g2d.setStroke(thickStroke);
            g.setColor(Color.black);
            g.drawPolygon(xCtrlPts, yCtrlPts, nCtrlPts);
            g2d.setStroke(thinStroke);
            g.setColor(Color.white);
            g.drawPolygon(xCtrlPts, yCtrlPts, nCtrlPts);

            if (activeCtrlPt >= 0) {
                // Draw the active control point.
                g.setColor(Color.black);
                g.fillRect(xCtrlPts[activeCtrlPt]-ctrlPtSize-1,
                           yCtrlPts[activeCtrlPt]-ctrlPtSize-1,
                           2*ctrlPtSize+3, 2*ctrlPtSize+3);
                g.setColor(Color.white);
                g.fillRect(xCtrlPts[activeCtrlPt]-ctrlPtSize,
                           yCtrlPts[activeCtrlPt]-ctrlPtSize,
                           2*ctrlPtSize+1, 2*ctrlPtSize+1);
            }
        }
    }

    /**
     * Make the affine transform which corresponds to this rectangular
     * selection.
     *
     * @return AffineTransform which describes the selected region */
    public AffineTransform makeAffineTransform() {

        int second = 0;
        int third = 0;

        // Find first the upper, left-hand point.
        int first = 0;
        int savedValue = xCtrlPts[0]*xCtrlPts[0]+yCtrlPts[0]*yCtrlPts[0];
        for (int i=1; i<4; i++) {
            int value = xCtrlPts[i]*xCtrlPts[i]+yCtrlPts[i]*yCtrlPts[i];
            if (value<savedValue) {
                savedValue = value;
                first = i;
            }
        }

        // Calculate the adjacent partner and the opposite corner.
        second = (first+1)%4;
        third = (first+2)%4;

        // Just call the utility function of the parent now.
        return makeTransform((double) xCtrlPts[first],
                             (double) yCtrlPts[first],
                             (double) xCtrlPts[second],
                             (double) yCtrlPts[second],
                             (double) xCtrlPts[third],
                             (double) yCtrlPts[third]);
    }

    /**
     * Determine if the selection is valid.  Return false if the area
     * is zero. */
    public boolean isValidSelection() {
        return (visible) && (xCtrlPts[0]!=xCtrlPts[2] ||
                             yCtrlPts[0]!=yCtrlPts[2]);
    }

}
