//######################################################################################
/*
 * Created on 04-Mar-2004
 * Author is Michael Camacho
 *
 */
//######################################################################################
package pipe.dataLayer;

import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.RectangularShape;

import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.event.MouseInputAdapter;
import javax.swing.text.DefaultHighlighter;

import pipe.gui.CreateGui;
import pipe.gui.Grid;
//######################################################################################
public class AnnotationNote extends PetriNetObject {
//######################################################################################	
	private JTextArea note = new JTextArea();
	private boolean drawBorder = true;
	private RectangularShape noteRect = new Rectangle();
	private ResizePoint[] dragPoints = new ResizePoint[8];
//######################################################################################
	public AnnotationNote(int x, int y){
		note.setAlignmentX(Component.CENTER_ALIGNMENT);
		note.setAlignmentY(Component.CENTER_ALIGNMENT);
		note.setOpaque(false);
		note.setEditable(false);
		note.setEnabled(false);
		note.setLineWrap(true);
		note.setWrapStyleWord(true);
		// Set minimum size the preferred size for an empty string:
		note.setText("");
		note.setFont(new Font(ANNOTATION_DEFAULT_FONT, Font.PLAIN, ANNOTATION_DEFAULT_FONT_SIZE));
		note.setSize(note.getPreferredSize().width,note.getPreferredSize().height);
		note.setMinimumSize(note.getPreferredSize());
		note.setHighlighter(new DefaultHighlighter());
		note.setDisabledTextColor(NOTE_DISABLED_COLOUR);
		note.setForeground(NOTE_EDITING_COLOUR);
		note.setCaretColor(NOTE_EDITING_COLOUR);
		note.addKeyListener(new AnnotationKeyUpdateHandler(this));
		
		add(note);
		setLocation(x - RESERVED_BORDER/2, y - RESERVED_BORDER/2);
		
		dragPoints[0] = new ResizePoint(this, ResizePoint.TOP | ResizePoint.LEFT);
		dragPoints[1] = new ResizePoint(this, ResizePoint.TOP);
		dragPoints[2] = new ResizePoint(this, ResizePoint.TOP | ResizePoint.RIGHT);
		dragPoints[3] = new ResizePoint(this, ResizePoint.RIGHT);
		dragPoints[4] = new ResizePoint(this, ResizePoint.BOTTOM | ResizePoint.RIGHT);
		dragPoints[5] = new ResizePoint(this, ResizePoint.BOTTOM);
		dragPoints[6] = new ResizePoint(this, ResizePoint.BOTTOM | ResizePoint.LEFT);
		dragPoints[7] = new ResizePoint(this, ResizePoint.LEFT);
		
		ResizePointHandler handler;
		for (int i=0; i<8; i++) {
			handler = new ResizePointHandler(dragPoints[i]);
			dragPoints[i].addMouseListener(handler);
			dragPoints[i].addMouseMotionListener(handler);
			add(dragPoints[i]);
		}
	}
//######################################################################################
	public AnnotationNote(String id, String text, int x, int y){
		this(x, y);
		this.id = id;
		note.setText(text);
		note.setSize(note.getPreferredSize().width,note.getPreferredSize().height);
		updateBounds();
	}
//######################################################################################
	public AnnotationNote(String text, int x, int y, int w, int h, boolean border){
		this(x, y);
		
		note.setText(text);
		drawBorder = border;
//		int width = note.getPreferredSize().width;
//		int height = note.getPreferredSize().height;
//		width = (w > width) ? w : width;
//		height = (h > height) ? h : height;
		note.setSize(w, h);
		updateBounds();
	}
//######################################################################################
	public void updateBounds(){
//    setLocation(Grid.getModifiedX(getX() + RESERVED_BORDER/2) - RESERVED_BORDER/2,
//        Grid.getModifiedY(getY() + RESERVED_BORDER/2) - RESERVED_BORDER/2);

		int newHeight = note.getPreferredSize().height;
		
		if ((note.getHeight() < newHeight) && (newHeight >= note.getMinimumSize().height))
			note.setSize(note.getWidth(), newHeight);
		
		int rectWidth = note.getWidth() + RESERVED_BORDER;
		int rectHeight = note.getHeight() + RESERVED_BORDER;
		
//		float spacing = Grid.getGridSpacing();
//		
//		rectWidth = (int)(((int)(rectWidth / spacing) + 1) * spacing);
//		rectHeight = (int)(((int)(rectHeight / spacing) + 1) * spacing);
		
		noteRect.setFrame(RESERVED_BORDER/2, RESERVED_BORDER/2, rectWidth, rectHeight);
		
		setSize(rectWidth + RESERVED_BORDER + ANNOTATION_SIZE_OFFSET,
				rectHeight + RESERVED_BORDER + ANNOTATION_SIZE_OFFSET);
		
		note.setLocation((int)noteRect.getX() + (rectWidth - note.getWidth())/2,
							(int)noteRect.getY() + (rectHeight - note.getHeight())/2);
		
		updatePointLocations();
	}
//######################################################################################
	private void updatePointLocations() {
		dragPoints[0].setLocation(noteRect.getMinX(), noteRect.getMinY());		// TOP-LEFT
		dragPoints[1].setLocation(noteRect.getCenterX(), noteRect.getMinY());	// TOP-MIDDLE
		dragPoints[2].setLocation(noteRect.getMaxX(), noteRect.getMinY());		// TOP-RIGHT
		dragPoints[3].setLocation(noteRect.getMaxX(), noteRect.getCenterY());	// MIDDLE-RIGHT
		dragPoints[4].setLocation(noteRect.getMaxX(), noteRect.getMaxY());		// BOTTOM-RIGHT
		dragPoints[5].setLocation(noteRect.getCenterX(), noteRect.getMaxY());	// BOTTOM-MIDDLE
		dragPoints[6].setLocation(noteRect.getMinX(), noteRect.getMaxY());		// BOTTOM-LEFT
		dragPoints[7].setLocation(noteRect.getMinX(), noteRect.getCenterY());	// MIDDLE-LEFT			
	}
//######################################################################################
	public void deselect()
	{
		super.deselect();
		disableEditMode();
	}
//######################################################################################
	public void disableEditMode()
	{
		note.setOpaque(false);
		note.setEditable(false);
		note.setEnabled(false);
		CreateGui.getApp().enableGuiMenu();		
	}
//######################################################################################
	public void enableEditMode()
	{
		note.setEditable(true);
		note.setEnabled(true);
		note.setOpaque(true);
		note.requestFocus();
		CreateGui.getApp().disableGuiMenu();	
	}
//######################################################################################
	public boolean isShowingBorder(){
		return drawBorder;
	}
//######################################################################################
	public void showBorder(boolean show){
		drawBorder = show;
	}
//######################################################################################
	public void setID(String id){
		this.id = id;
	}
//######################################################################################
	public String getID(){
		return id;
	}
//######################################################################################
	public JTextArea getNote(){
		return note;
	}
//######################################################################################
	public String getNoteText(){
		return note.getText();
	}
//######################################################################################
	public int getNoteWidth(){
		return note.getWidth();
	}
//	######################################################################################
	public int getNoteHeight(){
		return note.getHeight();
	}
//######################################################################################
	/** Translates the component by x,y */
	public void translate(int x, int y) {
		setLocation(getX() + x, getY() + y);
//		updateBounds();
	}
//######################################################################################
	public void adjustTop(int dy){
		if (note.getPreferredSize().height <= (note.getHeight() - dy))
		{
			note.setSize(new Dimension(note.getWidth(), note.getHeight() - dy));
			setLocation(getX(), getY() + dy);
		}
	}
//######################################################################################
	public void adjustBottom(int dy){
		if (note.getPreferredSize().height <= (note.getHeight() + dy))
		{
			note.setSize(new Dimension(note.getWidth(), note.getHeight() + dy));
		}
	}	
//######################################################################################
	public void adjustLeft(int dx){
		if (ANNOTATION_MIN_WIDTH <= (note.getWidth() - dx))
		{
			note.setSize(new Dimension(note.getWidth() - dx, note.getHeight()));
			setLocation(getX() + dx, getY());	
		}
	}	
//######################################################################################
	public void adjustRight(int dx){
		if (ANNOTATION_MIN_WIDTH <= (note.getWidth() + dx))
		{
			note.setSize(new Dimension(note.getWidth() + dx, note.getHeight()));		
		}
	}	
//######################################################################################
	public boolean contains(int x, int y) {
		boolean pointContains = false;
		for (int i=0; i<8; i++)
			pointContains |= dragPoints[i].contains(x - dragPoints[i].getX(), y - dragPoints[i].getY());
		return noteRect.contains(x, y) || pointContains;
	}
//######################################################################################
	public void paintComponent(Graphics g) {

		updateBounds();
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		g2.setStroke(new BasicStroke(1.0f));
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
		
		if (selected && !ignoreSelection) {
			g2.setPaint(SELECTION_FILL_COLOUR);
			g2.fill(noteRect);
			if (drawBorder){
				g2.setPaint(SELECTION_LINE_COLOUR);
				g2.draw(noteRect);
			}
		} 
		else {
			g2.setPaint(ELEMENT_FILL_COLOUR);
			g2.fill(noteRect);
			if (drawBorder){
				g2.setPaint(ELEMENT_LINE_COLOUR);
				g2.draw(noteRect);
			}
		}		
	}
//######################################################################################
	public class ResizePoint extends JComponent
	{
		public static final int SIZE = 4;
		public static final int TOP = 1;
		public static final int BOTTOM = 2;
		public static final int LEFT = 4;
		public static final int RIGHT = 8;
		
		private final Rectangle shape = new Rectangle(0,0,2*SIZE,2*SIZE);
		boolean isPressed = false;	
		AnnotationNote myNote;
		private int typeMask;
		
		public ResizePoint(AnnotationNote obj, int type) {
			myNote = obj;
			setOpaque(false);
			setBounds(-SIZE, -SIZE, 2*SIZE + ANNOTATION_SIZE_OFFSET, 2*SIZE + ANNOTATION_SIZE_OFFSET);
			typeMask = type;
		}
		
		public void setLocation(double x, double y){
			super.setLocation((int)(x - SIZE), (int)(y - SIZE));
		}
		
		public void drag(int x, int y){
			if ((typeMask & TOP) == TOP)
				myNote.adjustTop(y);
			if ((typeMask & BOTTOM) == BOTTOM)
				myNote.adjustBottom(y);
			if ((typeMask & LEFT) == LEFT)
				myNote.adjustLeft(x);
			if ((typeMask & RIGHT) == RIGHT)
				myNote.adjustRight(x);
//			myNote.updateBounds();
		}
		
		public void paintComponent(Graphics g) {
			if (myNote.selected && !PetriNetObject.ignoreSelection)
			{	
				super.paintComponent(g);
				Graphics2D g2 = (Graphics2D)g;
				g2.setStroke(new BasicStroke(1.0f));
				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				
				if (isPressed)
					g2.setPaint(RESIZE_POINT_DOWN_COLOUR);
				else
					g2.setPaint(ELEMENT_FILL_COLOUR);
				g2.fill(shape);
				g2.setPaint(ELEMENT_LINE_COLOUR);
				g2.draw(shape);
			}
		}
	}
//######################################################################################
	public class ResizePointHandler extends MouseInputAdapter {
		
		private ResizePoint myPoint;
		private int startX, startY;
		
		public ResizePointHandler(ResizePoint point) {
			myPoint = point;
		}	
		
		
		public void mousePressed(MouseEvent e)
		{
			myPoint.myNote.disableEditMode();
			myPoint.myNote.setDraggable(false);
			myPoint.isPressed = true;
			myPoint.repaint();
			startX = e.getX();
			startY = e.getY();
		}
		
		public void mouseDragged(MouseEvent e)
		{
			myPoint.drag(Grid.getModifiedX(e.getX() - startX), Grid.getModifiedY(e.getY() - startY));
		}
		
		public void mouseReleased(MouseEvent e)
		{			
			myPoint.myNote.setDraggable(true);
			myPoint.isPressed = false;
			myPoint.repaint();
		}
	}	
//######################################################################################
	public class AnnotationKeyUpdateHandler implements KeyListener {
	
		private AnnotationNote myAnnotation;

		public AnnotationKeyUpdateHandler(AnnotationNote annotation) {
			myAnnotation = annotation;
		}

		public void keyPressed(KeyEvent e) {
			myAnnotation.repaint();
		}

		public void keyReleased(KeyEvent e) {
      // empty
		}

		public void keyTyped(KeyEvent e) {
      // empty
		}
	}
//######################################################################################
}
//######################################################################################