package net.beadsproject.touch.surface;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import net.beadsproject.touch.SurfacePlayer;
import net.beadsproject.touch.event.EnterEvent;
import net.beadsproject.touch.event.ExitEvent;
import net.beadsproject.touch.event.SwitchEvent;
import net.beadsproject.touch.perform.PerformNode;
import processing.core.PApplet;


/**
 * Uses circles as regions.
 * 
 * @author ben
 */
public class CircleSurface extends Surface {
	public static class Circle
	{
		public double x, y, r;
		public PerformNode pn;
		public int depth;
		
		public Circle(double x, double y, double r, PerformNode pn, int depth)
		{
			this.x = x;
			this.y = y;
			this.r = r;
			this.pn = pn;
			this.depth = depth;
		}
		
		static public double sqdistance(double x1, double y1, double x2, double y2)
		{
			return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
		}
		
		static public boolean overlaps(Circle a, Circle b)
		{
			if (Math.sqrt(Circle.sqdistance(a.x,a.y,b.x,b.y)) < (a.r+b.r))
				return true;
			else return false;
		}
	}
	
	List<Circle> circles;
	Map<SurfacePlayer,Circle> activeCircles;
	
	public CircleSurface()
	{
		circles = new LinkedList<Circle>();
		activeCircles = new HashMap<SurfacePlayer,Circle>();
	}
	
	public void addCircle(Circle c)
	{
		circles.add(c);
	}
	
	public List<Circle> circles()
	{
		return this.circles;
	}
	
	// events that a surface can receive 
	// the coordinates are in world space
	/**
	 * A player clicked (rapid press and release) on the surface. 
	 */
	public void playerClicked(SurfacePlayer sp, float x, float y){}
	/**
	 * A player pressed into the surface.
	 */	
	public void playerPressed(SurfacePlayer sp, float x, float y){}
	/**
	 * A player released from the surface.
	 */
	public void playerReleased(SurfacePlayer sp, float x, float y){}
	/**
	 * A player who is pressed has moved location.
	 */
	public void playerMoved(SurfacePlayer sp, float fromX, float fromY, float toX, float toY)	
	{
		// find the two circles at from and to, with highest depth
		Circle from = null, to = null;
		int fromDepth = -1, toDepth = -1;
		Circle oldActive = activeCircles.get(sp);		
		
		// for each circle, check for distance to the circle
		for(Circle c: circles)
		{
			boolean insideBefore, insideAfter;
			insideBefore = (((c.x-fromX)*(c.x-fromX) + (c.y-fromY)*(c.y-fromY)) < c.r*c.r);
			insideAfter = (((c.x-toX)*(c.x-toX) + (c.y-toY)*(c.y-toY)) < c.r*c.r);
			
			if (insideBefore && fromDepth<c.depth)
			{
				from = c;
				fromDepth = c.depth;
			}
			
			if (insideAfter && toDepth<c.depth)
			{
				to = c;
				toDepth = c.depth;
			}
		}
		
		if (from!=to && from!=null && to!=null)
		{
			event(new SwitchEvent(this, from.pn, to.pn, toX, toY));
			activeCircles.put(sp, to);
		}
		else if (from==null && to!=null)
		{
			event(new EnterEvent(this,to.pn,toX,toY));
			activeCircles.put(sp, to);
		}
		else if (to==null && from!=null)
		{
			event(new ExitEvent(this,from.pn,toX,toY));
			activeCircles.remove(sp);
		}		
	}	
	
	public void mouseMoved(float fromX, float fromY, float toX, float toY)
	{
		// find the two circles at from and to, with highest depth
		Circle from = null, to = null;
		int fromDepth = -1, toDepth = -1;
		
		// for each circle, check for distance to the circle
		for(Circle c: circles)
		{
			boolean insideBefore, insideAfter;
			insideBefore = (((c.x-fromX)*(c.x-fromX) + (c.y-fromY)*(c.y-fromY)) < c.r*c.r);
			insideAfter = (((c.x-toX)*(c.x-toX) + (c.y-toY)*(c.y-toY)) < c.r*c.r);
			
			if (insideBefore && fromDepth<c.depth)
			{
				from = c;
				fromDepth = c.depth;
			}
			
			if (insideAfter && toDepth<c.depth)
			{
				to = c;
				toDepth = c.depth;
			}
		}
		
		if (from!=to && from!=null && to!=null)
		{
			event(new SwitchEvent(this, from.pn, to.pn, toX, toY));
		}
		else if (from==null && to!=null)
		{
			event(new EnterEvent(this,to.pn,toX,toY));
		}
		else if (to==null && from!=null)
		{
			event(new ExitEvent(this,from.pn,toX,toY));
		}
	}
	
	public void mousePressed(float x, float y){}
	public void mouseReleased(float x, float y){}	

	public void draw(PApplet app)
	{
		app.background(backgroundColour[0],backgroundColour[1],backgroundColour[2]);
		
		for (CircleSurface.Circle c: circles)
		{
			if (activeCircles.values().contains(c))
				app.fill(highlightColour[0],highlightColour[1],highlightColour[2]);
			else 
				app.fill(regionColour[0],regionColour[1],regionColour[2]);			
			app.ellipse((float)c.x,(float)c.y,(float)c.r*2,(float)c.r*2);			
		}
	}
	
}
