/*
 * Copyright (c) 2005, The JUNG Authors
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see either "license.txt"
 * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
 *
 * 
 */
package edu.uci.ics.jung.visualization.annotations;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.transform.AffineTransformer;
import edu.uci.ics.jung.visualization.transform.LensTransformer;
import edu.uci.ics.jung.visualization.transform.MutableTransformer;

/**
 * handles the selection of annotations, and the support for the
 * tools to draw them at specific layers.
 * 
 * @author Tom Nelson - tomnelson@dev.java.net
 *
 */
public class AnnotationManager {
	
    protected AnnotationRenderer annotationRenderer = new AnnotationRenderer();
	protected AnnotationPaintable lowerAnnotationPaintable;
	protected AnnotationPaintable upperAnnotationPaintable;
	
	protected RenderContext<?,?> rc;
	protected AffineTransformer transformer;

	public AnnotationManager(RenderContext<?,?> rc) {
		this.rc = rc;
		this.lowerAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer);
		this.upperAnnotationPaintable = new AnnotationPaintable(rc, annotationRenderer);
		
		MutableTransformer mt = rc.getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
		if(mt instanceof AffineTransformer) {
			transformer = (AffineTransformer)mt;
		} else if(mt instanceof LensTransformer) {
			transformer = (AffineTransformer)((LensTransformer)mt).getDelegate();
		}

	}
	
	public AnnotationPaintable getAnnotationPaintable(Annotation.Layer layer) {
		if(layer == Annotation.Layer.LOWER) {
			return this.lowerAnnotationPaintable;
		}
		if(layer == Annotation.Layer.UPPER) {
			return this.upperAnnotationPaintable;
		}
		return null;
	}
	
	public void add(Annotation.Layer layer, Annotation<?> annotation) {
		if(layer == Annotation.Layer.LOWER) {
			this.lowerAnnotationPaintable.add(annotation);
		}
		if(layer == Annotation.Layer.UPPER) {
			this.upperAnnotationPaintable.add(annotation);
		}
	}
	
	public void remove(Annotation<?> annotation) {
		this.lowerAnnotationPaintable.remove(annotation);
		this.upperAnnotationPaintable.remove(annotation);
	}
	
	protected AnnotationPaintable getLowerAnnotationPaintable() {
		return lowerAnnotationPaintable;
	}
	
	protected AnnotationPaintable getUpperAnnotationPaintable() {
		return upperAnnotationPaintable;
	}
	
    public Annotation<?> getAnnotation(Point2D p) {
		@SuppressWarnings("rawtypes")
		Set<Annotation> annotations = new HashSet<Annotation>(lowerAnnotationPaintable.getAnnotations());
		annotations.addAll(upperAnnotationPaintable.getAnnotations());
		return getAnnotation(p, annotations);
	}
	
    @SuppressWarnings("rawtypes")
	public Annotation<?> getAnnotation(Point2D p, Collection<Annotation> annotations) {
		double closestDistance = Double.MAX_VALUE;
		Annotation<?> closestAnnotation = null;
		for(Annotation annotation : annotations) {
			Object ann = annotation.getAnnotation();
			if(ann instanceof Shape) {
				Point2D ip = rc.getMultiLayerTransformer().inverseTransform(p);
				Shape shape = (Shape)ann;
				if(shape.contains(ip)) {
					
					Rectangle2D shapeBounds = shape.getBounds2D();
					Point2D shapeCenter = new Point2D.Double(shapeBounds.getCenterX(), shapeBounds.getCenterY());
					double distanceSq = shapeCenter.distanceSq(ip);
					if(distanceSq < closestDistance) {
						closestDistance = distanceSq;
						closestAnnotation = annotation;
					}
				}
			} else if(ann instanceof String) {
				
				Point2D ip = rc.getMultiLayerTransformer().inverseTransform(Layer.VIEW, p);
				Point2D ap = annotation.getLocation();
				String label = (String)ann;
				Component component = prepareRenderer(rc, annotationRenderer, label);
				
				AffineTransform base = new AffineTransform(transformer.getTransform());
				double rotation = transformer.getRotation();
				// unrotate the annotation
				AffineTransform unrotate =
					AffineTransform.getRotateInstance(-rotation, ap.getX(), ap.getY());
				base.concatenate(unrotate);
				
				Dimension d = component.getPreferredSize();
				Rectangle2D componentBounds = new Rectangle2D.Double(ap.getX(), ap.getY(), d.width, d.height);
				
				Shape componentBoundsShape = base.createTransformedShape(componentBounds);
				Point2D componentCenter = new Point2D.Double(componentBoundsShape.getBounds().getCenterX(),
						componentBoundsShape.getBounds().getCenterY());
				if(componentBoundsShape.contains(ip)) {
					double distanceSq = componentCenter.distanceSq(ip);
					if(distanceSq < closestDistance) {
						closestDistance = distanceSq;
						closestAnnotation = annotation;
					}
				}
				
			}
		}
		return closestAnnotation;
	}
	
	public Component prepareRenderer(RenderContext<?,?> rc, AnnotationRenderer annotationRenderer, Object value) {
		return annotationRenderer.getAnnotationRendererComponent(rc.getScreenDevice(), value);
	}
}
