import java.awt.*;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.plugin.PlugIn;
import java.util.*;
import java.awt.event.*;


/*	This plugin plots the mean intensity or standard deviation of the image intesity values.
	The y-axis corresponds to the intensity values (mean or std).
	The x-axis corresponds to the slice numbers.

	Gary Chinga 050108

	050323	Added the max difference option calculating the max difference in greylevel for a given image.
	050503	Fixed a bug for calculating the max difference.
*/

public class PlotJ_1c implements PlugIn, ItemListener {

    private static int index1;
    private ImageStack stack;
    private int nSlices;
	public static final int LINE=2,BOX=3, CIRCLE=0, CROSS=5,DOT=6,TRIANGLE=4, X=1;
	public static final String[] markers ={"X","Cross","Circle","Triangle","Box"};
	public static  int marker;
	public static final int STD=0,MEAN=1,SKEW=2,KURT=3,MIN=4,MAX=5,MAXDIFF=6;
	public static final String[] yVariable ={"Std","Mean","Skewness","Kurtosis","Min","Max","Max difference"};
	public static  int yVar;

 	String xAxis = "0,10";
  	String yAxis = "0,10";
	boolean xyFix=false, mean=true, std=false;

	Checkbox cbFix = new Checkbox("Fix x- and y-axes",xyFix);
	Label lbxFix = new Label("x-axis (min,max): ");
	TextField tfxFix = new TextField("0,10",0);
	Label lbyFix = new Label("y-axis (min,max): ");
	TextField tfyFix = new TextField("0,10",0);
	Label lbNone1 = new Label("");
	Label lbNone2 = new Label("");
	Label lbNone3 = new Label("");
	Label lbNone4 = new Label("");
	Label lbNone5 = new Label("");
	Label lbNone6 = new Label("");

    public void run(String arg) {
        if (showDialog())
            plot(stack);
    }

    public boolean showDialog() {
        int[] wList = WindowManager.getIDList();
        if (wList==null) {
            IJ.noImage();
            return false;
        }
        String[] titles = new String[wList.length];
        for (int i=0; i<wList.length; i++) {
            ImagePlus imp = WindowManager.getImage(wList[i]);
            if (imp!=null)
                titles[i] = imp.getTitle();
            else
                titles[i] = "";
        }
        if (index1>=titles.length)index1 = 0;
        GenericDialog gd = new GenericDialog("PlotJ");
        gd.setLayout(new GridLayout(9,2,0,0));
        gd.addChoice("Stack: ", titles, titles[index1]);
	 	gd.addChoice("y-axis: ", yVariable, yVariable[0]);
		gd.add(lbNone5);gd.add(lbNone6);
		gd.add(cbFix);gd.add(lbNone1);
		gd.add(lbxFix); gd.add(tfxFix);
		gd.add(lbyFix); gd.add(tfyFix);
	 	gd.addChoice("Markers: ", markers, markers[2]);

		lbxFix.setVisible(false);
		tfxFix.setVisible(false);
		lbyFix.setVisible(false);
		tfyFix.setVisible(false);

		cbFix.addItemListener(this);
        gd.add(lbNone3);gd.add(lbNone4);

     	gd.showDialog();
        if (gd.wasCanceled())
            return false;
        index1 = gd.getNextChoiceIndex();
        yVar = gd.getNextChoiceIndex();
        marker = gd.getNextChoiceIndex();
        xyFix = cbFix.getState();
        xAxis = tfxFix.getText();
        yAxis= tfyFix.getText();

        String title1 = titles[index1];
        ImagePlus slicestack = WindowManager.getImage(wList[index1]);
        stack = slicestack.getStack();
        return true;
   }

    public void plot(ImageStack stack) {
        nSlices = stack.getSize();

        double xmin=0,xmax=0,ymin=0,ymax=0;
     	int width = stack.getWidth();
        int height = stack.getHeight();
        ImageProcessor ip1;
		float[] yValues = new float[nSlices];
		float[] xValues= new float[nSlices];
		for(int i=0; i<nSlices; i++) {
			IJ.showProgress((double)(i)/nSlices);
			IJ.showStatus("a: "+(i)+"/"+nSlices);
		 	ip1 = stack.getProcessor(i+1); ip1 = ip1.convertToFloat();
           //	float[] iValues = (float[])ip1.getPixelsCopy();
           	yValues[i] = getStatistics(ip1, yVar);
           	xValues[i]=(i+1);
		}
		String yLabel=" ";
		if (yVar==MEAN) yLabel="Intensity (Mean)";
		if (yVar==STD) yLabel="Intensity (Std)";
		if (yVar==MAX) yLabel="Intensity (Max)";
		if (yVar==MIN) yLabel="Intensity (Min)";
		if (yVar==SKEW) yLabel="Intensity (Skewness)";
		if (yVar==KURT) yLabel="Intensity (Kurtosis)";
		if (yVar==MAXDIFF) yLabel="Intensity (Max difference)";

		Plot p = new Plot("Intensity values", "Slices",yLabel, xValues, yValues);
		if (xyFix){
			int[] xLimit=s2ints(xAxis);
			int[] yLimit=s2ints(yAxis);
			xmin=xLimit[0];		xmax=xLimit[1];		ymin=yLimit[0];	ymax=yLimit[1];
		p.setLimits(xmin,xmax,ymin,ymax);

		}
		if (marker==0) p.addPoints(xValues,yValues, PlotWindow.X);
		if (marker==1) p.addPoints(xValues,yValues, PlotWindow.CROSS);
		if (marker==2) p.addPoints(xValues,yValues, PlotWindow.CIRCLE);
		if (marker==3) p.addPoints(xValues,yValues, PlotWindow.TRIANGLE);
		if (marker==4) p.addPoints(xValues,yValues, PlotWindow.BOX);
		p.show();
		IJ.showProgress(1.0);
	}


    public int[] s2ints(String s) {
        StringTokenizer st = new StringTokenizer(s, ", \t");
        int nInts = st.countTokens();
        int[] ints = new int[nInts];
        for(int i=0; i<nInts; i++) {
            try {ints[i] = Integer.parseInt(st.nextToken());}
            catch (NumberFormatException e) {IJ.write(""+e); return null;}
        }
        return ints;
    }


	float calculateMean(ImageProcessor ipTemp){
		int ww=ipTemp.getWidth();
		int hh=ipTemp.getHeight();
		float[] tempSlice = (float[])getPixelValues(ipTemp);
		double mValue=0;
		int counter=(ww*hh);
		for (int j=0; j<counter; j++){mValue += tempSlice[j];}
		return (float)(mValue/counter);
	}

	float calculateStd(ImageProcessor ipTemp,float mValue){
		int ww=ipTemp.getWidth();
		int hh=ipTemp.getHeight();
		float[] tempSlice = (float[])getPixelValues(ipTemp);
		int counter=ww*hh;
		float sValue=0;
		if (counter==1) {return (float) (sValue);}
		else{
			for (int j=0; j<counter; j++){sValue += sqr2(mValue-tempSlice[j]);}
			return (float) (Math.sqrt(sValue/(counter-1)));
		}
	}

	private float[] getPixelValues(ImageProcessor ipTemp){
		int ww=ipTemp.getWidth();
		int hh=ipTemp.getHeight();
		float[] pValues = new float[ww*hh];
		for (int y = 0; y < hh; y++){
			for (int x = 0; x < ww; x++){
				int index = x + ww*y;
				pValues[index]=ipTemp.getPixelValue(x,y);
			}
		}
		return pValues;
	}


	public float getStatistics(ImageProcessor ipTemp, int p){
		int ww=ipTemp.getWidth();
		int hh=ipTemp.getHeight();
		int N=ww*hh;
		float min = Float.MAX_VALUE;
		float max = -Float.MAX_VALUE;
		float[] rValues = new float[7];
		float ra = 0, rq = 0, sk = 0, ku = 0;
		float zMean=calculateMean(ipTemp);
		float zStd = calculateStd(ipTemp,zMean);
		for (int y = 0; y < hh; y++){
			for (int x = 0; x < ww; x++){
				float temp = ipTemp.getPixelValue(x,y);
				float zTemp=temp-zMean;
				sk += sqr3(zTemp/zStd);
				ku += sqr4(zTemp/zStd);
				ra += Math.abs(zTemp);
				if (temp<min) min = temp;
				if (temp>max) max = temp;
			}
		}
		// Calculate Roughness stats (Ra, Rq, Rsk, Rku)
		//rValues[1] = (float)(ra/(ww*hh));
		rValues[1] = (float)(zMean);
		rValues[0] = (float)(zStd);
		//float Pq= rValues[0];

		rValues[2] = (float)(sk/N);
		rValues[3] = (float)((ku/N)-3);
		rValues[4] = min;
		rValues[5] = max;
		rValues[6] = max-Math.abs(min);

		return rValues[p];
	}

	double sqr2(double x) {return x*x;}
	double sqr3(double x) {return x*x*x;}
	double sqr4(double x) {return x*x*x*x;}

	public void itemStateChanged(ItemEvent e) {
		if(e.getItemSelectable() == cbFix){
			if (e.getStateChange() == ItemEvent.SELECTED) {
				lbxFix.setVisible(true);
				tfxFix.setVisible(true);
				lbyFix.setVisible(true);
				tfyFix.setVisible(true);

			}
			else {
				lbxFix.setVisible(false);
				tfxFix.setVisible(false);
				lbyFix.setVisible(false);
				tfyFix.setVisible(false);
			}
		}
	}
}


