package ipp.imse.fusionOptics.apps.imse;

import ipp.imse.fusionOptics.mse.AugMSESystem;
import jafama.FastMath;

import java.util.LinkedList;
import java.util.List;

import cache.randomAccessCache.RACache;
import cache.randomAccessCache.RACacheService;
import fusionOptics.MinervaOpticsSettings;
import fusionOptics.Util;
import fusionOptics.interfaces.Absorber;
import fusionOptics.optics.Box;
import fusionOptics.pointSpread.DualGaussianPSF;
import fusionOptics.pointSpread.PSFGrid;
import fusionOptics.pointSpread.PSFStatsSourceInterpolation;
import fusionOptics.pointSpread.PointSpreadFunction;
import fusionOptics.surfaces.Cylinder;
import fusionOptics.types.Element;
import fusionOptics.types.Optic;
import fusionOptics.types.Surface;

import otherSupport.BinarySTLFile;
import otherSupport.BinarySTLFile.Triangles;
import otherSupport.RandomManager;

import binaryMatrixFile.BinaryMatrixFile;

import algorithmrepository.Algorithms;

import oneLiners.OneLiners;

/** Generate images from PSF interpolation */

public class PSFReImage {
	
	final static String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/augImse/psfCollect";
	
	//Grids as used in SimpleImaging
	//final static String cacheSet = "augImse-gridKeyed-full"; 
	//final static String cacheSet = "augImse-gridKeyed-large-1000x1000";
	//final static String cacheSet = "augImse-gridKeyedData-30x30-mueller";
	//final static String cacheSet = "augImse-small";
	final static String cacheSet = "augImse-withWall-64x44x36";
	//final static String cacheSet = "augImse-gridKeyedData-100x100";
	
	final static double imageX[] = OneLiners.linSpace(-0.006, 0.006, 1000);
	final static double imageY[] = OneLiners.linSpace(-0.006, 0.006, 1000);
	
	final static AugMSESystem sys = new AugMSESystem();
	
	static PSFStatsSourceInterpolation psfInterp;
	static PSFGrid psfGrid;
	
	public static void main(String[] args) {
		psfGrid = new PSFGrid(cacheSet);
		psfInterp = new PSFStatsSourceInterpolation(psfGrid);
		 
		//findSimpleViewPoint(psfGrid, 20000);
		
		//psfGrid.dumpAllData(outPath + "/allPSFData.bin");
		
		//imageGridPSFs(psfGrid);
		
		//reimageGrid();
		
		//if(true)return;
		
		double p[] =  new double[]{ -1.2, -1.3, 0.06};
		Box box = new Box("box", p, 0.12, 0.12, 0.08, null, Absorber.ideal());
		Util.rotateOnX(box, p, 10*Math.PI/180);
		Util.rotateOnY(box, p, -30*Math.PI/180);
		Util.rotateOnZ(box, p, 45*Math.PI/180);
		//reimage(outPath + "/reimageBox.bin", psfInterp, Util.drawingToPoints(box.draw(), 0.002));
		
		reimage(outPath + "/reimageBeams.bin", psfInterp, Util.drawingToPoints(sys.makeBeamCylds().draw(), 0.001));
		
		/*List l = sys.makeBeamCylds().draw();
				 //new LinkedList<double[][]>();
		//drawSTL(l,"/work/ipp/augddd-models/vessel/limiter_s14_small.stl");
		drawSTL(l,"/work/ipp/augddd-models/vessel/PSL_s01-16_2010_10_21_small.stl");
		drawSTL(l,"/work/ipp/augddd-models/vessel/vessel_s12-13_small.stl");
		drawSTL(l,"/work/ipp/augddd-models/vessel/vessel_s14-15_small.stl");
		
		reimage(outPath + "/reimageBackground.bin", psfInterp, 
					Util.drawingToPoints(l, 0.01)
				);	
	*/
	}
	
	private static List<double[][]> drawSTL(List<double[][]> ret, String fileName){
		Triangles tri = BinarySTLFile.mustRead(fileName);
		
		if(ret == null)
			ret = new LinkedList<double[][]>();
		
		//double cameraPos[] = new double[]{ }; 
		
		double ang = -1.963;
		for(int i=0; i < tri.count; i++){
			//double viewVec[] = Util.reNorm(Util.minus(tri.vertex1[i], cameraPos));
			
			//only draw surfaces facing the camera
			{//if(Util.dot(tri.normal[i], viewVec) < 0){
				double l[][] = new double[][]{
						{ tri.vertex1[i][0], tri.vertex2[i][0], tri.vertex3[i][0], tri.vertex1[i][0] },
						{ tri.vertex1[i][1], tri.vertex2[i][1], tri.vertex3[i][1], tri.vertex1[i][1] },
						{ tri.vertex1[i][2], tri.vertex2[i][2], tri.vertex3[i][2], tri.vertex1[i][2] } };
				
				for(int j=0; j < 4; j++){
					l[0][j] /= 1000;
					l[1][j] /= 1000;
					l[2][j] /= 1000;
					double x = FastMath.cos(ang) * l[0][j] - FastMath.sin(ang) * l[1][j];
					double y = FastMath.sin(ang) * l[0][j] + FastMath.cos(ang) * l[1][j];
					l[0][j] = x;
					l[1][j] = y;
				}
				
				ret.add(l);
			}
		}
		
		return ret;
	}
	
	private static void findSimpleViewPoint(PSFGrid psfGrid2, int nRands) {
		double src[][] = psfGrid.getSourcePositions();
		int nSrc = src.length;
		
		double meanCross[] = new double[3];
		double sumI = 0;
		for(int i=0; i < nRands; i++){
			int j,k;
			PointSpreadFunction psfJ, psfK;
			do{
				j = (int)(RandomManager.instance().nextUniform(0, 1) * nSrc);
				psfJ = psfGrid.get(src[j]);
			}while(psfJ.isEmpty());
			do{
				k = (int)(RandomManager.instance().nextUniform(0, 1) * nSrc);
				psfK = psfGrid.get(src[k]);
			}while(j == k || psfK.isEmpty());
			double u[] = psfJ.getMeanSourceDir();
			double v[] = psfK.getMeanSourceDir();
			u = Util.reNorm(u);
			v = Util.reNorm(v);
			double I = psfJ.getMeanMueller()[0] * psfK.getMeanMueller()[0];
			double a = Algorithms.pointOnLineNearestAnotherLine(src[j], u, src[k], v);
			meanCross[0] += (src[j][0] + a * u[0]) * I;
			meanCross[1] += (src[j][1] + a * u[1]) * I;
			meanCross[2] += (src[j][2] + a * u[2]) * I;
			sumI += I;
			System.out.print(".");
		}
		System.out.println();
			
		meanCross[0] /= sumI;
		meanCross[1] /= sumI;
		meanCross[2] /= sumI;
		
		OneLiners.dumpArray(meanCross);
	}

	public static void reimage(String fileName, PSFStatsSourceInterpolation psfInterp, double obj[][]){
		
		double image[][] =  new double[imageX.length][imageY.length];
		
		for(int i=0; i < obj.length; i++) {
			PointSpreadFunction psf = psfInterp.getInterpolatedPSF(obj[i][0], obj[i][1], obj[i][2]);
			
			//PointSpreadFunction psf = psfColl.getPSFExact(new double[]{ gX[3], gY[iX], gZ[iY] });
			if(psf != null)
				//psf.addToGrid(imageX, imageY, image, 1);
				((DualGaussianPSF)psf).getInner().addToGrid(imageX, imageY, image, 1);
				
			System.out.print(".");
		}
		
		BinaryMatrixFile.mustWrite(fileName, imageX, imageY, image, false);
	}
	
	public static void reimageGrid(){
		double l[] = OneLiners.linSpace(0.2, 0.8, 60);
		double z[] = OneLiners.linSpace(-0.3, 0.3, 60);
		
		double image[][] =  new double[imageX.length][imageY.length];
		
		for(int iL=0; iL < l.length; iL++) {
			for(int iZ=0; iZ < z.length; iZ++) {
			
				double px = sys.nbiStart[0] + l[iL] * sys.nbiUnit[0]; 
				double py = sys.nbiStart[1] + l[iL] * sys.nbiUnit[1];
				double pz = sys.nbiStart[2] + l[iL] * sys.nbiUnit[2] + z[iZ];
				PointSpreadFunction psf = psfInterp.getInterpolatedPSF(px,py,pz);
				//PointSpreadFunction psf = psfGrid.get(new double[]{ px, py, pz });
				
				if(psf != null)
					psf.addToGrid(imageX, imageY, image, 1);
					//((DualGaussianPSF)psf).addToGridMonteCarlo(imageX, imageY, image, 1);
			}	
			System.out.print(".");
		}
		System.out.println();
		BinaryMatrixFile.mustWrite(outPath + "/reimageGrid.bin", imageX, imageY, image, false);
		
	}
	
	private static void imageGridPSFs(PSFGrid psfGrid) {

		double sourcePoses[][] = psfGrid.getSourcePositions();
		double image[][] =  new double[imageX.length][imageY.length];
		
		
		int n = sourcePoses.length;
		System.out.print("Imaging " + n + " psfs:");
		long t0 = System.currentTimeMillis(), updatePeriod = MinervaOpticsSettings.getUserAttentionSpan();
		for(int i=0; i < n; i++){
			PointSpreadFunction psf = psfGrid.get(sourcePoses[i]);
			
			if(!psf.isEmpty()){
				System.out.print(".");
				psf.addToGridMonteCarlo(imageX, imageY, image, 1, 100);
			}else{
				System.out.print("x");				
			}
			
			if((System.currentTimeMillis() - t0) > updatePeriod){ 
				System.out.print("\n" + i + " / " + n + " = " + (i*100.0/n) + "%");
				t0 = System.currentTimeMillis();
				BinaryMatrixFile.mustWrite(outPath + "/gridPSFsImage.bin", imageX, imageY, image, false);
			}
		}
		BinaryMatrixFile.mustWrite(outPath + "/gridPSFsImage.bin", imageX, imageY, image, false);
		
		if(true)return;
	}
}
