/* @(#) DrawShapes.java 1.1 99/7/24
 * Copyright (c) 1999 Lawrence Rodrigues
 */
package com.vistech.graphics;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.*;
import com.vistech.shapes.*;
import java.util.*;
import java.beans.*;
/** Enables users to interactively draw a variety of shapes.
  * @version 1.0  24  July 199
  * @author Larry Rodrigues
  * This class performs shape-related tasks.These include draw, move, fill,
  * erase, copy and paste. It expects a client object to set the task type and shape type.
  ***/
public class DrawShapes implements DrawShapesController, MouseListener,MouseMotionListener, KeyListener{
   protected int shapeType = Drawable.BOX;
   protected int savedShapeType = Drawable.BOX;
   protected GraphicsDisplay grCanvas;
   private Point oldPoint, currentPoint;
   private Point diff = new Point(0,0);
   private Point anchor  = new Point(0,0);
   private boolean mousePressed = false;
   private Shapes currentShape, prevShape;
   private Drawable shape;
   private Vector shapesList = new Vector();
   private Vector savedShapesList = new Vector();
   private Graphics2D destGc;
   private int currentCommand = DrawShapesController.DRAW;
   private int shapeCount = 0;
   private Vector shapesEventListeners = new Vector();

   private boolean graphicsOn = true, shapesOn = true, textOn = false,dragOn = false;

   private boolean shapeNumberingOn = true;
   private boolean defaultFillOn = false;
   private boolean firstTime = true;
   private Shapes copiedShape, prevCopiedShape;
   private String commandString ="Draw";
   private PropertyChangeSupport pcs;

   public DrawShapes(GraphicsDisplay c){
      grCanvas = c;
      destGc = grCanvas.getDisplayedImageGC();
      shape = (Drawable)ShapeFactory.createShapes(Drawable.BOX);
      pcs = new PropertyChangeSupport(this);
   }

   public synchronized void setTextOn(boolean onOrOff){ textOn = onOrOff; }
   public synchronized void setShapesOn(boolean onOrOff){ shapesOn = onOrOff; }

   public synchronized void setShapeType(int shapeType){
      int oldType = this.shapeType;
      this.shapeType = shapeType;
      if(shapeType  == Drawable.ANNOTEXT){
         setTextOn(true);
         setShapesOn(false);
      } else{
         setTextOn(false);
         setShapesOn(true);
      }
      pcs.firePropertyChange("shapeType", oldType, shapeType);
   }
   public int getShapeType(){ return shapeType; }

   public synchronized void setCommandAsString(String command){
      String oldCommand =this.commandString;
      this.commandString = command;
      pcs.firePropertyChange("commandString", oldCommand, commandString);
   }
   public String getCommandAsString(){ return commandString; }

   public synchronized void setCommand(int command){
      pcs.firePropertyChange("command", currentCommand, command);
      setCommandAsString(getCommandAsText(command));
      currentCommand = command;
      switch (currentCommand) {
         case DrawShapesController.PASTE:
            pasteShape(oldPoint.x+10,oldPoint.y+10);
            setCommand(DrawShapesController.MOVE);
            break;
         case DrawShapesController.ERASE_ALL:eraseShapes(); break;
         case DrawShapesController.LOAD:loadShapes();break;
         case DrawShapesController.SAVE:saveShapes();break;
         default: return;
      }
   }
   public int getCommand(){ return currentCommand; }

   // Mouse Events
   public void mousePressed(MouseEvent e) {
      if(SwingUtilities.isLeftMouseButton(e)){
        initGraphics(e.getX(), e.getY());
      }
   }

   public void mouseReleased(MouseEvent e) {
      if(SwingUtilities.isRightMouseButton(e)) return;
      int x = e.getX();
      int y = e.getY();
      drawGraphics(x,y);
      if(dragOn) {
         dragOn = false;
         if((shapesOn) && (shape != null)) releaseDrag(x,y);
      }
      mousePressed = false;
   }

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

   public void mouseDragged(MouseEvent e){
      if(SwingUtilities.isRightMouseButton(e)) return;
      dragOn = true;
      int x = e.getX();
      int y = e.getY();
      drawGraphics(x,y);
   }
   public void mouseMoved(MouseEvent e){}

   protected void initGraphics(int x, int y) {
      destGc = grCanvas.getDisplayedImageGC();
      if(shape != null) {
         if(((Shapes)shape).getSmallRectsOn()){
             ((Shapes)shape).drawSmallRects(destGc);
         }
      }
      if(prevCopiedShape != null) {
         if(((Shapes)prevCopiedShape).getSmallRectsOn()){
             ((Shapes)prevCopiedShape).drawSmallRects(destGc);
         }
      }
      switch (currentCommand) {
         case DrawShapesController.DRAW:
           createAndDrawShape(x,y);
           return;
         case  DrawShapesController.PASTE:
           pasteShape(x,y);
           return;
         case DrawShapesController.MOVE:
         case DrawShapesController.COPY:
         case DrawShapesController.FILL:
         case DrawShapesController.ERASE:
           hitTest(x,y,currentCommand);
           return;
      }
   }

    protected void createAndDrawShape(int x, int y) {
       destGc = grCanvas.getDisplayedImageGC();
       if(shapesOn){
          oldPoint = new Point(x, y);
          currentPoint = new Point(x, y);
          anchor = new Point(x, y);
          shape = (Drawable)ShapeFactory.createShapes(shapeType);
          ((Shapes)shape).drawingColor = grCanvas.getDrawingColor();
       }else {
          shape = (Drawable)ShapeFactory.createShapes(shapeType);
          Color textColor = grCanvas.getTextColor();
          destGc.setFont(grCanvas.getFont());
          ((Shapes)shape).setDrawingColor(textColor);
         ((JComponent)grCanvas).requestFocus();
       }
       if(shape != null){
          shapesList.addElement(shape);
          shape.init(x, y);
          grCanvas.repaint();
       }
    }
    protected void pasteShape(int x, int y) {
        if(copiedShape != null){
           ((Drawable) copiedShape).move(destGc, x-oldPoint.x, y-oldPoint.y);
           shapesList.addElement(copiedShape);
           oldPoint = new Point(x, y);
           currentPoint = new Point(x, y);
           prevCopiedShape = copiedShape;
           copiedShape = ShapeFactory.copyShape( (Shapes)copiedShape);
          grCanvas.repaint();
        }
     }

     protected void moveShape(int x, int y) {
         if(shape == null) return;
         destGc = grCanvas.getDisplayedImageGC();
         shape.move(destGc, x-oldPoint.x, y-oldPoint.y);
         oldPoint = new Point(x, y);
         currentPoint = new Point(x, y);
         grCanvas.repaint();
     }

     protected void hitTest(int x, int y, int command){
        for(Enumeration e = shapesList.elements() ; e.hasMoreElements() ;) {
            Shapes sh = (Shapes)(e.nextElement());
            if(sh.contains(destGc,x,y)){
               oldPoint = new Point(x, y);
               currentPoint = new Point(x, y);
               shape = (Drawable)sh;
               switch (command) {
                  case DrawShapesController.MOVE :
                    grCanvas.repaint();
                    break;
                  case DrawShapesController.FILL :
                    if(shape instanceof Fillable) {
		     ((Fillable)shape).fill(destGc, grCanvas.getFillColor());
                       grCanvas.repaint();
                    }
                    break;
                  case DrawShapesController.ERASE :
                     shape.erase(grCanvas.getDisplayedImageGC());
                     shapesList.removeElement(sh);
                     grCanvas.repaint();
                     break;
                  case DrawShapesController.COPY:
                    copiedShape = ShapeFactory.copyShape( (Shapes)shape);
                    grCanvas.repaint();
                    break;
               }
               break;
            } else shape = null;
        }
    }

    //Draws graphics when mouse is dragged. There is no text drawing when mouse is dragged.
    protected void drawGraphics(int x, int y) {
       switch (currentCommand) {
         case DrawShapesController.DRAW :
            if((shapesOn) &&(shape != null)) {
               shape.setCurrentPosition(new Point(x,y));
               shape.drawInteractive(grCanvas.getDisplayedImageGC());
               grCanvas.repaint();
            }
            break;
         case DrawShapesController.MOVE:
            moveShape(x, y);
            break;
       }
    }

    protected void releaseDrag(int x, int y){
       switch (currentCommand) {
          case DrawShapesController.MOVE:
            ((Shapes)shape).drawSmallRects(grCanvas.getDisplayedImageGC());
            break;
          case DrawShapesController.DRAW:
            if(shapeNumberingOn) {}
            shapeCount++;
            break;
      }
   }

   public void keyTyped(KeyEvent k) {
      if(!textOn) return;
      if((currentCommand == DrawShapesController.DRAW) && (shape instanceof Typable)){
         destGc = grCanvas.getDisplayedImageGC();
         destGc.setColor(Color.green);
        ((Typable)shape).setCurChar(k.getKeyChar());
        shape.drawInteractive(destGc);
        grCanvas.repaint();
      }
   }

   public void keyReleased(KeyEvent k){}
   public void keyPressed(KeyEvent k){}

   protected void redraw(){
       saveShapes();
       grCanvas.reset();
       loadShapes();
       grCanvas.repaint();
   }

   public void loadShapes() {
      eraseShapes();
      for (Enumeration e = savedShapesList.elements() ; e.hasMoreElements() ;) {
           Drawable sh = (Drawable)e.nextElement();
           shapesList.addElement(sh);
           sh.draw(grCanvas.getDisplayedImageGC());
           grCanvas.repaint();
      }
      grCanvas.repaint();
   }

   public void saveShapes() {
      savedShapesList = new Vector();
      for (Enumeration e = shapesList.elements() ; e.hasMoreElements() ;) {
           savedShapesList.addElement(e.nextElement());
      }
   }

   public void eraseShapes() {
      currentShape = null;
      shape = null;
      prevShape = null;
      copiedShape = null;
      prevCopiedShape = null;
      grCanvas.reset();
      grCanvas.repaint();
      shapeCount= 0;
      shapesList = new Vector();
   }

   public void reset() {
      currentShape = null;
      shape = null;
      prevShape = null;
      copiedShape = null;
      prevCopiedShape = null;
      currentCommand = DrawShapesController.DRAW;
      shapeCount =0;
      shapesList = new Vector();
      savedShapesList = new Vector();
      grCanvas.reset();
      grCanvas.repaint();
   }

   public void addPropertyChangeListener(PropertyChangeListener p){
      pcs.addPropertyChangeListener(p);
   }

    public void removePropertyChangeListener(PropertyChangeListener p){
       pcs.removePropertyChangeListener(p);
   }

   public void addShapesEventListener(ShapesEventListener s){
      shapesEventListeners.addElement(s);
   }

   public void removeShapesEventListener(ShapesEventListener s){
      shapesEventListeners.removeElement(s);
   }

   protected void fireShapesEvent() {
       ShapesEvent ise = new ShapesEvent(this,currentShape);
       for(Enumeration e = shapesEventListeners.elements(); e.hasMoreElements();){
           ShapesEventListener ist = (ShapesEventListener)(e.nextElement());
           ist.shapeDrawn(ise);
       }
   }

   public static void  main(String[] args){
      GraphicsCanvas2D gc = new GraphicsCanvas2D();
      DrawShapes dss = new DrawShapes(gc);
      gc.addMouseListener(dss);
      gc.addMouseMotionListener(dss);
      gc.setSize(600,400);
      JFrame fr = new JFrame();
      fr.addWindowListener(
        new WindowAdapter(){
          public void windowClosing(WindowEvent e){
             System.exit(0);
          }
        }
       );
       fr.setTitle("Graphics Canvas");
       fr.getContentPane().add((Component)gc);
       fr.pack();
       fr.setSize(400,300);
       fr.show();
       fr.repaint();
   }

   public static String getCommandAsText(int command) {
      switch(command) {
          case DrawShapesController.DRAW: return "Draw";
          case DrawShapesController.MOVE: return "Move";
          case DrawShapesController.FILL: return "Fill";
          case DrawShapesController.ERASE: return "Erase";
          case DrawShapesController.COPY: return "Copy";
          case DrawShapesController.PASTE: return "Paste";
          case DrawShapesController.SAVE: return "Save Shapes";
          case DrawShapesController.ERASE_ALL: return "Erase All";
          case DrawShapesController.LOAD: return "Load Shapes";
          default: return "Draw";
      }
   }
}