// SlateAppMenuBar.java
package version1;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

import slate.*;

/**
 * The menu bar for the application.
 * This menu bar adds a few menus--for clearing the slate
 * switching between different drawing modes
 * @author Ramnivas Laddad
 */
public class SlateAppMenuBar extends JMenuBar {
    private Slate _slate;

    /**
     * Construct the menu bar
     * Creates sub menus for various operations.
     * @param slate the slate to draw
     */
    public SlateAppMenuBar(Slate slate) {
	_slate = slate;
	createSlateMenus();
    }

    /**
     * Private method that does actual work of creating menus
     */
    private void createSlateMenus() {
	JMenu slateMenu = add(new JMenu("Slate"));

	slateMenu.add(new AbstractAction("Clear") {
	    public void actionPerformed(ActionEvent e) {
		_slate.getModel().removeAllShapes();
	    }
	});

	JMenuItem shapeModeMenu = slateMenu.add(new JMenu("Set Shape Mode"));
	String[] shapeModeStrings 
	    = new String[]{"Rectangle", "Ellipse", "Line"};

	ButtonGroup shapeModeButtonGroup = new ButtonGroup();
	ItemListener toggleShapeModeItemListener 
	    = new ToggleShapeModeItemListener(_slate);

	for (int size = shapeModeStrings.length, i = 0; i < size; ++i) {
	    JRadioButtonMenuItem shapeModeSubMenu 
		= new JRadioButtonMenuItem(shapeModeStrings[i]);
	    shapeModeButtonGroup.add(shapeModeSubMenu);
	    shapeModeMenu.add(shapeModeSubMenu);
	    shapeModeSubMenu.addItemListener(toggleShapeModeItemListener);
	    if ( i == 0 ) { 
		shapeModeSubMenu.doClick(); // select first menu
	    }
	}
    }
}

/**
 * Common mouse listener for various drawing modes.
 * The change of mode is effected by installing different
 * MouseListener to the slate. Each mosue listener interprets
 * the mouse actions that is suitable for type of shape it draws.
 * This class is suitable to be extended by Shapes that are defined
 * by two location: the starting point and the ending point.
 *
 * The sub-classes must provide implementation for the abstract method
 * createShape().
 *
 * This class could also implement MouseMotionListener if we want to give
 * feedback to user about the Shape that will get drawn on mouse release
 * But for now, keep it simple.
 */
abstract class ShapeDrawMouseListener extends MouseAdapter {
    private Point _pressPoint;

    /**
     * Creates a shape defined by provided point
     * Abstract method to be implemented by sub-classes to return
     * a Shape object by interpreting the starting and ending point
     * in some way.
     * @param startPoint the point at which the shape starts
     * @param endPoint the point at which the shape ends
     * @return shape defined by startPoint and endPoint
     */
    public abstract Shape createShape(Point startPoint, Point endPoint);

    /**
     * Respond to mouse pressed event
     * Stores the location of mouse pressed to be used at starting point.
     * @param e mouse event
     */
    public void mousePressed(MouseEvent e) {
	_pressPoint = e.getPoint();
    }
    
    /**
     * Respond to mouse release event
     * Creates a shape (by calling abstract method) and adds it to the
     * slate
     * @param e mouse event
     */
    public void mouseReleased(MouseEvent e) {
	SlateModel slateModel = ((Slate)e.getSource()).getModel();
	slateModel.addShape(createShape(_pressPoint, e.getPoint()));
    }

    //-------------------------------------------------------------
    // Protected methods
    //-------------------------------------------------------------
    /**
     * Convenience method that returns the bound given two points
     * @param first a point
     * @param second another point
     * @return smallest rectangle that bound both the supplied points
     */
    protected Rectangle getRectBound(Point first, Point second) {
	Rectangle boundingRect = new Rectangle(first);
	boundingRect.add(second);
	return boundingRect;
    }
}

/**
 * MouseListener that draws a rectangle.
 */
class RectangleShapeDrawMouseListener extends ShapeDrawMouseListener {
    /**
     * Implement the abstract method in base class to create a rectangle.
     * This rectangle is bounded by the starting and ending points given
     * @param startPoint the point at which the shape starts
     * @param endPoint the point at which the shape ends
     * @return rectangle bound by startPoint and endPoint
     */
    public Shape createShape(Point startPoint, Point endPoint) {
	Rectangle boundingRect = getRectBound(startPoint, endPoint);
	return new Rectangle2D.Float(boundingRect.x, boundingRect.y,
				      boundingRect.width, boundingRect.height);
    }
}

/**
 * MouseListener that draws a ellipse.
 */
class EllipseShapeDrawMouseListener extends ShapeDrawMouseListener {
    /**
     * Implement the abstract method in base class to create a ellipse.
     * This ellipse is bounded by the starting and ending points given
     * @param startPoint the point at which the shape starts
     * @param endPoint the point at which the shape ends
     * @return ellipse bound by startPoint and endPoint
     */
    public Shape createShape(Point startPoint, Point endPoint) {
	Rectangle boundingRect = getRectBound(startPoint, endPoint);
	return new Ellipse2D.Float(boundingRect.x, boundingRect.y,
				    boundingRect.width, boundingRect.height);
    }
}

/**
 * MouseListener that draws a line.
 */
class LineShapeDrawMouseListener extends ShapeDrawMouseListener {
    /**
     * Implement the abstract method in base class to create a line.
     * This line starts at the starting and ends at the ending point given
     * @param startPoint the point at which the shape starts
     * @param endPoint the point at which the shape ends
     * @return line bound by startPoint and endPoint
     */
    public Shape createShape(Point startPoint, Point endPoint) {
	return  new Line2D.Float(startPoint, endPoint);
    }
}

/**
 * A common item listener for all shape mode switching menus
 * When a menu switching mode is activated, it removes the last installed
 * ShapeDrawMouseListener and installs a new one that corresponds to the
 * new shape mode.
 */
class ToggleShapeModeItemListener implements ItemListener {
    private Slate _slate;
    private ShapeDrawMouseListener _currentListener;

    /**
     * Constructor.
     * @param slate slate for which mouse listeners are switched
     */
    public ToggleShapeModeItemListener(Slate slate){
	_slate = slate;
    }

    /**
     * Respond to change in items state by switching drawing mode.
     * @param e item event 
     */
    public void itemStateChanged(ItemEvent e) {
	JRadioButtonMenuItem rb = (JRadioButtonMenuItem) e.getSource();
	if(! rb.isSelected()) {
	    return;
	}
	String buttonText = rb.getText();
	_slate.removeMouseListener(_currentListener);
	if (buttonText.equals("Rectangle")) {
	    _currentListener = new RectangleShapeDrawMouseListener();
	} else if (buttonText.equals("Ellipse")) {
	    _currentListener = new EllipseShapeDrawMouseListener();
	} else if (buttonText.equals("Line")) {
	    _currentListener = new LineShapeDrawMouseListener();
	}
	_slate.addMouseListener(_currentListener);
    }
}