package org.osros.image;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import java.awt.geom.Ellipse2D;
import org.apache.log4j.Logger;
import org.osros.service.ImageProcessor;

/*
 * Should be more aptly named Color blob groupin & Color sparse sampling/splitting
 * ObjectList = ColorBlobList
 * 
 * General Goal - try to define an "object" to the best of the data's ability
 * us multiple frames and filters to augment the quality and detail of an objects definition
 * Not only will this allow great functionality in the computers capability to identify "real" objects in nature
 * but it will also facilitate ranging, mapping and other useful functions
 * 
 * A Scene has many dimensions - one of which is objects, area, name, (light position)
 * An Object has man dimensions, position, luminosity, color, reflectivity, texture, perimeter, shadows, size, depth, hidden area
 *
 * 
 */


public class ObjectGroupingThread extends Thread {

	public final static Logger LOG = Logger.getLogger(ObjectGroupingThread.class.getCanonicalName());
	
	//public static HashMap<String, ImageQueue> imageQueues;
	public static ImageQueueHashMap imageQueues;
	public HashMap<String, String> params;
	public boolean isParametersValid = true;
	private boolean forkOutput = true; // TODO parameterize
	public int frameCount = 0;
	public int dumpFileIndex = 20;// TODO parameterize
	
	public int imgWidth = 320;// TODO change Based on Img
	
	public boolean done = false; // TODO - fn

	public ArrayList<Rectangle> tileList; // TODO is this needed?
	public ObjectList objectList; // TODO static? dangerous ?

	
	public Rectangle[][] tileGridTLCorners;
	public Rectangle[][] tileGridTRCorners;
	public Rectangle[][] tileGridBLCorners;
	public Rectangle[][] tileGridBRCorners;
	
	public ArrayList<Rectangle> largeTileList;
	
	public int maxTileSize = 0;
	private String inputName;


	//TODO - Color Blob?
	public class Object 
	{
		// TODO - 3 dimensional related e.g. edges points faces
		public String name;
		public Point anchorPoint;
		public ArrayList <Point> listOfPoints = new ArrayList <Point>(); // map of points?
		//public HashMap <Point> listOfEdges;
		public ArrayList<Rectangle> listOfRect = new ArrayList<Rectangle>();
		public Rectangle[][] gridOfRect = new Rectangle[320][240]; // TODO - img dimensions
		public int avgLuminosity = 0;
		public Color avgColor; // TODO change to Color
		public int area = 0;
		public String type;
		public Rectangle boundingBox;// = new Rectangle();
		
				
		public Object()
		{
			
		}
		public String toString() 
		{
			StringBuffer ret = new StringBuffer(name);
			ret.append(listOfRect.toString());
			ret.append(avgColor);
			return ret.toString();
		}
		public boolean add (Rectangle r)
		{
			if (boundingBox == null)
			{
				boundingBox = new Rectangle(r.x, r.y, r.width, r.height);
			}
			
			if (r.x < boundingBox.x)
			{
				boundingBox.width = boundingBox.width + boundingBox.x - r.x; 
				boundingBox.x = r.x;
			}
			
			if (r.y < boundingBox.y)
			{
				boundingBox.height = boundingBox.height + boundingBox.y - r.y;
				boundingBox.y = r.y;
			}
			
			if (boundingBox.x + boundingBox.width < r.x + r.width)
			{
				//boundingBox.width = r.x + r.width - boundingBox.x;
				boundingBox.width = r.x + r.width - boundingBox.x;
			}
			
			if (boundingBox.y + boundingBox.height < r.y + r.height)
			{
				boundingBox.height = r.y + r.height - boundingBox.y;
			}
			
			area += r.height * r.width;
			gridOfRect[r.x][r.y] = r;
			return listOfRect.add(r);
		}
	}
	
	public class ObjectList 
	{
		public int maxSize = 0;
		public int minSize = 1024; // define outside
		public int maxCount = 0;
		private ArrayList<Object> list = new ArrayList<Object> ();
		
		public ObjectList()
		{
			
		}
		
		public boolean add(Object o)
		{
			return list.add(o);
		}
		
		public int size ()
		{
			return list.size();
		}
		
		public Object get(int i)
		{
			return list.get(i);
		}
	
	}
	
	public ObjectGroupingThread(ImageQueueHashMap imageQueues, HashMap<String,String> params) {
		super("ObjectGroupingThread_ImageProcessorThread");
		
		this.params = params;
		ObjectGroupingThread.imageQueues = imageQueues;
		try {

			// validate params inputs and outputs begin ----
			if (params == null)
			{
				LOG.warn("params are null - parameters will be defaulted");
				params = new HashMap<String,String>();
			}

			if (!params.containsKey("input"))
			{
				LOG.warn("no \"input\" value - defaulting to \"input\"");
				params.put("input", "input");
			}
			
			inputName = params.get("input");
			
			if (!imageQueues.containsKey(inputName))
			{ 	
				throw new ParameterValidation("a named input ImageQueue is required, aborting");
			}
			if (!params.containsKey("output"))
			{
				LOG.warn("no \"output\" value - defaulting to \"output.jpg\"");
				params.put("output", "output.jpg");
			}
			ImageProcessor.createImageQueue(params.get("output"));

			if (!params.containsKey("fork"))
			{
				LOG.warn("no \"fork\" value - defaulting to \"fork.jpg\"");
				params.put("fork", "fork.jpg");
			}
			ImageProcessor.createImageQueue(params.get("fork"));
			
			// validate params inputs and outputs end ----
	
						
		}catch (ParameterValidation pv)
		{
			isParametersValid = false;
			LOG.error("validation failed - " + pv.toString());			
		}
						
	}

	public void run() {
		try {			
			
			if (!isParametersValid)
			{
				LOG.error("parameters not valid");
				return;
			}
			
			System.setProperty("java.awt.headless", "true");
			byte[] inputBytes = new byte[230400];
			
			org.osros.image.Image img = new org.osros.image.Image(320, 240, 3);
			
			sleep(1000);
			//Rectangle targetArea = new Rectangle (0,100,320,48); // single line
			Rectangle targetArea = new Rectangle (0,0,320,240); // single line
			
			/* possibly garbage
			for (Integer oi = 0; oi < 6; ++oi)
			{
				Object o = new Object();
				o.name = oi.toString();
				objectList.add(o);
			}
			*/
			
			while (!done) {
				LOG.info(imageQueues.get(inputName).size());
				org.osros.image.Image in = imageQueues.get(inputName).removeLast();
				if (in != null) {
					objectList = new ObjectList(); // 
					tileList = new ArrayList<Rectangle>(); //TODO - probably should be TileGroup not ObjectList
					tileGridTLCorners = new Rectangle[img.getWidth()][img.getHeight()];
					tileGridTRCorners = new Rectangle[img.getWidth()][img.getHeight()];
					tileGridBLCorners = new Rectangle[img.getWidth()][img.getHeight()];
					tileGridBRCorners = new Rectangle[img.getWidth()][img.getHeight()];
					++frameCount;
					// copy data from input queue					
					System.arraycopy(in.getBytes(), 0, inputBytes, 0, 230400);
					img.setBytes(inputBytes);	
					org.osros.image.Image histogram = new org.osros.image.Image(img.getWidth(), img.getHeight(), img.getColorDepth());

					byte[] src = img.getBytes();
					byte[] dst = histogram.getBytes();
					
					findTiles(src, dst, targetArea, 32, 12, 0);  // TODO - find out what should be global - and local to the thread
					findObjects();
					
					//LOG.info("tile list " + tileList.size());
					// BUFFERED IMAGE BEGIN -------------
					BufferedImage biInput = Filter.toImage(320, 240, histogram.getBytes());
					//Font font = new Font("Arial", Font.PLAIN, 10);
					Graphics2D g = biInput.createGraphics();										
					g.setColor(Color.WHITE);
//					g.drawString(input.getName() + " " + input.getframeNumber(), 5, 10);					
//					g.dispose();
					g.drawRect(targetArea.x, targetArea.y, targetArea.width, targetArea.height);
					
															
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					ImageIO.write(biInput, "jpg", baos);
					org.osros.image.Image newImage = new org.osros.image.Image(320, 240, 3, baos.toByteArray());
					
					baos.close();					

					//if (dumpFileIndex == frameCount)
					if (frameCount < 30)
					{
						FileOutputStream fos = new FileOutputStream("object_" + frameCount + ".jpg");
						fos.write(baos.toByteArray());
					}
															
					imageQueues.get(params.get("output")).addFirst(newImage);
					
					if (forkOutput)
					{
						BufferedImage biFork = Filter.toImage(320, 240, img.getBytes());
						//Font font = new Font("Arial", Font.PLAIN, 10);
						//Graphics2D g = biFork.createGraphics();										
						g = biFork.createGraphics();		 								
						g.setColor(Color.GREEN);
						//g.drawString(input.getName() + " " + input.getframeNumber(), 5, 10);		
						g.drawRect(targetArea.x, targetArea.y, targetArea.width, targetArea.height);
																	    

						// display begin -------------
						
						for (int j = 0; j < tileList.size(); ++j)
						{
							//LOG.info("o " + i + " " + tileList);
							Rectangle r = tileList.get(j);
							g.setColor(r.color);

							g.fillRect(r.x, r.y, r.width, r.height);
							g.setColor(Color.GREEN);									
							g.drawRect(r.x, r.y, r.width, r.height);								
						}
						
						
						for (int k = 0; k < objectList.size(); ++k)
						{

							Object o = objectList.get(k);
							//Random rand = new Random();
							//Color  c = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
							//LOG.info("Object " + o.name + " size " + o.area);
							//if (!(o.avgColor.getGreen() > 220 && o.avgColor.getBlue() > 220))
							{
								for (int l = 0; l < o.listOfRect.size(); ++l)
								{
									Rectangle r = o.listOfRect.get(l);
									g.setColor(r.color);
									g.fillRect(r.x, r.y, r.width, r.height);
									//g.setColor(Color.GREEN);
									//LOG.info(o.name + " " + r);
									
									if (k == 0){g.setColor(Color.ORANGE);}
									if (k == 1){g.setColor(Color.YELLOW);}
									if (k == 2){g.setColor(Color.LIGHT_GRAY);}
									if (k == 3){g.setColor(Color.CYAN);}
									if (k == 4){g.setColor(Color.PINK);}
									//if (k == 2){g.setColor(Color.GREEN);}
									
									//g.setColor(c);
									g.drawRect(r.x, r.y, r.width, r.height);								
									if (l==0)
									{
										//g.setColor(Color.GRAY);
										g.setColor(Color.BLACK);
										g.drawString(o.name + " ", r.x + 15, r.y + 15);		
									}
								}
							}
							//LOG.info("bb" + o.boundingBox);
														
						}
						
						
						for (int k = 0; k < objectList.size(); ++k)
						{

							Object o = objectList.get(k);
							if (!(o.avgColor.getGreen() > 220 && o.avgColor.getBlue() > 220))
							{
	
								int bbx = o.boundingBox.x;
								int bby = o.boundingBox.y;
								int bbwidth = o.boundingBox.width;
								int bbheight = o.boundingBox.height;
								g.setColor(Color.BLACK);
								g.drawRect(bbx, bby, o.boundingBox.width, o.boundingBox.height);
								Font font = new Font("Arial", Font.PLAIN, 8);
								g.setFont(font);
								/*
								g.drawString("(" + bbx + "," + bby +  ") ", bbx + bbwidth/2 + 10, bby + bbheight/2 + 10);
								g.drawString(o.area + "", bbx + bbwidth/2 + 10, bby + bbheight/2 + 20);
								String rgb = Integer.toHexString(o.avgColor.getRGB());
								rgb = rgb.substring(2, rgb.length());
	
								g.drawString(rgb, bbx + bbwidth/2 + 10, bby + bbheight/2 + 30);
								g.drawString(o.listOfRect.size()+"", bbx + bbwidth/2 + 10, bby + bbheight/2 + 40);
								
								if (o.avgColor.getRed() > (o.avgColor.getBlue() + o.avgColor.getGreen())/2 )
								{
									g.drawString("red", bbx + bbwidth/2 + 10, bby + bbheight/2 + 50);
									
								}
								if (o.avgColor.getGreen() > (o.avgColor.getBlue() + o.avgColor.getRed())/2 )
								{
									g.drawString("green", bbx + bbwidth/2 + 10, bby + bbheight/2 + 50);
									
								}
								if (o.avgColor.getBlue()  > (o.avgColor.getGreen() + o.avgColor.getRed())/2 )
								{
									g.drawString("blue", bbx + bbwidth/2 + 10, bby + bbheight/2 + 50);
									
								}
								if (bbwidth * bbheight - o.area < 1000)
								{
									g.drawString("box thingy", bbx + bbwidth/2 + 10, bby + bbheight/2 + 60);
								}
								
								g.setColor(Color.RED);
								Ellipse2D.Double circle = new Ellipse2D.Double(bbx + bbwidth/2, bby + bbheight/2,10,10);
								g.fill(circle);
								*/
							}
						}
						
						
						// display end --------------
						
						
						g.dispose();										
						
						
						baos = new ByteArrayOutputStream();
						ImageIO.write(biFork, "jpg", baos);
						newImage = new org.osros.image.Image(320, 240, 3, baos.toByteArray());
						baos.close();					
						imageQueues.get(params.get("fork")).addFirst(newImage);

						//if (dumpFileIndex == frameCount)
						if (frameCount < 30)
						{
							FileOutputStream fos = new FileOutputStream("fork_" + frameCount + ".jpg");
							fos.write(baos.toByteArray());
						}												
					}

				} else {
					LOG.info("yikes");
					sleep(1000);
				}

			}

		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void findTiles (byte[] src, byte[] dst, Rectangle targetArea, int XStep, int YStep, int depth) // TODO pass Images not byte[] !
	{
		
		int sampleRedPoint = 0;
		int sampleGreenPoint = 0;
		int sampleBluePoint = 0;
		int sampleRedTotal = 0; 
		int sampleGreenTotal = 0; 
		int sampleBlueTotal = 0; 
		int minRed = 255;
		int maxRed = 0;
		int minGreen = 255;
		int maxGreen = 0;
		int minBlue = 255;
		int maxBlue = 0;
		int numSamplePoints = 0;
		boolean fill = true;
		
		if (XStep < 1) {XStep = 1;}
		if (YStep < 1) {YStep = 1;}
		
		/*
		 * Width & Height > 2 X 2  to support avg = |x-1 - x| & |y-1 - y|
		 * Finding averages minRed and maxRed for a given sample area determined by target area divided by divisors.
		 * Recursively called until deviation is small enough or no more area to search.
		 * The Objective of this function is to identify Objects.  Stereo Vision relies on camera
		 * calibration in order to do processing.  These algorithms try to identify and refine an "Object" first
		 * then the correspondence problem becomes less complex and less prone to camera differences, localized noise, or 
		 * other interference.
		 * 
		 * Currently, the threshold is only looking at minRed maxRed values to determine whether to split
		 * and recurse. Previously, I use Average Deviation which was a significantly lower value but
		 * might be important in the future  
		 * 
		 * TODO - bounds checking
		 */
		for (int y = targetArea.y; y < targetArea.y+targetArea.height; y+=YStep)
		{
			//LOG.info(y);
			for (int x = targetArea.x; x < targetArea.x+targetArea.width; x+=XStep)
			{
				sampleBluePoint = src[x * 3 + y * imgWidth * 3];
				sampleGreenPoint = src[x * 3 + y * imgWidth * 3 + 1];
				sampleRedPoint = src[x * 3 + y * imgWidth * 3 + 2];
				
				if (fill) // TODO - this is display (not finding tiles! remove)
				{
					for (int yy = y; yy < y + YStep; ++yy)
					{
						for (int xx = x; xx < x + XStep; ++xx)
						{
							dst[xx * 3 + yy * imgWidth * 3] = (byte)sampleRedPoint; 										
							dst[xx * 3 + yy * imgWidth * 3 + 1] = (byte)sampleGreenPoint; 										
							dst[xx * 3 + yy * imgWidth * 3 + 2] = (byte)sampleBluePoint; 										
						}
						
					}
				} else {
					dst[x * 3 + y * imgWidth * 3] = (byte)sampleRedPoint; 								
				}
				
				if (sampleRedPoint < 0) { sampleRedPoint += 255; }
				if (sampleGreenPoint < 0) { sampleGreenPoint += 255; }
				if (sampleBluePoint < 0) { sampleBluePoint += 255; }
				sampleRedTotal += sampleRedPoint;
				sampleGreenTotal += sampleGreenPoint;
				sampleBlueTotal += sampleBluePoint;
				++numSamplePoints;
				
				// find minRed & maxRed
				if (sampleRedPoint > maxRed){ maxRed = sampleRedPoint;}
				if (sampleRedPoint < minRed){ minRed = sampleRedPoint;}
				if (sampleGreenPoint > maxGreen){ maxGreen = sampleGreenPoint;}
				if (sampleGreenPoint < minGreen){ minGreen = sampleGreenPoint;}
				if (sampleBluePoint > maxBlue){ maxBlue = sampleBluePoint;}
				if (sampleBluePoint < minBlue){ minBlue = sampleBluePoint;}
			}
		}
		
		/*
		 * Here various conditions can exist which determine how far 
		 * and when to recurse and split the current target area into smaller sets
		 * 1. avgDeviation - is the average amount of difference of luminance within an area
		 * 2. targetArea.width - current width of the area being examined
		 * 3. minRed & maxRed are minRed and maxRed about of luminance
		 */

		//if ((maxRed - minRed > 120 || maxGreen - minGreen > 120 || maxBlue - minBlue > 120) && depth < 4)
		if ((maxRed - minRed > 120 || maxGreen - minGreen > 120 || maxBlue - minBlue > 120) && depth < 6)
		{ 
			// SPLIT CURRENT SAMPLE
			//LOG.info("split " + depth + " (" + targetArea.x + "," +  targetArea.y + "[" + targetArea.width + "," + targetArea.height + "]) x/"+XStep+" y/"+YStep+" avg " + sampleRedTotal / numSamplePoints + " minRed " + minRed + " maxRed " + maxRed + " mdev " + (maxRed - minRed));
			/*
			 *  320 = 2^6 * 5  --> depth 6
			 *  240 = 2^4 * 5  --> depth 4
			 */
			int heightDivisor;
			if (depth < 4)
			{
				heightDivisor = 2;
			} else {
				heightDivisor = 1;
			}
			
			Rectangle quad1 = new Rectangle(targetArea);
			quad1.height = quad1.height/heightDivisor;
			quad1.width = quad1.width/2;		
			findTiles (src, dst, quad1, XStep/2, YStep/2, depth + 1);
			
			Rectangle quad2 = new Rectangle(targetArea);
			quad2.x = quad2.x + quad2.width/2;
			quad2.height = quad2.height/heightDivisor;
			quad2.width = quad2.width/2;		
			findTiles (src, dst, quad2, XStep/2, YStep/2, depth + 1);

			if (heightDivisor == 2)
			{
				Rectangle quad3 = new Rectangle(targetArea);
				quad3.x = quad3.x + quad3.width/2;
				quad3.y = quad3.y + quad3.height/2;
				quad3.height = quad3.height/2;
				quad3.width = quad3.width/2;		
				findTiles (src, dst, quad3, XStep/2, YStep/2, depth + 1);
	
				Rectangle quad4 = new Rectangle(targetArea);
				quad4.y = quad4.y + quad4.height/2;
				quad4.height = quad4.height/2;
				quad4.width = quad4.width/2;		
				findTiles (src, dst, quad4, XStep/2, YStep/2, depth + 1);
			}
		
		} else {
			// MAKE A TILE
			if (targetArea.width * targetArea.height > maxTileSize) {maxTileSize = targetArea.width * targetArea.height;}
			//LOG.info("split " + depth + " (" + targetArea.x + "," +  targetArea.y + "[" + targetArea.width + "," + targetArea.height + "]) x/"+XStep+" y/"+YStep+" avg " + sampleRedTotal / numSamplePoints + " minRed " + minRed + " maxRed " + maxRed + " mdev " + (maxRed - minRed));
			targetArea.color = new Color (sampleRedTotal/numSamplePoints, sampleGreenTotal/numSamplePoints, sampleBlueTotal/numSamplePoints);
			tileList.add(targetArea);
			// build up indexes
			//tileGridTLCorners[320][60] = targetArea;
			tileGridTLCorners[targetArea.x][targetArea.y] = targetArea;
			tileGridTRCorners[targetArea.x + targetArea.width - 1][targetArea.y] = targetArea;
			tileGridBRCorners[targetArea.x + targetArea.width - 1][targetArea.y + targetArea.height - 1] = targetArea;
			tileGridBLCorners[targetArea.x][targetArea.y + targetArea.height - 1] = targetArea;
		}
		
	}

	
	@SuppressWarnings("unchecked")
	public void findObjects ()
	{
		Collections.sort(tileList, Collections.reverseOrder()); // is sort the correct dimension to compare? what about color or proximity? - linear joins too?

		/*
		 * TODO - while ...
		 * get object
		 * group and grow object - add all rectangles to master list (objects accounted for) named hasmap? 
		 * get next object
		 */
		boolean finished = false;
		int i = 0;
		while (!finished)
		{
			if (tileList.size() > i)
			{
				Rectangle r = tileList.get(i);
				if (r.height * r.width >= 600)  // power of 2 divisor gives 40 x 30 smallest square
				{
					if (r.assigned != true)
					{
						//LOG.info(r.height * r.width);
						Object o = new Object();
						o.avgColor = r.color;
						o.name = "o" + i;
						group(r,o);
						objectList.add(o);
						//LOG.info(o);
					} else {
						//LOG.info("r already assigned " + r);
					}
				} else {
					finished = true;
				}
				++i;
			} else {
				finished = true;
				
			}
		}
		
		//LOG.info("hello");
	}
	
	//String dumpIndex (Rectangle[][])
	
	// TODO - change color to Color
	// TODO - find histogram @ edge on a pixel level
	public void group(Rectangle r, Object parent)
	{
		if (r == null || parent == null) return;
		// if color threshold is met add to this object
		//LOG.info("group object " + parent.name + " " + r.toString());
		if (parent.gridOfRect[r.x][r.y] == null && // if parent does not already have this rectangle 
				Math.abs(parent.avgColor.getRed() - r.color.getRed()) < 40  
				&& Math.abs(parent.avgColor.getGreen() - r.color.getGreen()) < 40
				&& Math.abs(parent.avgColor.getBlue() - r.color.getBlue()) < 40) // TODO - interesting threshold - should be self adjusting depending on histogram data !!
		{
			r.assigned = true;
			//parent.listOfRect.add(r);
			//parent.gridOfRect[r.x][r.y] = r;
			parent.add(r);
						
			int xOffset = r.x + r.width;
			int yOffset = r.y + r.height;
			//if (xOffset == 320 || yOffset == 240){ return; }
			// try adjacent vertices
			// Top Left ----------------------------------------
			//dump (tileGridTRCorners);
			if (r.x != 0 && tileGridTRCorners[r.x-1][r.y] != null && tileGridTRCorners[r.x-1][r.y].assigned == false)
			{
				group (tileGridTRCorners[r.x-1][r.y],parent);
			}
			if (r.x !=0 && r.y !=0 && tileGridBRCorners[r.x-1][r.y-1] != null && tileGridBRCorners[r.x-1][r.y-1].assigned == false)
			{
				group (tileGridBRCorners[r.x-1][r.y-1],parent);
			}
			if (r.y !=0 && tileGridBLCorners[r.x][r.y-1] != null && tileGridBLCorners[r.x][r.y-1].assigned == false)
			{
				group (tileGridBLCorners[r.x][r.y-1],parent);
			}
			// Top Right ----------------------------------------
			if (xOffset != 320 && tileGridTLCorners[xOffset][r.y] != null && tileGridTLCorners[xOffset][r.y].assigned == false) // r.x + width -1 (index) + 1 (movement) = r.x + width
			{
				group (tileGridTLCorners[xOffset][r.y],parent);
			}
			if (r.y != 0 && tileGridBRCorners[xOffset-1][r.y-1] != null && tileGridBRCorners[xOffset-1][r.y-1].assigned == false)
			{
				group (tileGridBRCorners[xOffset-1][r.y-1],parent);
			}
			if (xOffset != 320 && r.y != 0 && tileGridBLCorners[xOffset][r.y-1] != null && tileGridBLCorners[xOffset][r.y-1].assigned == false)
			{
				group (tileGridBLCorners[xOffset][r.y-1],parent);
			}
			
			// Bottom Right ----------------------------------------
			if (xOffset != 320 && yOffset != 240 && tileGridTLCorners[xOffset][yOffset] != null && tileGridTLCorners[xOffset][yOffset].assigned == false)
			{
				group (tileGridTLCorners[xOffset][yOffset],parent);
			}
			if (yOffset != 240 && tileGridTRCorners[xOffset-1][yOffset] != null && tileGridTRCorners[xOffset-1][yOffset].assigned == false)
			{
				group (tileGridTRCorners[xOffset-1][yOffset],parent);
			}
			if (xOffset != 320 && tileGridBLCorners[xOffset][yOffset-1] != null && tileGridBLCorners[xOffset][yOffset-1].assigned == false)
			{
				group (tileGridBLCorners[xOffset][yOffset-1],parent);
			}
			// Bottom Left ----------------------------------------
			if (yOffset != 240 && tileGridTLCorners[r.x][yOffset] != null && tileGridTLCorners[r.x][yOffset].assigned == false)
			{
				group (tileGridTLCorners[r.x][yOffset],parent);
			}
			if (r.x != 0 && yOffset != 240 && tileGridTRCorners[r.x-1][yOffset] != null && tileGridTRCorners[r.x-1][yOffset].assigned == false)
			{
				group (tileGridTRCorners[r.x-1][yOffset],parent);
			}
			if (r.x != 0 && tileGridBRCorners[r.x-1][yOffset-1] != null && tileGridBRCorners[r.x-1][yOffset-1].assigned == false)
			{
				group (tileGridBRCorners[r.x-1][yOffset-1],parent);
			}
		}
	}
	
	public void dump (Rectangle[][] rect)
	{
		for (int r=0; r<rect.length; r++) {
		     for (int c=0; c<rect[r].length; c++) {
		    	 if (rect[r][c] != null){LOG.info("(" + r + "," + c + rect[r][c]);}
		     }
		}
	
	}
}
	


