package seed.minerva.apps.permIMSE;

import jafama.FastMath;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import cache.common.Cache;
import cache.randomAccessCache.RACacheService;

import algorithmrepository.Algorithms;
import binaryMatrixFile.AsciiMatrixFile;
import binaryMatrixFile.BinaryMatrixFile;
import binaryMatrixFile.BinaryMatrixWriter;

import otherSupport.ColorMaps;
import otherSupport.StatusOutput;


import oneLiners.OneLiners;

import seed.minerva.MinervaOpticsSettings;
import seed.minerva.aug.mse.AugMSESystem;
import seed.minerva.aug.mse.AugMseFaroData;
import seed.minerva.imse.IMSEOptics105_35;
import seed.minerva.imse.IMSEOptics135_50;
import seed.minerva.imse.IMSEOptics135_50_rescaled;
import seed.minerva.imse.IMSEOptics180_50;
import seed.minerva.imse.IMSEOptics75_25;
import seed.minerva.imse.IMSEOptics75_25_expanded_filter;
import seed.minerva.imse.IMSEOptics75_25_imgFielded;
import seed.minerva.imse.IMSEOptics75_25_imgFielded_fibreFielded;
import seed.minerva.imse.IMSEOptics75_25_rescaled;
import seed.minerva.imse.IMSEOptics75_25_silly_second_diag;
import seed.minerva.optics.Util;
import seed.minerva.optics.collection.HitsCollector;
import seed.minerva.optics.collection.ImageCollector;
import seed.minerva.optics.collection.IntensityInfo;
import seed.minerva.optics.collection.LensDepolarisationInfoCollector;
import seed.minerva.optics.collection.PlaneAngleInfo;
import seed.minerva.optics.drawing.SVGCylindricalProjection;
import seed.minerva.optics.drawing.SVGRayDrawing;
import seed.minerva.optics.drawing.VRMLDrawer;
import seed.minerva.optics.interfaces.NullInterface;
import seed.minerva.optics.optics.Box;
import seed.minerva.optics.optics.SimpleDoubleConvexLens;
import seed.minerva.optics.optimisation.OptimiseLensCurvature;
import seed.minerva.optics.pointSpread.DualGaussianPSF;
import seed.minerva.optics.pointSpread.PSFGrid;
import seed.minerva.optics.pointSpread.PointSpreadBuilder;
import seed.minerva.optics.pointSpread.PointSpreadFunction;
import seed.minerva.optics.pointSpread.PointsPSF;
import seed.minerva.optics.surfaces.Cylinder;
import seed.minerva.optics.surfaces.Plane;
import seed.minerva.optics.surfaces.Square;
import seed.minerva.optics.tracer.Tracer;
import seed.minerva.optics.types.Element;
import seed.minerva.optics.types.Intersection;
import seed.minerva.optics.types.Optic;
import seed.minerva.optics.types.Pol;
import seed.minerva.optics.types.RaySegment;
import seed.minerva.optics.types.Surface;
import seed.minerva.permIMSE.PermIMSE;


/** Generates the outputs for the AUG system for presentations etc.
 * 
 * @author oliford
 *
 */
public class PrettyPictures {
	
	/*final static String outPath = MinervaSettings.getAppsOutputPath() + "/rayTracing/augImse/pictures-focusing/";
	final static int nRays = 10;
	final static double p0 = 0.58; // 0.4 to 0.8 ish
	final static double p1 = 0.62; // 0.4 to 0.8 ish
	final static int nPoints = 4;
	//*/
	
	final static int beamIdx = PermIMSE.BEAM_Q7;
	
	final static String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/permImse/pictures/Q"+(beamIdx+1)+"/";
	final static int nRays = 200;
	final static double R0 = 1.5; // 0.4 to 0.8 ish
	final static double R1 = 2.2; // 0.4 to 0.8 ish
	final static int nPoints = 16;
	//*/
	
	
	/** What rays to trace */
	public static double minIntensity = 0.01;
	public static boolean traceReflections = false;
	
	private static final double svgRayWidth = 0.0001;
	private static final double svgOpticWidth = 0.0003;
	
	/** Optical system setup */
	public static PermIMSE sys = new PermIMSE();
	
	//we require three optics;
	// 1) The imaging plane, which will collect the light INTENSITY.
	// 2) The polarisation sensitive plane, which collects the light polarisation.
	// 3) The inital target to fire rays at.
	// e.g. for the normal MSE system, 1 is thee fibre plane, 2 is the PEMs and 3 is the main mirror:		
	public static Plane imagePlane = sys.imagePlane;
	public static Plane polarisationPlane = sys.imagePlane;
	public static Element initRaysTarget = sys.mirror; //sys.mirrorBox.holeGlassFront;
	
	/** Don't draw rays that don't hit these elements */
	public static Surface mustHitToDraw = sys.portA11;
	
	final static double wavelen = 653e-9;
	
	public static void main(String[] args) {
		drawRayDiagrams();
		//drawBeamCyldImages();
	}
	
	public static void drawRayDiagrams(){
		
		VRMLDrawer vrmlOut = new VRMLDrawer(outPath + "/pictures.vrml", 0.005);
		vrmlOut.setDrawPolarisationFrames(false);
		vrmlOut.addVRML(AugMSESystem.vrmlScaleToAUGDDD);
		//vrmlOut.setSkipRays(39);
		
		
		double rot[][] = new double[3][];
		rot[0] = sys.portA11.getNormal();
		rot[2] = Util.reNorm(Util.cross(rot[0], sys.mirror.getNormal()));
		rot[1] = Util.reNorm(Util.cross(rot[2], rot[0]));
				
		//One svg drawer in cartesian x/y/z and one in the mirror normal plane
		SVGRayDrawing svgOutCart = new SVGRayDrawing(outPath + "/raysCart", new double[]{ -2, -4, -1, 1, 0, 1 }, true);
		SVGRayDrawing svgOutFlat = new SVGRayDrawing(outPath + "/raysFlat", new double[]{ -2, -4, -1, 1, 0, 1 }, true, rot);
		SVGCylindricalProjection svgPol = new SVGCylindricalProjection(outPath + "/rayPol.svg", 5, -2, 2, true);
		double cols[][] = ColorMaps.jet(nPoints);
		svgOutCart.generateLineStyles(cols, svgRayWidth, svgOpticWidth);
		svgOutFlat.generateLineStyles(cols, svgRayWidth, svgOpticWidth);
		svgPol.generateLineStyles(cols, svgRayWidth, svgOpticWidth);

		drawVRMLFaroData(vrmlOut);
		
		IntensityInfo intensityInfo = new IntensityInfo(sys);
		
		StatusOutput stat = new StatusOutput(PrettyPictures.class, nPoints*nRays);		
		for(int j=0; j < nPoints; j++){
			double R = R0 + (R1 - R0) * j / (nPoints - 1);
			double t[] = Algorithms.cylinderLineIntersection(sys.nbiStartAll[beamIdx], sys.nbiUnitAll[beamIdx], 
					new double[]{ 0, 0, 0 }, new double[]{ 0, 0, 1 }, R*R);
			
			double startPos[] = Util.plus(sys.nbiStartAll[beamIdx], Util.mul(sys.nbiUnitAll[beamIdx], Algorithms.min(t)));
			System.out.println("Rrec = " + FastMath.sqrt(startPos[0]*startPos[0] + startPos[1]*startPos[1]));
			//double p = p0 + (p1 - p0) * j / (nPoints - 1); 
			/*double startPos[] = new double[]{ //-1.3, -1.3, 0.1 };
					sys.nbiStartAll[beamIdx][0] + p * sys.nbiUnitAll[beamIdx][0],
					sys.nbiStartAll[beamIdx][1] + p * sys.nbiUnitAll[beamIdx][1],
					sys.nbiStartAll[beamIdx][2] + p * sys.nbiUnitAll[beamIdx][2],
			};
			*/
			svgOutCart.getSVG3D().startGroup("point" + j + "_" + R);
			svgOutFlat.getSVG3D().startGroup("point" + j + "_" + R);
			svgPol.getSVG().startGroup("point" + j + "_" + R);
			
			int nAttempts = 0, nHit = 0;
			
			for(int i=0; i < nRays; i++){
				 do{
					stat.doStatus(i);
					
					Pol.recoverAll();
					
					RaySegment ray = new RaySegment();
					ray.startPos =  startPos.clone();
					ray.dir = Tracer.generateRandomRayTowardSurface(ray.startPos, initRaysTarget);
					//ray.dir = Util.reNorm(Util.minus(sys.mirrorCentre, startPos));
					
					ray.length = Double.POSITIVE_INFINITY;
					ray.up = Util.cross(Util.reNorm(Util.cross(ray.dir, new double[]{0,0,1})), ray.dir);
					
					ray.E0 = PointSpreadFunction.getInputStatesForMuellerCalc();
					ray.wavelength = wavelen;
					
					Tracer.trace(sys, ray, 100, minIntensity, traceReflections);
					
					stat.doStatus(j*nRays+i);
					
					ray.processIntersections(null, intensityInfo);
					
					List<Intersection> hits = ray.getIntersections(mustHitToDraw); 
									
					if(hits.size() > 0){
						vrmlOut.drawRay(ray, cols[j]);
						svgOutCart.drawRay(ray, j);
						svgOutFlat.drawRay(ray, j);
						svgPol.drawRay(ray, j);
												
						nHit++;
						break;
					}
					if(nAttempts > 10000){
						break;
					}
					nAttempts++;
				}while(true);
			}
			svgOutCart.getSVG3D().endGroup();
			svgOutFlat.getSVG3D().endGroup();
			svgPol.getSVG().endGroup();
			
			System.out.println("\n---------------------------------------- "+j+" ----------------------------------------");
			System.out.println(j + "(R=" + R + "):\t" + nAttempts + "\t" + nHit);
			
			//intensityInfo.dump(true);
			
			intensityInfo.dump(Arrays.asList(new Surface[]{
					sys.mirror,
					sys.portA11
				}), sys.mirror, true);
			intensityInfo.reset();
			
			sys.addElement(new Box("pos_" + j, startPos, 0.002, 0.002, 0.002, null, NullInterface.ideal()));

		}
		
		
		
		vrmlOut.drawOptic(sys);
		vrmlOut.addVRML("}"); //end of rotate/transform
		vrmlOut.destroy();
		stat.done();
		

		svgOutCart.drawElement(sys);
		svgOutCart.destroy();

		svgOutFlat.drawElement(sys);
		svgOutFlat.destroy();
		
		svgPol.drawElement(sys);
		svgPol.destroy();
		
	}

	private static void drawVRMLFaroData(VRMLDrawer vrmlOut) {
		AugMseFaroData losInfo = new AugMseFaroData();
		//sys.addElement(AugMSESystem.makeAllBeamCylds());
		//sys.addElement(losInfo.makeCylds());
		
		//sys.addElement(new Cylinder("R2m1", new double[]{ 0,0,0}, new double[]{ 0,0,1}, 2.1, 1.5, NullInterface.ideal()));
		//sys.addElement(new Cylinder("R1m0", new double[]{ 0,0,0}, new double[]{ 0,0,1}, 1.0, 1.5, NullInterface.ideal()));
		
		double A[] = losInfo.getCameraPos();
		double v[] = losInfo.getCameraViewVec();
		double u[] = losInfo.getCameraUpVec();
		
		vrmlOut.drawPolygonEdge(new double[][]{ 
				{ A[0], A[0] + 10 * v[0] }, 
				{ A[1], A[1] + 10 * v[1] }, 
				{ A[2], A[2] + 10 * v[2] },
		});
		vrmlOut.drawPolygonEdge(new double[][]{ 
				{ A[0], A[0] + 10 * u[0] }, 
				{ A[1], A[1] + 10 * u[1] }, 
				{ A[2], A[2] + 10 * u[2] },
		});
		
		
		//sys.addElement(new Optic("losIntBoxes", losInfo.intBoxes));
		//sys.addElement(new Box("basePlane", new double[]{ 0, 0, -2 }, new double[]{ -2, -2, -1 }, null, NullInterface.ideal()));
	}

	
}
