package net.beadsproject.touch.surface;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import math.geom2d.Box2D;
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 net.beadsproject.touch.perform.PerformTree;
import processing.core.PApplet;

/**
 * A surface which subdivides space into rectangles.
 * It recursively subdivides space like a treemap. When you construct a rectsurface from a PerformTree, there will
 * be a mirroring space subdivision tree.  
 * 
 * @author ben
 */
public class RectangleSurface extends Surface {
	
	static private class RectNode
	{
		public List<RectNode> children = null; 
		public Box2D bounds; // bounds of this node, including all the children
		public PerformNode pn;
		
		static public RectNode buildTree(Box2D r, PerformNode p, List<RectNode> l){return buildTree(r,p,l,true);}
		static public RectNode buildTree(Box2D region, PerformNode pn, List<RectNode> leafNodes, boolean horizontal)
		{
			RectNode tn = new RectNode();
			tn.pn = pn;
			tn.bounds = region;
			
			// recursively add the children
			List<PerformNode> children = pn.getChildren();
			if (children==null || children.size()==0)
			{
				leafNodes.add(tn);
				return tn;
			}
			else
			{
				tn.children = new LinkedList<RectNode>();
				int numChildren = children.size();
				int index = 0;
				double dw = tn.bounds.getWidth()/numChildren;
				double dh = tn.bounds.getHeight()/numChildren;
				
				for (PerformNode child: children)
				{
					Box2D newregion = null;
					if (horizontal)
						newregion = new Box2D(tn.bounds.getMinX()+dw*index, tn.bounds.getMinX()+dw*(index+1), tn.bounds.getMinY(), tn.bounds.getMaxY());
					else
						newregion = new Box2D(tn.bounds.getMinX(), tn.bounds.getMaxX(), tn.bounds.getMinY()+dh*(index), tn.bounds.getMinY()+dh*(index+1));
					
					tn.children.add(buildTree(newregion, child, leafNodes, !horizontal));
					index++;
				}
			}
						
			return tn;
		}
	}
	
	RectNode root;
	List<RectNode> leafNodes;
	
	Map<SurfacePlayer,RectNode> activeRegions;
	
	public RectangleSurface(PerformTree pt)
	{
		activeRegions = new HashMap<SurfacePlayer,RectNode>();
		PerformNode pn = pt.getRoot();
		if (pn!=null)
		{
			leafNodes = new LinkedList<RectNode>();
			root = RectNode.buildTree(new Box2D(0,1,0,1), pn, leafNodes);			
		}	
		else 
			root = null;
	}
	
	public void playerMoved(SurfacePlayer sp, float fromX, float fromY, float toX, float toY)
	{
		// hierarchically locate from and to
		RectNode from = locateNode(root,fromX,fromY), to = locateNode(root,toX,toY);
		
		if (from==null && to!=null)
		{
			event(new EnterEvent(this,to.pn,toX,toY));		
			activeRegions.put(sp,to);
		}
		else if (from!=null && to==null)
		{
			event(new ExitEvent(this,from.pn,toX,toY));
			activeRegions.remove(sp);
		}
		else if (from != null)
		{
			event(new SwitchEvent(this,from.pn,to.pn,toX,toY));
			activeRegions.put(sp,to);
		}		
	}
	
	public void playerPressed(SurfacePlayer sp, float x, float y)
	{
		RectNode any = locateNode(root,x,y);
		if (any!=null)
		{
			event(new EnterEvent(this,any.pn,x,y));		
			activeRegions.put(sp,any);
		}
	}
	
	public void playerReleased(SurfacePlayer sp, float x, float y)
	{
		RectNode any = locateNode(root,x,y);
		if (any!=null)
		{
			event(new ExitEvent(this,any.pn,x,y));		
			activeRegions.remove(sp);
		}
	}
	
	private RectNode locateNode(RectNode parent, float x, float y)
	{
		if (!parent.bounds.contains(x,y))
		{
			return null;
		}
		else
		{
			if (parent.children==null)				
			{
				return parent;
			}
			else
			{
				for (RectNode child: parent.children)
				{
					RectNode c = locateNode(child,x,y);
					if (c!=null) return c; 
				}
			}
			
			return null;
		}
	}

	@Override
	public void draw(PApplet app) {
		app.background(backgroundColour[0],backgroundColour[1],backgroundColour[2]);
		app.stroke(borderColour[0],borderColour[1],borderColour[2]);
		for(RectNode rn: leafNodes)
		{
			if (activeRegions.values().contains(rn))
				app.fill(highlightColour[0],highlightColour[1],highlightColour[2]);
			else 
				app.noFill();
			app.rect((float)rn.bounds.getMinX(),(float)rn.bounds.getMinY(),
					 (float)rn.bounds.getWidth(),(float)rn.bounds.getHeight());			
		}		
	}
}
