import Targa.*;
import util.*;

import java.io.*;
import java.util.Vector;

/**
* This class calculates all colour statistics for a given box size. Although we
* established that pixel level was best, and hence used Colour1.java for all of
* our processing requirements, this class was used to correlate to the human data.
* It will work at the pixel level (box size 1), but is very, very slow...
* Comments are similar to Colour1.java. The only strange bit is the box situation...
*/
public class Colour {
	TargaPicture tp;
	PrintWriter log;
	final boolean LESION = true;
	final boolean SKIN = false;
	int boxSize;


	///----------------------------------------------------
	public Colour(TargaPicture t, PrintWriter logFile, int boxSize) {
		this.tp = t;
		this.log = logFile;
		this.boxSize = boxSize;
	}



  ///----------------------------------------------------
	public void getColourAttributes() {
		int skinCount = 0; int tumorCount = 0;
		Vector ptStoreAverage = new Vector();
		Vector ptSkinStoreAverage = new Vector();

		double rTumorVar=0, gTumorVar=0, bTumorVar=0, lumTumorVar =0 ;
		double rTumorAv=0, gTumorAv=0, bTumorAv=0, lumTumorAv =0;
		double rSkinAv=0, gSkinAv=0, bSkinAv =0;

		double lTumorAv=0, alphaTumorAv=0, betaTumorAv =0; // Spherical colour coords
		double lTumorVar=0, alphaTumorVar=0, betaTumorVar =0; // Spherical colour coords
		double lSkinAv=0, alphaSkinAv=0, betaSkinAv =0; // Spherical colour coords

		double LxTumorAv=0,axTumorAv=0,bxTumorAv =0;          // L* a* b*
		double LxTumorVar=0,axTumorVar=0,bxTumorVar =0;       // L* a* b*
		double LxSkinAv=0,axSkinAv=0,bxSkinAv =0;             // L* a* b*

		double hueTumorAv = 0, hueSkinAv = 0;
		double cxTumorAv = 0, cxSkinAv = 0;
		double hueTumorVar = 0, hueSkinVar = 0;
		double cxTumorVar = 0, cxSkinVar = 0;

		double relChromRed, relChromGreen, relChromBlue ;

		float r,g,b;

		RGBPoint pt = null;

		/**
		* This bit stores all of the pixels in a single box.
		for (int y = 0; y < tp.h-boxSize;y+=boxSize) {
			for (int x = 0; x < tp.w-boxSize; x+=boxSize) {
				if (anyLesionInBox(x,y,boxSize) == true) {  // some lesion
					ptStoreAverage.addElement(getMedianColour(x,y,boxSize, LESION));
				}
				if (anySkinInBox(x,y,boxSize) == true) { // some skin
					ptSkinStoreAverage.addElement(getMedianColour(x,y,boxSize, SKIN));
				}
			}
		}
//    new TargaView(tp);
		//------------------------------------------------
		// And since we have all the median colour points stored in either ptStoreAverage or ptSkinStoreAverage
		// we can calculate the variance values...
		//------------------------------------------------
		for (int i = 0; i < ptStoreAverage.size(); i++) {
			pt = (RGBPoint)ptStoreAverage.elementAt(i);
			rTumorAv += pt.r;
			gTumorAv += pt.g;
			bTumorAv += pt.b;
			lTumorAv += pt.L;
			alphaTumorAv += pt.alpha;
			betaTumorAv += pt.beta;
			LxTumorAv += pt.Lx;
			axTumorAv += pt.ax;
			bxTumorAv += pt.bx;
			cxTumorAv += pt.Cx;
			hueTumorAv += pt.hue;
		}
		rTumorAv /= ptStoreAverage.size();
		gTumorAv /= ptStoreAverage.size();
		bTumorAv /= ptStoreAverage.size();
		lTumorAv /= ptStoreAverage.size();
		alphaTumorAv /= ptStoreAverage.size();
		betaTumorAv /= ptStoreAverage.size();
		LxTumorAv /= ptStoreAverage.size();
		axTumorAv /= ptStoreAverage.size();
		bxTumorAv /= ptStoreAverage.size();
		cxTumorAv /= ptStoreAverage.size();
		hueTumorAv /= ptStoreAverage.size();

		for (int i = 0; i < ptSkinStoreAverage.size(); i++) {
			pt = (RGBPoint)ptSkinStoreAverage.elementAt(i);
			rSkinAv += pt.r;
			gSkinAv += pt.g;
			bSkinAv += pt.b;
			lSkinAv += pt.L;
			alphaSkinAv += pt.alpha;
			betaSkinAv += pt.beta;
			LxSkinAv += pt.Lx;
			axSkinAv += pt.ax;
			bxSkinAv += pt.bx;
			cxSkinAv += pt.Cx;
			hueSkinAv += pt.hue;
		}
		rSkinAv /= ptSkinStoreAverage.size();
		gSkinAv /= ptSkinStoreAverage.size();
		bSkinAv /= ptSkinStoreAverage.size();
		lSkinAv /= ptSkinStoreAverage.size();
		alphaSkinAv /= ptSkinStoreAverage.size();
		betaSkinAv /= ptSkinStoreAverage.size();
		LxSkinAv /= ptSkinStoreAverage.size();
		axSkinAv /= ptSkinStoreAverage.size();
		bxSkinAv /= ptSkinStoreAverage.size();
		cxSkinAv /= ptSkinStoreAverage.size();
		hueSkinAv /= ptSkinStoreAverage.size();

    		///----------------- Variance calculation
		for (int i = 0; i < ptStoreAverage.size(); i++) {
			pt = (RGBPoint)ptStoreAverage.elementAt(i);

			rTumorVar += (pt.r-rTumorAv)*(pt.r-rTumorAv);
			gTumorVar += (pt.g-gTumorAv)*(pt.g-gTumorAv);
			bTumorVar += (pt.b-bTumorAv)*(pt.b-bTumorAv);
			lTumorVar += (pt.L-lTumorAv)*(pt.L-lTumorAv);
			alphaTumorVar += (pt.alpha-alphaTumorAv)*(pt.alpha-alphaTumorAv);
			betaTumorVar += (pt.beta-betaTumorAv)*(pt.beta-betaTumorAv);
			LxTumorVar += (pt.Lx-LxTumorAv)*(pt.Lx-LxTumorAv);
			axTumorVar += (pt.ax-axTumorAv)*(pt.ax-axTumorAv);
			bxTumorVar += (pt.bx-bxTumorAv)*(pt.bx-bxTumorAv);
			cxTumorVar += (pt.Cx-cxTumorAv)*(pt.Cx-cxTumorAv);
			hueTumorVar += (pt.hue-hueTumorAv)*(pt.hue-hueTumorAv);
		}
		rTumorVar /= ptStoreAverage.size();
		gTumorVar /= ptStoreAverage.size();
		bTumorVar /= ptStoreAverage.size();
		lTumorVar /= ptStoreAverage.size();
		alphaTumorVar /= ptStoreAverage.size();
		betaTumorVar /= ptStoreAverage.size();
		LxTumorVar /= ptStoreAverage.size();
		axTumorVar /= ptStoreAverage.size();
		bxTumorVar /= ptStoreAverage.size();
		cxTumorVar /= ptStoreAverage.size();
		hueTumorVar /= ptStoreAverage.size();

		relChromRed = rTumorAv/(rTumorAv+gTumorAv+bTumorAv) - rSkinAv/(rSkinAv+gSkinAv+bSkinAv) ;
		relChromGreen = gTumorAv/(rTumorAv+gTumorAv+bTumorAv) - gSkinAv/(rSkinAv+gSkinAv+bSkinAv) ;
		relChromBlue = bTumorAv/(rTumorAv+gTumorAv+bTumorAv) - bSkinAv/(rSkinAv+gSkinAv+bSkinAv) ;

		log.print(rTumorAv + "\t" + gTumorAv + "\t" + bTumorAv + "\t" );
		log.print(lTumorAv + "\t" + alphaTumorAv + "\t" + betaTumorAv + "\t");
		log.print(LxTumorAv + "\t" + axTumorAv + "\t" + bxTumorAv + "\t");
		log.print(cxTumorAv + "\t" + hueTumorAv+ "\t");


		log.print((rSkinAv-rTumorAv) + "\t" + (gSkinAv-gTumorAv) + "\t" + (bSkinAv-bTumorAv) + "\t");
		log.print((lSkinAv-lTumorAv) + "\t" + (alphaSkinAv-alphaTumorAv) + "\t" + (betaSkinAv-betaTumorAv) + "\t");
		log.print((LxSkinAv-LxTumorAv) + "\t" + (axSkinAv-axTumorAv) + "\t" + (bxSkinAv-bxTumorAv) + "\t");
		log.print((cxSkinAv-cxTumorAv) + "\t" + (hueSkinAv-hueTumorAv) + "\t" );

		log.print(rTumorVar + "\t" + gTumorVar + "\t" + bTumorVar + "\t" );
		log.print(lTumorVar + "\t" + alphaTumorVar + "\t" + betaTumorVar + "\t");
		log.print(LxTumorVar + "\t" + axTumorVar + "\t" + bxTumorVar + "\t");
		log.print(cxTumorVar + "\t" + hueTumorVar + "\t");

		log.println(relChromRed + "\t" + relChromGreen + "\t" + relChromBlue + "\t");
		log.flush();
	}



	//----------------------------------------------------
	//anyLesionInBox returns true if any of the pixels in the specified box are lesion pixels
	//----------------------------------------------------
	public boolean anyLesionInBox(int x,int y,int boxSize) {
		boolean found = false;
outerLoop:		for (int i = 0; i < boxSize; i++) {
				for (int j = 0; j < boxSize; j++) {
					if (tp.getMaskData(x+j,y+i) == LESION) {
						found = true;
						break outerLoop;
					}
				}
			}
		return found;
	}
	//----------------------------------------------------
	//anySkinInBox returns true if any of the pixels in the specified box are skin pixels
	//----------------------------------------------------
	public boolean anySkinInBox(int x,int y,int boxSize) {
		boolean found = false;
outerLoop:    	for (int i = 0; i < boxSize; i++) {
      			for (int j = 0; j < boxSize; j++) {
				if (tp.getMaskData(x+j,y+i) == SKIN) {
					found = true;
					break outerLoop;
				}
			}
		}
		return found;
	}



	//----------------------------------------------------
	//getMedianColour. Given a box, what is the median skin or lesion colour in it?
	//----------------------------------------------------
	RGBPoint getMedianColour(int x, int y, int wid, boolean lesionOrSkin) {
		RGBPoint pt = null;
		RGBPoint[] colour = new RGBPoint[wid*wid];
		float[]rgbDistance = new float[wid*wid];
		float[]lAlphaBetaDistance = new float[wid*wid];
		float[] lxAxBxDistance = new float[wid*wid];

		int count = 0;
		double[] temp = null;
		for (int i =0; i< wid; i++) {
			for (int j =0; j< wid; j++) {
				if (tp.getMaskData(x+j,y+i) == lesionOrSkin) {
					try {
						pt = new RGBPoint(tp.getImageData(x+j,y+i));
					} catch (ArrayIndexOutOfBoundsException e) {
						continue ;
					}
					if (pt != null && pt.r > 0) {
						pt.calcLAB();
						temp = pt.getSphericalCoords() ;
						rgbDistance[count] = (float)Distance.getEuclidDistance(0,0,0,pt.r,pt.g,pt.b);
						lxAxBxDistance[count] = (float)Distance.getEuclidDistance(0,0,0,pt.Lx,pt.ax,pt.bx);
						lAlphaBetaDistance[count] = (float)Distance.getEuclidDistance(0,0,0,temp[0],temp[1],temp[2]);
						colour[count] = pt;
						count ++;
					}
				}
      			}
		}

		RGBPoint rgbMedianColour = (RGBPoint)Median.getMedianValue(colour,rgbDistance, count);
		RGBPoint lxAxBxMedianColour = (RGBPoint)Median.getMedianValue(colour,lxAxBxDistance, count);
		RGBPoint lAlphaBetaMedianColour = (RGBPoint)Median.getMedianValue(colour, lAlphaBetaDistance, count);
		lxAxBxMedianColour.calcLAB();
		lAlphaBetaMedianColour.calcSpherical();
		rgbMedianColour.Lx = lxAxBxMedianColour.Lx;
		rgbMedianColour.ax = lxAxBxMedianColour.ax;
		rgbMedianColour.bx = lxAxBxMedianColour.bx;
		rgbMedianColour.Cx = lxAxBxMedianColour.Cx;
		rgbMedianColour.hue = lxAxBxMedianColour.hue;
		rgbMedianColour.L = lAlphaBetaMedianColour.L;
		rgbMedianColour.alpha = lAlphaBetaMedianColour.alpha;
		rgbMedianColour.beta = lAlphaBetaMedianColour.beta;

		//----------------------------------------------------
		//This bit displays each box in its median colour. Useful for displays....
		//----------------------------------------------------
//		for (int i =0; i< wid; i++) {
//			for (int j =0; j< wid; j++) {
//				if (lesionOrSkin == LESION) {
//					tp.setImageData(x+j,y+i,rgbMedianColour.toInt());
//				}
//			}
//		}
		return rgbMedianColour;
	}



	//-----------------------------------------
	//Main is similar to the previous files, although three or four arguments are
	//used. Four if iteration is required, and three if only a single file is to
	//be processed. The first two arguments in single iteration mode are the image file and the mask file.
	//The first three arguments for iterative mode are `iterate', script file, output file.
	//The final argument for both methods is an integer which describes the box size. 
	//-----------------------------------------
	public static void main(String []args) {
		int boxSize = 1;
		if (args[0].equals("iterate") == true) {
			Vector files = new Vector() ;
			FileReader fr = null;
			StreamTokenizer st = null;
			int numberOfFiles = 0;
			String file = null;
			String dir1=null, dir2=null;

			boxSize = new Integer(args[3]).intValue() ;
System.out.println("Colour (clinical view) analysis. BoxSize:"+boxSize);
			try {
				fr = new FileReader(args[1]) ;
				st = new StreamTokenizer(fr) ;
				st.slashStarComments(true);
				st.nextToken() ;
				numberOfFiles = (int)st.nval;

				st.nextToken() ;
				dir1 = (String)st.sval;
				st.nextToken() ;
				dir2 = (String)st.sval;

				for (int i = 0; i < numberOfFiles; i++) {
					st.nextToken() ;
					file = (new Integer((int)st.nval)).toString();
					st.nextToken() ;
					if (st.sval != null && st.sval.length() == 1) {
						file += st.sval;
					}
					else {
						st.pushBack();
					}
					files.addElement(file) ;
//System.out.println("File " + file);
				}
			} catch (IOException e) {
System.out.println("Read Error in Specification File: ACTION: EXIT") ;
System.exit(1);
			}

			// Create log stream
			FileWriter fw = null;
			PrintWriter logFile = null;
			try {
				fw = new FileWriter(args[2]) ;
				logFile = new PrintWriter(fw, true);
			} catch (IOException e) {
System.out.println("Error opening Log File: ACTION: Use System.out") ;
	        	// true turns automatic flushing on, so every println causes something to print
				logFile = new PrintWriter(System.out, true) ;
			}

			String tgaFile = null;
			String maskFile = null;
			logFile.print("File \t rTumorAv \t gTumorAv \t bTumorAv \t");
			logFile.print("lTumorAv \t alphaTumorAv \t betaTumorAv \t");
			logFile.print("LxTumorAv \t axTumorAv \t bxTumorAv \t");
			logFile.print("cxTumorAv \t hueTumorAv \t");

			logFile.print("rAvgDf \t gAvgDf \t bAvgDf \t");
			logFile.print("lAvgDf \t alphaAvgDf \t betaAvgDf \t");
			logFile.print("LxAvgDf \t axAvgDf \t bxAvgDf \t");
			logFile.print("CxAvgDf \t hueAvgDf \t ");

			logFile.print("rTumorVar \t gTumorVar \t bTumorVar \t");
			logFile.print("lTumorVar \t alphaTumorVar \t betaTumorVar \t");
			logFile.print("LxTumorVar \t axTumorVar \t bxTumorVar \t");
			logFile.print("cxTumorVar \t hueTumorVar \t");

			logFile.println("relChromRed \t relChromGreen \t relChromBlue \t");
			Colour c ;
			for (int i = 0; i < numberOfFiles; i++) {
				try {
					tgaFile =dir1+((String)files.elementAt(i))+".tga";
					maskFile = dir2+((String)files.elementAt(i))+".msk";
					TargaPicture tp = new TargaPicture(tgaFile) ;
					tp.read() ;
					tp.readMask(maskFile);
					logFile.print(((String)files.elementAt(i)) + "\t");

					c = new Colour(tp, logFile, boxSize) ;
					c.getColourAttributes() ;
				} catch (IOException e) {
System.out.println("Error reading file:");
				}
			} // End for
		} // end if

		else {
			try {
				boxSize = new Integer(args[2]).intValue() ;
				TargaPicture tp = new TargaPicture(args[0]) ;
				tp.read() ;
				tp.readMask(args[1]);
				Colour c = new Colour(tp, new PrintWriter(System.out, true), boxSize);
				c.getColourAttributes() ;
		  	} catch (IOException e) {
System.out.println("Error " +e);
			}
		}
	}
}








