/* @(#) SahpeMarker.java 1.1 99/21/11
 * Copyright (c) 1999 Lawrence Rodrigues
 */
package com.vistech.roi;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import com.vistech.imageviewer.*;

/** Enables users to interactively mark shapes over an image. The shapes supported
  * by this class are: Freehand, rectangle, and ellipse.
  * @version 1.0  4 Oct 2000
  * @author Lawrence Rodrigues
  */
public class ShapeMarker implements  MouseListener,MouseMotionListener{
   public final static int FREEHAND = 1;
   public final static int RECTANGLE = 2;
   public final static int ELLIPSE = 4;
   protected int shapeType = FREEHAND;

   private Point diff = new Point(0,0);
   private Point shapeAnchor  = new Point(0,0);
   private boolean mousePressed = false;
   protected Shape currentShape, prevShape;
   protected DrawingCanvas imageCanvas;
   private boolean mouseDragged = false;
   private Point prevPoint, curPoint;
   protected GeneralPath path;
   protected Vector shapeEventListeners = new Vector();
   protected boolean roiOn = true;
   protected Color drawingColor = Color.white;

   /** The ShapeMarker constructor.
     * @param viewer the drawing canvas.
     **/
   public ShapeMarker(DrawingCanvas viewer){
      imageCanvas = viewer;
      imageCanvas.addMouseListener(this);
      imageCanvas.addMouseMotionListener(this);
   }

   /**
     * @param onOrOff if true, the ROI feature is turned on.
     **/

   public void setRoiOn(boolean onOrOff){roiOn = onOrOff;}
   public boolean getRoiOn() { return roiOn;}

   /** Sets the shape type.
     * @param type the type of the shape which can be one of FREEHAND, RECATNGLE, or ELLIPSE.
     **/
   public void setShapeType(int type){ shapeType = type;reset();}
   public int getShapeType() {return shapeType;}

   /** Sets the drawing color of the shape outline.
     * @param color the drawing color of the shape outline.
     **/
   public void setDrawingColor(Color color){ drawingColor = color;}
   public Color getDrawingColor(){ return drawingColor;}

   /** Erases shapes */
   public void eraseShapes(){
      imageCanvas.eraseShapes();
   }

   /** Initiates shape drawing at the specified position.
     * @param x the coordinate of the starting position of the shape.
     * @param y the coordinate of the starting position of the shape.
     */
   public void startDraw(int x, int y) {
      shapeAnchor = new Point(x,y);
      diff = new Point(0,0);
      prevPoint = new Point(x,y);
      curPoint = new Point(x,y);
      path = new GeneralPath();
      path.moveTo((float)x, (float)y);
   }

    /** Draws the current shape at the specified end position.
     * @param x the coordinate of the current end position of the shape.
     * @param y the coordinate of the current end position of the shape.
     */
   public void draw(int x, int y) {
      Graphics2D g = imageCanvas.getDisplayedImageGC();

      switch(shapeType) {
        case RECTANGLE:
        case ELLIPSE:
           diff.x = x - shapeAnchor.x;
           diff.y = y - shapeAnchor.y;
           int wid = diff.x;
           int ht = diff.y;
           Point ulhc = new Point(shapeAnchor);
           if(diff.x <0) {
              wid = -diff.x;
              ulhc.x = x;
           }
           if(diff.y <0){
              ht = -diff.y;
              ulhc.y = y;
           }
           if(shapeType == RECTANGLE) currentShape = new Rectangle(ulhc.x, ulhc.y, wid, ht);
           else currentShape = new Ellipse2D.Double(ulhc.x, ulhc.y, wid, ht);
           g.setColor(Color.white);
           if(drawingColor == Color.white)g.setColor(Color.black);
           g.setXORMode(drawingColor);
           if(prevShape != null)g.draw(prevShape);
           g.draw(currentShape);
           prevShape = currentShape;
           break;
        case FREEHAND:
           g.setPaintMode();
           curPoint = new Point(x,y);
           g.setColor(drawingColor);
           g.drawLine(prevPoint.x, prevPoint.y, x,y);
           prevPoint = curPoint;
           path.lineTo((float)x, (float)y);
        }
        imageCanvas.repaint();
   }

   /**
     *  Fires the shapes event to the registered listeners.
     **/
   protected void updateROIPanel() {
      fireShapeEvent();
   }

   /** Called when the shape drawing starts, i.e., when the mouse is pressed by the user to initiate
     * the drawing.
     * @param e the MouseEvent state object, which carries the mouse position.
     */
   public void mousePressed(MouseEvent e) {
      if(!roiOn) return;
      if(SwingUtilities.isLeftMouseButton(e))startDraw(e.getX(), e.getY());
   }

   /** Called when the shape drawing ends, i.e., when the mouse is released by the user to end
     * the drawing.
     * @param e the MouseEvent state object, which carries the mouse position.
     */
   public void mouseReleased(MouseEvent e) {
      if((!mouseDragged)|| (!roiOn)) return;
      if(SwingUtilities.isLeftMouseButton(e)){
         Graphics2D g = imageCanvas.getDisplayedImageGC();
         draw(e.getX(), e.getY());
         prevShape = null;
         if(shapeType == FREEHAND) currentShape = (Shape)path;
      }
      if(mouseDragged) updateROIPanel();
      mouseDragged = false;
   }

   public void mouseClicked(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}

   /** Called when the shape drawing is in progree, i.e., when the mouse is dragged by the user.
     * @param e the MouseEvent state object, which carries the mouse position.
     */
   public void mouseDragged(MouseEvent e){
      if(!roiOn) return;
      if(SwingUtilities.isLeftMouseButton(e)){
         mouseDragged= true;
         draw(e.getX(), e.getY());
      }
   }
   public void mouseMoved(MouseEvent e){}
   public void addShapeEventListener(ShapeEventListener s){
      shapeEventListeners.addElement(s);
    }

   public void removeShapeEventListener(ShapeEventListener s){
      shapeEventListeners.removeElement(s);
   }

   /** Fires a shape event to registered listeners */
   protected void fireShapeEvent() {
      ShapeEvent ise = new ShapeEvent(this,currentShape);
      for(Enumeration e = shapeEventListeners.elements(); e.hasMoreElements();){
          ShapeEventListener ist = (ShapeEventListener)(e.nextElement());
          ist.shapeDrawn(ise);
      }
   }
   public void reset() {
      diff = new Point(0,0);
      shapeAnchor  = new Point(0,0);
   }
}
