package ipp.imse.fusionOptics.apps.permIMSE;

import ipp.imse.fusionOptics.imse.IMSEOptics105_35;
import ipp.imse.fusionOptics.imse.IMSEOptics135_50;
import ipp.imse.fusionOptics.imse.IMSEOptics135_50_rescaled;
import ipp.imse.fusionOptics.imse.IMSEOptics180_50;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25_expanded_filter;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25_imgFielded;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25_imgFielded_fibreFielded;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25_rescaled;
import ipp.imse.fusionOptics.imse.IMSEOptics75_25_silly_second_diag;
import ipp.imse.fusionOptics.mse.AugMSESystem;
import ipp.imse.fusionOptics.mse.AugMseFaroData;
import ipp.imse.fusionOptics.permIMSE.PermIMSE;
import ipp.imse.fusionOptics.permIMSE.PermIMSEBackup;
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.RACache;
import cache.randomAccessCache.RACacheService;
import fusionOptics.MinervaOpticsSettings;
import fusionOptics.Util;
import fusionOptics.collection.HitsCollector;
import fusionOptics.collection.ImageCollector;
import fusionOptics.collection.IntensityInfo;
import fusionOptics.collection.LensDepolarisationInfoCollector;
import fusionOptics.collection.PlaneAngleInfo;
import fusionOptics.drawing.SVGCylindricalProjection;
import fusionOptics.drawing.SVGRayDrawing;
import fusionOptics.drawing.VRMLDrawer;
import fusionOptics.interfaces.NullInterface;
import fusionOptics.optics.Box;
import fusionOptics.optics.SimpleDoubleConvexLens;
import fusionOptics.optimisation.OptimiseLensCurvature;
import fusionOptics.optimisationMulti.BendableDish;
import fusionOptics.optimisationMulti.MoveableElement;
import fusionOptics.optimisationMulti.OptimiseMulti;
import fusionOptics.optimisationMulti.RayBundle;
import fusionOptics.pointSpread.DualGaussianPSF;
import fusionOptics.pointSpread.PSFGrid;
import fusionOptics.pointSpread.PointSpreadBuilder;
import fusionOptics.pointSpread.PointSpreadFunction;
import fusionOptics.pointSpread.PointsPSF;
import fusionOptics.surfaces.Cylinder;
import fusionOptics.surfaces.Dish;
import fusionOptics.surfaces.Plane;
import fusionOptics.surfaces.Square;
import fusionOptics.tracer.Tracer;
import fusionOptics.types.Element;
import fusionOptics.types.Intersection;
import fusionOptics.types.Optic;
import fusionOptics.types.Pol;
import fusionOptics.types.RaySegment;
import fusionOptics.types.Surface;
import algorithmrepository.Algorithms;
import binaryMatrixFile.AsciiMatrixFile;
import binaryMatrixFile.BinaryMatrixFile;
import binaryMatrixFile.BinaryMatrixWriter;
import otherSupport.ColorMaps;
import otherSupport.StatusOutput;
import oneLiners.OneLiners;

/** Optimisation of Andreas' PermIMSEBackup to try to improve the focus
 * 
 * Result is pretty poor. Intesity is very low - < 1% of rays make it and rays
 * are only being first at the 6cm lens.
 * (though i'm not sure how that compares to what actually reaches the CCD in the prototype IMSE)
 * 
 * Focusing is /very/ poor, with a best <FWHM>= 0.8mm in a 20mm image (all points, all beams)
 * That's basically a focusing of worse than ~3cm 
 * 
 * 
 * @author oliford
 * 
 */
public class PermIMSEOptimisation_PermIMSEBackup {
		
	final static String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/permImse/optimise/";
	final static double R0 = 1.5;
	final static double R1 = 2.2;
	final static int nRaysPerBeamPoint = 2000; //need 5000 at the very least, because our success rate is very low 
	final static int nBeamPoints = 8;

	/** 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();
	public static PermIMSEBackup sys = new PermIMSEBackup();

	//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.lens0; //sys.mirrorBox.holeGlassFront;
	
	/** Don't draw rays that don't hit these elements */
	public static Surface mustHitToDraw = sys.portA11;
	//public static Surface mustHitToDraw = sys.mirror;
	//public static Surface mustHitToDraw = sys.l1Surface;
	
	final static double wavelen = 653e-9;
	
	public static void main(String[] args) {
		drawRayDiagrams();
		//drawBeamCyldImages();
	}
	
	public static void drawRayDiagrams(){

		OptimiseMulti optim = new OptimiseMulti();
		
		//generate ray bundles from source points
		for(int iB=0; iB < 4; iB++){
			int beamIdx = 4 + iB; //beams 5,6,7,8 (0-based)
			
			for(int iBP=0; iBP < nBeamPoints; iBP++){ // beam point
				double R = R0 + (R1 - R0) * iBP / (nBeamPoints - 1);
				double t[] = Algorithms.cylinderLineIntersection(sys.nbiStartAll[beamIdx], sys.nbiUnitAll[beamIdx], 
						new double[]{ 0, 0, 0 }, new double[]{ 0, 0, 1 }, R*R);
				
				double pos[] = Util.plus(sys.nbiStartAll[beamIdx], Util.mul(sys.nbiUnitAll[beamIdx], Algorithms.min(t)));
				System.out.println("point_Q" + (beamIdx+1) + "_" + iB + "_" + R + ": Rrec = " + FastMath.sqrt(pos[0]*pos[0] + pos[1]*pos[1]));
				
				//create a ray bundle (from the optimiser) from this point
				RayBundle rayBundle = new RayBundle((beamIdx+1) * 1000 + iBP); // bundle IDs are beam number * 1000, so 5th point of Q7 is 7004
				
				rayBundle.setImagePlane(imagePlane);
				//rayBundle.setTargetPos(new double[]{ ?? , ?? }); //will need to do later this to fix the magnification
				rayBundle.setSharpnessWeight(1.0); //we can weight the sharpness, intensity and targeting separately for each source point
				rayBundle.setIntensityWeight(0.0); 
				rayBundle.setTargetWeight(0.0);
				rayBundle.initRaysImaging(initRaysTarget, pos, wavelen, nRaysPerBeamPoint);
				
				optim.addRayBundle(rayBundle);
			}
		}
		
		optim.setTracingElements(sys);
		
		MoveableElement param0 = new MoveableElement(imagePlane, imagePlane.getNormal(), -0.1, 0.1);
		param0.setScale(0.1);
		optim.addParameter(param0);
		
		Dish lens0dish = sys.lens1.getConvexSurface();
		BendableDish param1 = new BendableDish(lens0dish, 0.7*lens0dish.getRadiusOfCurv(), 1.3*lens0dish.getRadiusOfCurv());
		optim.addParameter(param1);
		
		//Dish lens3dish = sys.lens3.getConvexSurface();
		//BendableDish param1 = new BendableDish(lens3dish, 0.7*lens3dish.getRadiusOfCurv(), 1.3*lens3dish.getRadiusOfCurv());
		//optim.addParameter(param1);

		optim.setHitsOutputFile(outPath + "/hits.bin");
		optim.setRegularDrawing(outPath + "/optim", VRMLDrawer.class, 10, 10);
		
		optim.eval();
		optim.dumpParams();
		optim.dumpRayBundles();
		
		optim.optimise(20);
		
		optim.dumpParams();
		optim.dumpRayBundles();
		
		
	}
	
}
