import java.awt.*;                  // For Graphics
import java.awt.event.*;            // For events
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;         // For mouse input adapter
import java.util.*;                 // For Observer

class SketchView extends JComponent implements Observer, Constants
{
  public SketchView(Sketcher theApp)
  {
    this.theApp = theApp;
    MouseHandler handler = new MouseHandler();         // Create the mouse listener
    addMouseListener(handler);                         // Listen for button events
    addMouseMotionListener(handler);                   // Listen for motion events
  }

  public void paint(Graphics g)
  {
    Graphics2D g2D = (Graphics2D)g;                     // Get a 2D device context
    Iterator elements = theApp.getModel().getIterator();
    Element element;                                    // Stores an element

    while(elements.hasNext())                           // Go through the list
    {
      element = (Element)elements.next();               // Get the next element
      element.draw(g2D);                                // Draw its shape
    }
  }

	// Method called by Observable object when it changes
    public void update(Observable o, Object rectangle)
    {
      if(rectangle == null)
        repaint();
      else
        repaint((Rectangle)rectangle);
    }
  
  class MouseHandler extends MouseInputAdapter
  {
    public void mousePressed(MouseEvent e)
    {
      start = e.getPoint();                              // Save cursor position
      int modifier = e.getModifiers();                   // Get modifiers

      if((modifier & e.BUTTON1_MASK) != 0)
      {
        g2D = (Graphics2D)getGraphics();                 // Get graphics context
        g2D.setXORMode(getBackground());                 // Set XOR mode
        g2D.setPaint(theApp.getWindow().getElementColor());   // Set color
      }
    }

    public void mouseDragged(MouseEvent e)
    {
      last = e.getPoint();                               // Save cursor position
      int modifier = e.getModifiers();                   // Get modifiers

      if((modifier & e.BUTTON1_MASK) != 0 && (theApp.getWindow().getElementType() != TEXT))
      {
        if(tempElement == null)                         // Is there an element?
          tempElement = createElement(start, last);     // No, so create one
        else
        {
          tempElement.draw(g2D);                        // Yes - draw to erase it
          tempElement.modify(start, last);              // Now modify it
        }
        tempElement.draw(g2D);                          // and draw it
      }
    }

    public void mouseReleased(MouseEvent e)
    {
      int modifier = e.getModifiers();         // Get modifiers

      if(e.isPopupTrigger())
      {
        start = e.getPoint();
        theApp.getWindow().getPopup().show((Component)e.getSource(), start.x, start.y); 
        start = null;
      }

	  else if((modifier & e.BUTTON1_MASK) != 0 && (theApp.getWindow().getElementType() != TEXT))
      {
        if(tempElement != null)
        {
          theApp.getModel().add(tempElement);  // Add element to the model
          tempElement = null;                  // No temporary stored
        }
        if(g2D != null)
        {
          g2D.dispose();                       // Release graphic context resource
          g2D = null;                          // Set it to null
        }
        start = last = null;                   // Remove the points
      }
    }

    public void mouseClicked(MouseEvent e)
    {
      int modifier = e.getModifiers();                   // Get modifiers
      if((modifier & e.BUTTON1_MASK) != 0 && 
         (theApp.getWindow().getElementType() == TEXT))
      {
        start = e.getPoint();               // Save cursor position - start of text
        String text = JOptionPane.showInputDialog(
                   (Component)e.getSource(),              // Used to get the frame
                   "Enter Text:",                         // The message
                   "Dialog for Text Element",             // Dialog title
                   JOptionPane.PLAIN_MESSAGE);            // No icon

        if(text != null)                                  // If we have text
        {                                                 // create the element
          g2D = (Graphics2D)getGraphics();
          Font font = theApp.getWindow().getCurrentFont();

          // Create the text element
          tempElement = new Element.Text(font, 
		                                 text, 
                                         start, 
                                         theApp.getWindow().getElementColor(), 
										 font.getStringBounds(text, 
										 g2D.getFontRenderContext()).getBounds());

          if(tempElement != null)                         // If we created one
            theApp.getModel().add(tempElement);           // add it to the model
          tempElement = null;
          g2D.dispose();
          g2D = null;
          start = null;
        }
      }
    }

	// Handle mouse moves
    public void mouseMoved(MouseEvent e)
    {
      Point currentCursor = e.getPoint();          // Get current cursor position
      Iterator elements = theApp.getModel().getIterator();
      Element element;                                    // Stores an element

      while(elements.hasNext())                           // Go through the list
      {
        element = (Element)elements.next();               // Get the next element
        if(element.getBounds().contains(currentCursor))   // Under the cursor?
        {
          if(element==highlightElement)            // If its already highlighted
            return;                                // we are done
          g2D = (Graphics2D)getGraphics();         // Get graphics context
          if(highlightElement!=null)               // If an element is highlighted
          {
            highlightElement.setHighlighted(false);// un-highlight it and
            highlightElement.draw(g2D);            // draw it normal color
          }
          element.setHighlighted(true);            // Set highlight for new element
          highlightElement = element;              // Store new highlighted element
          element.draw(g2D);                       // Draw it highlighted 
          g2D.dispose();                           // Release graphic context resources
          g2D = null;

          return;
        }
      }

      // Here there is no element under the cursor so...
      if(highlightElement!=null)                // If an element is highlighted
      {
        g2D = (Graphics2D)getGraphics();        // Get graphics context
        highlightElement.setHighlighted(false); // ...turn off highlighting
        highlightElement.draw(g2D);             // Redraw the element
        highlightElement = null;                // No element highlighted
        g2D.dispose();                         // Release graphic context resources
        g2D = null;
      }
    }

    private Element createElement(Point start, Point end)
    {
      switch(theApp.getWindow().getElementType())
      {
        case LINE:
           return new Element.Line(start, end,
                                   theApp.getWindow().getElementColor());
        
        case RECTANGLE:
           return new Element.Rectangle(start, end,
                                        theApp.getWindow().getElementColor());
        
        case CIRCLE:
           return new Element.Circle(start, end, 
                                     theApp.getWindow().getElementColor());

        case CURVE:
           return new Element.Curve(start, end,
                                    theApp.getWindow().getElementColor());
      }
      return null;
    }  // End of class MouseHandler

    private Graphics2D g2D;                  // Temporary graphics context
    private Point start;                     // Stores cursor position on press
    private Point last;                      // Stores cursor position on drag
    private Element tempElement;             // Stores a temporary element
  }

  private Sketcher theApp;                   // The application object
  private Element highlightElement;          // Highlighted element
}