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

/**
* Implements the method of Stoecker et al. (1992) for calculating shape asymmetry.
*/

public class Asym4 {
	boolean DISPLAY = false;
	int cx,cy;
	long area;
	TargaPicture tp;
	PrintWriter log;

/**
* Constructor. Requires a targa file string s, and a printwriter object to
* send output to (ps). ps can be System.out, or otherwise a file stream.
*/
	public Asym4(String s, PrintWriter ps) throws IOException {
		this.log = ps;
		try {
			tp = new TargaPicture(s) ; // This is to find out the dimensions of the image
			//tp.read();  // Reading file is not necessary, as we dont use colour data, only mask data
		} catch (IOException e) {
log.println("IO Exception : opening Targa Picture -"+e) ;
			throw new IOException();
		}
	}

	/**
	* Constructor 2. Has a Display flag to allow display. 
	public Asym4(String s, PrintWriter ps, boolean display) throws IOException {
		this(s,ps) ;
		this.DISPLAY = display;
	}


	/**
	* Technique from Arvo (1991)
	*/
	void getCentroid(TargaPicture t) {
		long sumX=0,sumY=0,sum=0;
		boolean pt;
		int val =0;

		for (int i = 0; i < t.w; i++) {
			for (int j = 0; j < t.h; j++) {
				pt = t.getMaskData(i,j) ;
				if (pt== true) val = 1; else val = 0;
				sumX += i*val ;
				sumY += j*val ;
				sum += val;
			}
		}
		cx = (int)(sumX/sum);
		cy = (int)(sumY/sum);
		area = sum;
System.out.println("cx :"+cx+" cy:"+cy);
	}


	/**
	* This works out the angle of the principal axis.
	*/
	double getPrincipalAxisAngle () {
		double sumTop = 0, sumBot1 = 0, sumBot2 = 0;
		double sum = 0;
		int val = 0;
		boolean pt;

		for (int i =0; i < tp.w; i++){
			for (int j = 0; j < tp.h; j++) {
				pt = tp.getMaskData(i,j) ;
				if (pt == true) val = 1; else val = 0;
				sumTop += (i-cx)*(j-cy)*val;
				sumBot1 += ((i-cx)*(i-cx))*val;
				sumBot2 += ((j-cy)*(j-cy))*val;

				sum += val;
			}
		}
		sumTop /= sum;
		sumTop *= 2;
		sumBot1 /=sum;

		sumBot2 /=sum;

		sum = sumTop/(sumBot1-sumBot2) ;
		sum = java.lang.Math.atan(sum)/2;
		return sum;

	}


	/**
	* This method rotates the image by the angle found in the previous method, to align the principal 
	* axis with the x-axis.
	*/
	TargaPicture rotate(double angle) {
		double cosine = java.lang.Math.cos(-angle) ;
		double sine = java.lang.Math.sin(-angle) ;
		int diagonal = (int)java.lang.Math.sqrt(tp.w*tp.w+tp.h*tp.h);
		TargaPicture tp2 = new TargaPicture(diagonal+1,diagonal+1) ;
		int centreX = tp.w/2;
		int centreY = tp.h/2;
		boolean pt;
		int sourceX,sourceY;

		for (int i = 0; i < tp.h; i++) {
			for (int j = 0; j < tp.w; j++) {
				sourceX = (int)((j-cx)*cosine+(i-cy)*sine);
				sourceY = (int)((i-cy)*cosine-(j-cx)*sine);
				if ((int)cx+sourceX >=0 && (int)cx+sourceX < tp.w && (int)cy+sourceY >=0 && (int)cy+sourceY < tp.h) {
					pt = tp.getMaskData((int)cx+sourceX,(int)cy+sourceY);
					if (pt== true) {
						tp2.setMaskData(j,i,true) ;
						tp2.setImageData(j,i,tp.getImageData(sourceX+(int)cx,sourceY+(int)cy)) ;
					}
				}
			}
		}
		return tp2;

	}

	/** 
	* Main method. Calculates area asymmetry, using the method of Stoecker et al (1992).
	* Find principal axis angle, rotate image to align principal axis and x-axis, and then overlap two
	* halves of image. Firstly reflect in x-axis, and then in y-axis. The minimum of these values
	* makes up the Asymmetry index of Stoecker et al (1992)
	*/
	double getAreaAsymmetry() {
		double sum=0,sum1=0,sum2=0;
		int count = 0;
		getCentroid(this.tp) ;
		double principalAngle = getPrincipalAxisAngle() ;
		TargaPicture temp = rotate(principalAngle);
		getCentroid(temp);
		if (DISPLAY == true) new TargaView(temp);


		for (int i = 1; i < tp.h/2; i++) {
			for (int j = 0; j < tp.w; j++) {
				if ((temp.getMaskData(j,cy+i) ^ temp.getMaskData(j,cy-i))==true) {
					if (DISPLAY == true) temp.setImageData(j,i+cy,0xFFFF0000);
					sum1++;
				}

			}
		}
		if (DISPLAY == true) new TargaView(temp);

		for (int i = 0; i < temp.h; i++) {
			for (int j = 1; j < tp.w/2; j++) {
				if ((temp.getMaskData(cx+j,i) ^ temp.getMaskData(cx-j,i))==true) {
					if (DISPLAY == true) temp.setImageData(cx+j,i,0xFFffff00);
					sum2++;
				}
			}
		}

		log.println("\t\t" + sum1/area + "\t" + sum2/area);

		if (DISPLAY == true) new TargaView(temp);
		return 0;
	}



	/**
	* Main method. Takes 2 or 3 arguments. Argument 1 is either ``iterate" or a TGA image file.
	* If argument 1 = iterate
	* 	Argument 2 = script file name
	* 	argument 3 = output file name
	* ELSE argument 1 = image file
	* 	Argument 2 = mask file
	*
	* That is, 2 arguments run the method on a single lesion, while three arguments run it on
	* a series of lesions, and store the results in an output file.
	*/
	public static void main(String []args) {
		if (args[0].equals("iterate") == true) {
			Vector v = new Vector() ;
			FileReader fr = null;
			StreamTokenizer st = null;

			int file = 0, numberOfFiles = 0;
			String dir1=null, dir2=null;

			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 = (int)st.nval;
					v.addElement(new Integer(file)) ;
				}
			} catch (IOException e) {
/* If there is an error in the specification file, it is not recoverable, so exit */
System.out.println("Read Error in Specification File: ACTION: EXIT") ;
System.exit(1);
	  		}


/* Otherwise, proceed and calculate asymmetry index for each file listed in specification*/
			// 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) ;
			}


			Asym4 a = null;
			for (int i = 0; i < numberOfFiles; i++) {
				// Print image file number (ie filename)
				logFile.print(((Integer)v.elementAt(i)).toString());
				try { 
					a = new Asym4(dir1+((Integer)v.elementAt(i)).toString()+".tga",logFile);
					a.tp.readMask(dir2+((Integer)v.elementAt(i)).toString()+".msk");
					a.getAreaAsymmetry();
				} catch (IOException e) {
					continue ;
				}
			}
		}

		else { // Only iterate once, and print output to std out, and display
			try{
				Asym4 a = new Asym4(args[0]+".tga",new PrintWriter(System.out,true), true);
				a.tp.readMask(args[1]+".msk");
				a.getAreaAsymmetry();
			} catch (IOException e) {
System.out.println("Error reading tga file: "+e );
			}
		}

	}
}







