import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.awt.image.*;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * CMCMarkerTracking.java
 * 
 * A class to find markers in pictures made by LED white light.
 *  
 * @author Will Levine, Matt Kracum
 * @version 11-9-2009
 * @deprecated
 * */

public class CMCMarkerTracking implements MarkerTracking {
	String inFolder = "/Users/shinkawk/Desktop/pic3";
	String outFile = "out.text";
	
	// Besides 'BLACK' we don't currently use these color variables.
	public static final int[] WHITE = {97, 110, 120};	//The RGB values we chose for the four marker colors
	public static final int[] BLUE = {75, 95, 140};
	public static final int[] ORANGE = {135, 120, 100};
	public static final int[] RED = {150, 105, 75};
	public static final int[] GREEN = {85, 130, 100};
	public static final int[] BLACK = {140, 140, 140};
	public static final int WLWAY = 10;
	public static final int BLWAY = 15;
	public static final int OLWAY = 5;
	public static final int RLWAY = 20;
	public static final int GLWAY = 15;
	
	//Constructor
	public CMCMarkerTracking(){
		
	}

	//Builds array containing the picture files as File objects.  We iterate through this array later to find the coordinates of each of the markers in each picture.
	private static void addFilesRecursively(File file, Collection<File> all) {
	    final File[] children = file.listFiles();
	    //System.out.println("addFiles");
	    if (children != null) {
	        for (File child : children) {
	        	if(child.getName().endsWith(".jpg"))all.add(child);
	            addFilesRecursively(child, all);
	        }
	    }
	}
	
	/*
	 * Creates a double array of double values to store the coordinates.  Each array in the outer array represents a picture.  Each of these arrays contains the XYZ
	 * coords of each of the markers.  These are of the form: [XYZ(head), XYZ(shoulder1), XYZ(shoulder2), etc...].  If a cluster is not found, XYZ of that cluster is
	 * [-1,-1,-1].  Currently, Z will always be -1. RETURNS: 'coordinates', a double array of double values.
	 */
	public double[][] getCoordinates() {
		ArrayList<double[]> twoDDouble = new ArrayList<double[]>();
		final ArrayList<File> FILES = new ArrayList<File>();
		addFilesRecursively(new File(inFolder), FILES);
		System.out.println(FILES);
		Collections.sort((List<File>)FILES, new FileComparator());
		System.out.println(FILES);
		Object[] temp = FILES.toArray();
		for(int i = 0; i < temp.length; i++){
			System.out.println("File name: "+ temp[i]);
			if(temp[i].toString().endsWith(".jpg")){
				twoDDouble.add(getCoordinates(temp[i].toString()));
			}
			
		}

		System.out.println(twoDDouble.size());
		double[][] coordinates = new double[twoDDouble.size()][twoDDouble.get(0).length];
		System.out.println(coordinates.length);
		for(int i =0; i<twoDDouble.size(); i++){
			coordinates[i] = twoDDouble.get(i);
		}

		//Process the coordinates to determine what center represents what body part.
		coordinates = processCoords(coordinates);
		
		return coordinates;
	}
	/*
	 * Finds the XYZ coords of each marker in a picture (called by double[][] getCoordinates for each pic).
	 */
	public double[] getCoordinates(String fileName) {
		BufferedImage bim = null;
		try {
			 bim = ImageIO.read(new File(fileName));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		ArrayList all = new ArrayList();
		double[] clust = new double[39];
		
		
		for(int i = 0; i<13; i++){
			Object[] temp = findCluster(bim, BLACK);
			double[] center = (double[]) temp[0];
			bim = (BufferedImage) temp[1];
			int j = i*3;
			
			clust[j] = center[0];
			clust[j+1] = center[1];
			clust[j+2] = center[2];
			
		}
		return clust;
	}	
	
	/*
	 * Cleans up the coords double array so that all points are in the right order and all missing points are dealt with.
	 */
	private double[][] processCoords(double[][] coordinates){
		
		for(int i = 3; i<coordinates.length; i++){
			double[] temp = new double[coordinates[0].length];
			for(int j = 0; j<coordinates[0].length-1; j = j+3){
				
				double predX = coordinates[i-1][j] + ((coordinates[i-1][j] - coordinates[i-2][j]));
				double predY = coordinates[i-1][j+1] + ((coordinates[i-1][j+1] - coordinates[i-2][j+1]));
				double predZ = coordinates[i-1][j+2] + ((coordinates[i-1][j+2] - coordinates[i-2][j+2]));
				
				double leeway = .022; //.017
				
				double difX = (Math.abs(coordinates[i-1][j] - coordinates[i-2][j]) + 1)*leeway;
				double difY = (Math.abs(coordinates[i-1][j+1] - coordinates[i-2][j+1]) + 1)*leeway;
				double difZ = (Math.abs(coordinates[i-1][j+2] - coordinates[i-2][j+2]) + 1)*leeway;
				System.out.println("dif values:" + Math.abs(coordinates[i-1][j] - coordinates[i-2][j]) + "  "  + Math.abs(coordinates[i-1][j+1] - coordinates[i-2][j+1]) + "  "  + Math.abs(coordinates[i-1][j+2] - coordinates[i-2][j+2]));
				
				
				//System.out.println(predX+" ,"+predY+" ,"+predZ);
				boolean found = false;
				
				
				for(int k=0; k< coordinates[0].length-1; k= k+3){
					if((coordinates[i][k]-difX) <= predX && (coordinates[i][k]+difX) > predX && (coordinates[i][k+1]-difY) <= predY && (coordinates[i][k+1]+difY) > predY && (coordinates[i][k+2]-difZ) <= predZ && (coordinates[i][k+2]+difZ) > predZ){
						temp[j] = coordinates[i][k];
						temp[j+1] = coordinates[i][k+1];
						temp[j+2] = coordinates[i][k+2];
						/*for(int m = k-1; m == 0; m = m - 3){
							if()
						}*/
						found = true;
						//System.out.println("Coords[i][k]: " + coordinates[i][k] +" coordinates[i][k+1]: "+ coordinates[i][k+1] + " coordinates[i][k+2]: " + coordinates[i][k+2]);
						break;
					}
				}
				if(!found){
					temp[j] = predX;
					temp[j+1] = predY;
					temp[j+2] = predZ;
					//System.out.println("set predicted");
				}
			}
			for(int j = 0; j<coordinates[0].length; j= j+3){
				for(int k=0; k< coordinates[0].length; k= k+3){
					if(j!=k){
						if(temp[j]==temp[k] && temp[j+1]==temp[k+1] && temp[j+2]==temp[k+2]){
							double[] p1 = new double[3];
							double[] p2 = new double[3];
							p1[0] = coordinates[i-1][j] + ((coordinates[i-1][j] - coordinates[i-2][j]));
							p1[1] = coordinates[i-1][j+1] + ((coordinates[i-1][j+1] - coordinates[i-2][j+1]));
							p1[2] = coordinates[i-1][j+2] + ((coordinates[i-1][j+2] - coordinates[i-2][j+2]));
							p2[0] = coordinates[i-1][k] + ((coordinates[i-1][k] - coordinates[i-2][k]));
							p2[1] = coordinates[i-1][k+1] + ((coordinates[i-1][k+1] - coordinates[i-2][k+1]));
							p2[2] = coordinates[i-1][k+2] + ((coordinates[i-1][k+2] - coordinates[i-2][k+2]));
							double dist1 = (temp[j]-p1[0])*(temp[j]-p1[0]) + (temp[j+1]-p1[1])*(temp[j+1]-p1[1]) + (temp[j+2]-p1[2])*(temp[j+2]-p1[2]);
							double dist2 = (temp[j]-p2[0])*(temp[j]-p2[0]) + (temp[j+1]-p2[1])*(temp[j+1]-p2[1]) + (temp[j+2]-p2[2])*(temp[j+2]-p2[2]);
							if(dist1 <= dist2){
								temp[k] = p2[0];
								temp[k+1] = p2[1];
								temp[k+2] = p2[2];
							}
							else{
								temp[j] = p1[0];
								temp[j+1] = p1[1];
								temp[j+2] = p1[2];
							}
						}
					}
				}
			}
			coordinates[i] = temp;
			
		}
		
	
		return coordinates;
	}
	

	/*
	 * Finds a cluster of anything not black and returns the coords of the center of the cluster, found using the averages of xmin, xmax, ymin, ymax, 
	 * and the average RGB values of the cluster.  Returns them in an array of doubles
	 */
	private Object[] findCluster(BufferedImage bim, int[] target){
		int height = bim.getHeight();
		int width = bim.getWidth();
		int clusterSize = 0;
		int xmin = -1;
		int ymin = -1;
		int xmax = -1;
		int ymax = -1;
		ArrayList rGBs = new ArrayList();
		
		Boolean done = false;
		for(int y =0; y < height; y++){
			if (done){
				break;
			}
            for(int x = 0; x< width; x++){
            	int bits = bim.getRGB(x, y);
                Color cl = new Color(bits, false);
                if (MatchColor(cl, target, 0)){
                	//System.out.println(x+" "+y);
                	ArrayList<Object> result = findClusterHelper(bim, x, y, 1, target, rGBs);
                	bim = (BufferedImage)result.get(0);
                	//if (((Number)result.get(1)).intValue() > clusterSize){
                		clusterSize = ((Number)result.get(1)).intValue();
                		xmin = ((Number)result.get(2)).intValue();
                		xmax = ((Number)result.get(3)).intValue();
                		ymin = ((Number)result.get(4)).intValue();
                		ymax = ((Number)result.get(5)).intValue();
                		rGBs = (ArrayList) result.get(6);
                	done = true;
                	break;
                }	
               }
            }
		
		
		double[] cl = findAvgRGB(rGBs);
		//System.out.println("cl: " + cl[0] + " " + cl[1] + " " + cl[2]);
		
		//System.out.println(xmax+" "+xmin);
		double[] center = new double[6];
		if(clusterSize == 0){
		 center[0] = -1.0;
		 center[1] = -1.0;
		 center[2] = -1.0;
		 center[3] = 0;
		 center[4] = 0;
		 center[5] = 0;
		}
		else{
		 center[0] = (((double)(xmax+xmin))/(2*((double)width)));
		 center[1] = (((double)(ymax+ymin))/(2*((double)height)));
		 center[2] = -1;
		 center[3] = cl[0];
		 center[4] = cl[1];
		 center[5] = cl[2];
		 }
		
		Object[] combo = new Object[2];
		combo[0] = center;
		combo[1] = bim;
		
        return combo;
	}

	/*
	 * Finds the average RGB values of a fiven cluster and returns an array of the 3 values.
	 */
	private double[] findAvgRGB(ArrayList rGBs){
		double[] cl = new double[3];
		int r=0;
		int g=0;
		int b=0;
		int size = rGBs.size();
		
		for(int i = 0; i<rGBs.size(); i++){
			/*//orange
			if((((Color) rGBs.get(i)).getRed() + 0 >= ORANGE[0]) && (((Color) rGBs.get(i)).getRed() - 0 <= ORANGE[0]) && (((Color) rGBs.get(i)).getGreen() + 0 >= ORANGE[1]) && (((Color) rGBs.get(i)).getGreen() - 5 <= ORANGE[1])
					&& (((Color) rGBs.get(i)).getBlue() + 0 >= ORANGE[2]) && (((Color) rGBs.get(i)).getBlue() - 0 <= ORANGE[2])){
				cl[0]=ORANGE[0];
				cl[1]=ORANGE[1];
				cl[2]=ORANGE[2];
			}
			//white
			else if((((Color) rGBs.get(i)).getRed() + 5 >= WHITE[0]) && (((Color) rGBs.get(i)).getRed() - 5 <= WHITE[0]) && (((Color) rGBs.get(i)).getGreen() + 5 >= WHITE[1]) && (((Color) rGBs.get(i)).getGreen() - 5 <= ORANGE[1])
					&& (((Color) rGBs.get(i)).getBlue() + 5 >= WHITE[2]) && (((Color) rGBs.get(i)).getBlue() - 5 <= WHITE[2])){
				cl[0]=WHITE[0];
				cl[1]=WHITE[1];
				cl[2]=WHITE[2];
			}
			//green
			else if((((Color) rGBs.get(i)).getRed() + 5 >= GREEN[0]) && (((Color) rGBs.get(i)).getRed() - 5 <= GREEN[0]) && (((Color) rGBs.get(i)).getGreen() + 5 >= GREEN[1]) && (((Color) rGBs.get(i)).getGreen() - 5 <= ORANGE[1])
					&& (((Color) rGBs.get(i)).getBlue() + 5 >= GREEN[2]) && (((Color) rGBs.get(i)).getBlue() - 5 <= GREEN[2])){
				cl[0]=GREEN[0];
				cl[1]=GREEN[1];
				cl[2]=GREEN[2];
			}
			//blue
			else if((((Color) rGBs.get(i)).getRed() + 5 >= BLUE[0]) && (((Color) rGBs.get(i)).getRed() - 5 <= BLUE[0]) && (((Color) rGBs.get(i)).getGreen() + 5 >= BLUE[1]) && (((Color) rGBs.get(i)).getGreen() - 5 <= ORANGE[1])
					&& (((Color) rGBs.get(i)).getBlue() + 5 >= BLUE[2]) && (((Color) rGBs.get(i)).getBlue() - 5 <= BLUE[2])){
				cl[0]=BLUE[0];
				cl[1]=BLUE[1];
				cl[2]=BLUE[2];
			}
			//red
			else if((((Color) rGBs.get(i)).getRed() + 5 >= RED[0]) && (((Color) rGBs.get(i)).getRed() - 5 <= RED[0]) && (((Color) rGBs.get(i)).getGreen() + 5 >= RED[1]) && (((Color) rGBs.get(i)).getGreen() - 5 <= ORANGE[1])
					&& (((Color) rGBs.get(i)).getBlue() + 5 >= RED[2]) && (((Color) rGBs.get(i)).getBlue() - 5 <= RED[2])){
				cl[0]=RED[0];
				cl[1]=RED[1];
				cl[2]=RED[2];
			}*/
			
			
			//if(((Color) rGBs.get(i)).getRed()<200 && ((Color) rGBs.get(i)).getGreen() < 200 && ((Color) rGBs.get(i)).getBlue() < 200){
				r = r + ((Color) rGBs.get(i)).getRed();
				g = g + ((Color) rGBs.get(i)).getGreen();
				b = b + ((Color) rGBs.get(i)).getBlue();
				
			//}
			//else{
			//	size = size - 1;
			//}
		}
		
		//System.out.println(r + "" + g + ""+  b);
		if (size != 0){
			r = r/size;
			g = g/size;
			b = b/size;
		}
		
		cl[0]=r;
		cl[1]=g;
		cl[2]=b;
	
		
		//System.out.println("r: " + r + " g: " + g + " b: " + b);
		return cl;
	}
	
	/*
	 * Recursively 'helps' to 'findClusters'.
	 */
	private ArrayList<Object> findClusterHelper(BufferedImage bim, int x , int y, int counter, int[] target, ArrayList rGBs){
		rGBs.add(new Color(bim.getRGB(x,y)));
    	bim.setRGB(x, y, Color.black.hashCode());
    	
    	int xmin = x;
    	int xmax = x; 	
    	int ymin = y;
    	int ymax = y;
    	
       	//ArrayList = {bim, counter, xmin, xmax, ymin, ymax}
    	ArrayList<Object> result = null;
    	
		if( ((x-1) > 0) && MatchColor(new Color(bim.getRGB(x-1,y)), target, 0)){
			result = findClusterHelper(bim, x-1, y, 1, target, rGBs);
			
			bim = (BufferedImage) result.get(0);
			counter += ((Number)result.get(1)).intValue();
			int newxmin= ((Number)result.get(2)).intValue();
			int newxmax= ((Number)result.get(3)).intValue();
			int newymin= ((Number)result.get(4)).intValue();
			int newymax= ((Number)result.get(5)).intValue();

			if (xmin > newxmin) xmin = newxmin;
			if (xmax < newxmax) xmax = newxmax;
			if (ymax < newymax) ymax = newymax;
			if (ymin > newymin) ymin = newymin;
			
		}
		
		if((bim.getWidth() >= (x +1)) && MatchColor(new Color(bim.getRGB(x+1,y)), target, 0)){
			result = findClusterHelper(bim, x+1, y, 1, target, rGBs);
			
			bim = (BufferedImage) result.get(0);
			counter += ((Number)result.get(1)).intValue();
			int newxmin= ((Number)result.get(2)).intValue();
			int newxmax= ((Number)result.get(3)).intValue();
			int newymin= ((Number)result.get(4)).intValue();
			int newymax= ((Number)result.get(5)).intValue();

			if (xmin > newxmin) xmin = newxmin;
			if (xmax < newxmax) xmax = newxmax;
			if (ymax < newymax) ymax = newymax;
			if (ymin > newymin) ymin = newymin;
			
		}
		if(((y-1) > 0) && MatchColor(new Color(bim.getRGB(x,y-1)), target, 0)){
			result = findClusterHelper(bim, x, y-1, 1, target, rGBs);
			
			bim = (BufferedImage) result.get(0);
			counter += ((Number)result.get(1)).intValue();
			int newxmin= ((Number)result.get(2)).intValue();
			int newxmax= ((Number)result.get(3)).intValue();
			int newymin= ((Number)result.get(4)).intValue();
			int newymax= ((Number)result.get(5)).intValue();

			if (xmin > newxmin) xmin = newxmin;
			if (xmax < newxmax) xmax = newxmax;
			if (ymax < newymax) ymax = newymax;
			if (ymin > newymin) ymin = newymin;
			
		}
		
		
		if((bim.getHeight() >= (y +1)) && MatchColor(new Color(bim.getRGB(x,y+1)), target, 0)){
			result = findClusterHelper(bim, x, y+1, 1, target, rGBs);
			bim = (BufferedImage) result.get(0);
			//System.out.println(bim.getHeight());
			//System.out.println(y);
		
			counter += ((Number)result.get(1)).intValue();
			int newxmin= ((Number)result.get(2)).intValue();
			int newxmax= ((Number)result.get(3)).intValue();
			int newymin= ((Number)result.get(4)).intValue();
			int newymax= ((Number)result.get(5)).intValue();

			if (xmin > newxmin) xmin = newxmin;
			if (xmax < newxmax) xmax = newxmax;
			if (ymax < newymax) ymax = newymax;
			if (ymin > newymin) ymin = newymin;
			
		}
		
		ArrayList<Object> finalResult= new ArrayList<Object>();
		finalResult.add(bim);
		finalResult.add(counter);
		finalResult.add(xmin);
		finalResult.add(xmax);
		finalResult.add(ymin);
		finalResult.add(ymax);
		finalResult.add(rGBs);

		return finalResult;
	}
	
	/*
	 * Displays specified image from BufferedImage
	 */
	private void display(BufferedImage bim, String fileName){
		ImageIcon image= new ImageIcon((Image)bim); //cast cast cast...
		JLabel pic = new JLabel(image);
		
		JFrame f = new JFrame(fileName);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(image.getIconWidth(), image.getIconHeight());
        f.setLocation(200,200);
        f.add(pic);
        f.setVisible(true);
        
        f.dispose();
	}

	private boolean MatchColor(Color cl, int red, int green, int blue, int lway){
		int r = cl.getRed();
		int g = cl.getGreen();
		int b = cl.getBlue();
		//System.out.println("" +r+ " " +g+ " "+b);
		
		if ((r > red) && (g > green) && (b > blue)){
			return true;
		}
	
		return false;
	}
	
	/*
	 * Makes it easier to determine if a pixel matches a given RGB value.
	 */
	private boolean MatchColor(Color cl, int[] colorArray, int lway){
		return MatchColor(cl, colorArray[0], colorArray[1],colorArray[2], lway);
	}


	/*
	 * Gets the folder name where our pictures are.
	 */
	public String getInFolder() {
		return inFolder;
	}

	/*
	 * Gets the file name where we are outputting the coords of each marker in each picture.  This is more an alternative to using the double array of coords.
	 */
	public String getOutFile() {
		return outFile;
	}
	
	/*
	 * Sets the name of the folder we are going to be using to find our pictures.
	 */
	public void setInFolder(String foldername) {
		inFolder = foldername;

	}

	/*
	 * Sets the name of the file we are going to be using to store the coords of the markers in each of the images.
	 */
	public void setOutFile(String filename) {
		outFile = filename;
	}

	/*
	 * Writes the coords of each of the markers in each of the pictures and the colors theyre associated with to a NEW file.
	 */
	public void writeToFile() {
		double[][] coords = getCoordinates();
		Writer writer = null;
		try {
			writer = new BufferedWriter(new FileWriter(outFile));
			writer.write(inFolder);
			for(int i =0; i < coords.length ; i++){
				for(int k =0; k < coords[i].length; k++){
					writer.write(Double.toString(coords[i][k]));
					if(k != coords[i].length-1) writer.write(",");
				}
				writer.write("\r\n");
			}
		writer.close();
		writer = null;
		} catch (IOException e) {           
			System.out.println("FAILED");
			e.printStackTrace();
		}
	}
	
	/*
	 * Appends the coords of each of the markers in each of the pictures and the colors theyre associated with to an OLD file. Creates a new file if there is no old file.
	 */
	public void writeToFileAppend() throws IOException {
		double[][] coords = getCoordinates();
		Writer writer = null;
		try {
			writer = new BufferedWriter(new FileWriter(outFile, true));
			writer.write(inFolder+"\r\n");
			for(int i =0; i < coords.length ; i++){
				for(int k =0; k < coords[i].length; k++){
					writer.write(Double.toString(coords[i][k]));
					if(k != coords[i].length-1) writer.write(",");
				}
				writer.write("\r\n");
			}
		writer.close();
		writer = null;
		} catch (IOException e) {
			System.out.println("FAILED");
			e.printStackTrace();
		}
	}

	/*
	 * Main method.  Runs the show.
	 */
	public static void main(String[] args) throws IOException{
		/*final Collection<File> FILES = new ArrayList<File>();
		addFilesRecursively(new File("/Users/shinkawk/Desktop/KOHEI"), FILES);
		System.out.println(FILES);
		Object[] temp = FILES.toArray();
		Arrays.sort(temp);
		for(int i = 0; i < temp.length; i++)
			System.out.println(temp[i]);
		//Collections<File>.sort(FILES);*/
		

		CMCMarkerTracking cmcMT = new CMCMarkerTracking();
		cmcMT.writeToFile();
		
	}

	public void setAviFile() {
		// TODO Auto-generated method stub
		
	}
}
