package pipe.gui;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JLayeredPane;
import javax.swing.event.MouseInputAdapter;

import pipe.dataLayer.AnnotationNote;
import pipe.dataLayer.Arc;
import pipe.dataLayer.ArcPathPoint;
import pipe.dataLayer.PetriNetObject;
import pipe.dataLayer.Place;
import pipe.dataLayer.PlaceTransitionObject;
import pipe.dataLayer.Transition;

/**
 * The petrinet is drawn onto this frame.
 */
public class GuiView extends JLayeredPane implements Observer, Constants, Printable {
	public boolean netChanged = false;
//	public HandlerFactory handlerFactory;
	public boolean animationmode = false;
//	private PetriNetObject pnObject;
//	private boolean draggable = false;
	public Arc createArc;	//no longer static
  private AnimationHandler animationHandler = new AnimationHandler();
	
	boolean shiftDown = false;
	
	private SelectionObject selection;
//######################################################################################  
	public GuiView() {
		setLayout(null);
		setOpaque(true);
		setDoubleBuffered(true);
		setBackground(ELEMENT_FILL_COLOUR);
		// set up the HandlerFactory - we want one per GuiView/model
//		handlerFactory = new HandlerFactory(this);
		
		MouseHandler handler = new MouseHandler();
		
		setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
		
		addMouseListener(handler);
		addMouseMotionListener(handler);
		
		selection = new SelectionObject(this);
	}
//###################################################################################### 
	public void addNewPetriNetObject(PetriNetObject newObject) {
		
		if (newObject != null) {
			
			int l =  newObject.getMouseListeners().length;
			
			if (l==0) {
				if (newObject instanceof Place) {
					PlaceHandler placeHandler = new PlaceHandler(this, (Place)newObject);
					newObject.addMouseListener(placeHandler);
					newObject.addMouseMotionListener(placeHandler);
					add(newObject);	
				}
				else if (newObject instanceof Transition) {
//					AnimationHandler animationHandler = new AnimationHandler();
					TransitionHandler transitionHandler = new TransitionHandler(this, (Transition)newObject);
					newObject.addMouseListener(transitionHandler);
					newObject.addMouseMotionListener(transitionHandler);
					newObject.addMouseListener(animationHandler);
					add(newObject);	
				}
				else if (newObject instanceof Arc) {
					add(newObject);
					ArcHandler arcHandler = new ArcHandler(this, (Arc)newObject);
					newObject.addMouseListener(arcHandler);
					newObject.addMouseMotionListener(arcHandler);
				}
				else if (newObject instanceof AnnotationNote) {
					add(newObject);
					AnnotationNoteHandler noteHandler =
							new AnnotationNoteHandler(this, (AnnotationNote)newObject);
					newObject.addMouseListener(noteHandler);
					newObject.addMouseMotionListener(noteHandler);					
					((AnnotationNote)newObject).getNote().addMouseListener(noteHandler);
					((AnnotationNote)newObject).getNote().addMouseMotionListener(noteHandler);
				}
			}
		}
		
		repaint();
	}
//######################################################################################  
	public void update(Observable o, Object diffObj) {
		
		if((diffObj instanceof PetriNetObject) && (diffObj != null))
		{
			if (CreateGui.appGui.getMode() == CREATING)
			{
				addNewPetriNetObject((PetriNetObject)diffObj);
			}
			repaint();
		} 
	}
//###################################################################################### 
	public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
		if(pageIndex>0)
			return Printable.NO_SUCH_PAGE;
		Graphics2D g2D = (Graphics2D) g;
		//Move origin to page printing area corner
		g2D.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
    g2D.scale(0.5,0.5);
		print(g2D); // Draw the net
		
		return Printable.PAGE_EXISTS;
	}
//######################################################################################  
	/**
	 * This method is called whenever the frame is moved, resized etc.
	 * It iterates over the existing petrinet objects and repaints them.
	 * TODO: write a better description than this since it is now totally not happening.
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (Grid.enabled()) {
			Grid.updateSize(this);
			Grid.drawGrid(g);
		}
		selection.updateBounds();
	}
//######################################################################################    
	public void updatePreferredSize() {
		// iterate over net objects
		// setPreferredSize() accordingly
		Component[] components=getComponents();
		Dimension d=new Dimension(0,0);
		for(int i=0;i<components.length;i++) {
			if(components[i] instanceof SelectionObject) continue; // SelectionObject not included
			Rectangle r=components[i].getBounds();
			int x=r.x+r.width;
			int y=r.y+r.height;
			if(x>d.width)  d.width =x;
			if(y>d.height) d.height=y;
		}
		setPreferredSize(d);
		
		getParent().validate();
	}
	
	//  private void notifyNewDimension(int x,int y) {
	//    Dimension d=getPreferredSize();
	//    x+=100; // some extra elbow room
	//    y+=100;
	//    if(d.width <x) d.width =x;
	//    if(d.height<y) d.height=y;
	//    setPreferredSize(d);
	//  }
//######################################################################################  
	public void changeAnimationMode(boolean status) {
		animationmode = status;
	}
//######################################################################################  
	public SelectionObject getSelectionObject() {
		return selection;
	}
//######################################################################################    
	public PetriNetObject add(PetriNetObject c)
	{
		super.add(c);
		c.addedToGui();
		
		if (c instanceof ArcPathPoint)
			setLayer(c, DEFAULT_LAYER.intValue() + ARC_POINT_LAYER_OFFSET);
		else if (c instanceof Arc)
			setLayer(c, DEFAULT_LAYER.intValue() + ARC_LAYER_OFFSET);	
		else if (c instanceof PlaceTransitionObject)
			setLayer(c, DEFAULT_LAYER.intValue() + PLACE_TRANSITION_LAYER_OFFSET);
		else if (c instanceof AnnotationNote)
			setLayer(c, DEFAULT_LAYER.intValue() + ANNOTATION_LAYER_OFFSET);			
		return c;
	}  
//######################################################################################  
	public void setShiftDown(boolean down){
		shiftDown = down;
		if (createArc != null) {
			createArc.getArcPath().setFinalPointType(shiftDown);
			createArc.getArcPath().createPath();
		}
	}
//######################################################################################  
	class MouseHandler extends MouseInputAdapter{
//######################################################################################  
  private PetriNetObject pnObject;
	public void mousePressed(MouseEvent e){
			if (e.getButton() == MouseEvent.BUTTON1) {
        
        Point start = e.getPoint();
				switch(CreateGui.getApp().getMode()){
				case PLACE:
					pnObject = new Place(Grid.getModifiedX(start.x),Grid.getModifiedY(start.y));
					CreateGui.getModel().addPetriNetObject(pnObject);
					addNewPetriNetObject(pnObject);
					break;
					
        case IMMTRANS:
          pnObject = new Transition(Grid.getModifiedX(start.x),Grid.getModifiedY(start.y));
          CreateGui.getModel().addPetriNetObject(pnObject);
          addNewPetriNetObject(pnObject);
          break;

        case TIMEDTRANS:
          pnObject = new Transition(Grid.getModifiedX(start.x),Grid.getModifiedY(start.y));
          ((Transition)pnObject).setTimed(true);
          CreateGui.getModel().addPetriNetObject(pnObject);
          addNewPetriNetObject(pnObject);
          break;
          
        case ARC:
          // Add point to arc in creation
          if (createArc != null) {
            createArc.setEndPoint(Grid.getModifiedX(e.getX()), Grid.getModifiedY(e.getY()), shiftDown);
            createArc.getArcPath().addPoint(Grid.getModifiedX(e.getX()), Grid.getModifiedY(e.getY()), shiftDown);
          }
          break;
          
        case ANNOTATION:
          	pnObject = new AnnotationNote("", "", Grid.getModifiedX(e.getX()), Grid.getModifiedY(e.getY()));
            CreateGui.getModel().addPetriNetObject(pnObject);
          	addNewPetriNetObject(pnObject);
          	break;
		}

        updatePreferredSize();
	}
	}
//######################################################################################  
		public void mouseMoved(MouseEvent e) {
			if (createArc != null) {
				createArc.setEndPoint(Grid.getModifiedX(e.getX()), Grid.getModifiedY(e.getY()), shiftDown);
			}
		}
//######################################################################################  
	}
//######################################################################################  
}
//######################################################################################  