package ipp.imse.fusionOptics.imse;

import java.util.LinkedList;

import fusionOptics.MinervaOpticsSettings;
import fusionOptics.Util;
import fusionOptics.drawing.SVGRayDrawing;
import fusionOptics.drawing.VRMLDrawer;
import fusionOptics.interfaces.Absorber;
import fusionOptics.interfaces.IsoIsoInterface;
import fusionOptics.interfaces.NullInterface;
import fusionOptics.lenses.Nikon135mmF28;
import fusionOptics.lenses.Nikon50mmF11;
import fusionOptics.lenses.SchneiderXenon25mmF095;
import fusionOptics.materials.AlphaBariumBorate;
import fusionOptics.materials.BK7;
import fusionOptics.materials.IsotropicFixedIndexGlass;
import fusionOptics.optics.DoubleGaussLens;
import fusionOptics.optics.SimpleDoubleConvexLens;
import fusionOptics.optimisation.AutoFocus;
import fusionOptics.optimisation.OptimiseLensCurvature;
import fusionOptics.surfaces.Cylinder;
import fusionOptics.surfaces.Disc;
import fusionOptics.surfaces.Iris;
import fusionOptics.surfaces.Plane;
import fusionOptics.surfaces.Square;
import fusionOptics.tracer.Tracer;
import fusionOptics.types.Interface;
import fusionOptics.types.Material;
import fusionOptics.types.Medium;
import fusionOptics.types.Optic;
import fusionOptics.types.Surface;

import algorithmrepository.Algorithms;



/** Optics for IMSE system, to be coupled to the end of 'tube optics'
 * 
 * Setup with x along the system axis, and x=0 at the virtual image plane
 *  (TubeOptics.fibrePlane)
 * 
 * @author oliford
 *
 */
public abstract class IMSEOptics extends Optic {
	
	public double wavelength = 653e-9;
	
	/** Focal length of 'objective' lens, before the cell */
	protected double focalLength1;
	
	protected boolean rotateObjLens;
	
	/** Space between object lens 'plane' and cell */
	protected double objLensPlaneToCell;
	
	/** Diamter of the cell aparture */
	public double cellApature = 0.050;
	public double cellRadius = 0.015; //0.075/2;
	
	/** Diameter of the cell apature */
	public double cellLength = 0.075;	
	
	public double plateSetLength = 0.035;
	
	/** Clear-aperture of the most restrictive component in the cell */
	public double plate1Apature = 0.025; //with the 25mm FLC
	public double plate2Apature = 0.030; //and 30mm plates
	
	/** Space between object lens 'plane' and cell */
	protected double imgLensPlaneToCell; //for the 50mm
	
	/** Focal length of the imaging lens on the camera */
	protected double focalLength2;
	
	/** Focusing. Set to 0 and run this to autofocus, then put the number in here*/
	public double focusAdjustObj = 0;
	public double focusAdjustImg = 0;
	
	/** Angle of 0° to Savart axes w.r.t to IMSE frame (CCW cam POV) */
	public double savartToIMSEFrame = Double.NaN; //in setupForPulse() 
	
	/** Sign of angle, (because of changes in the Savart segement used) */
	public int savartAngleSign = 0; //in setupForPulse() 
	
	public IMSEOptics() {
		super(null);
		this.name = getClass().getSimpleName();
		
	}
	
	public Square fibrePlane;
	public SimpleDoubleConvexLens fibrePlaneFieldLens1, fibrePlaneFieldLens2;
	public Plane filterAtImage, filterAtPEMs, filterAtField;
	
	public Optic objLens, imgLens; 
	public Iris objLensIris;
	public Cylinder cell;
	public Iris cellFrontIris;
	public Disc plate1;
	public Iris plate1Iris;
	public Disc plate2;
	public Iris plate2Iris;
	
	public Square ccd;
	public Iris imgLensIris;
	
	public double imgLensCaseRadius, objLensCaseRadius;

	//for seeing how much light actually leaves len1,
	//public Disc objLensCellEnd;
	
	
	protected double imgLensPos(){
		return focalLength1 + objLensPlaneToCell + cellLength + imgLensPlaneToCell;
	}
	
	protected void init() {
		
		fibrePlane = new Square("fibrePlane", 
										new double[]{ 3*Tracer.reHitTolerance, 0, 0 }, //just far enough to not cause problems
										new double[]{ 1, 0, 0 }, new double[]{ 0, 0, 1 }, 0.1, 0.1, null, null, NullInterface.ideal());
		
		fibrePlaneFieldLens1 = new SimpleDoubleConvexLens("fibrePlaneFieldLens1",
				new double[]{ -0.004, 0, 0 },
				new double[]{ 1, 0, 0 },
				0.025,
				new Medium(new BK7()),
				IsoIsoInterface.ideal(),
				0.080,
				wavelength);
		
		filterAtImage = new Square("filterAtImage", 
				new double[]{ 0.010, 0, 0 },
				new double[]{ 1, 0, 0 }, new double[]{ 0, 0, 1 }, 0.050, 0.050, null, null, NullInterface.ideal());
	
		filterAtPEMs = new Disc("filterAtPEMs", 
				new double[]{ -0.2056, 0, 0 }, // at PEMS
				//new double[]{ -0.918, 0, 0 }, // in tube between L1 and L2
				new double[]{ 1, 0, 0 }, 0.070, null, null, NullInterface.ideal());
	
		filterAtField = new Square("filterAtField", 
				new double[]{ focalLength1 + objLensPlaneToCell + cellLength + 0.002, 0, 0 },
				new double[]{ 1, 0, 0 }, new double[]{ 0, 0, 1 }, 0.050, 0.050, null, null, NullInterface.ideal());
	
		fibrePlaneFieldLens2 = new SimpleDoubleConvexLens("fibrePlaneFieldLens2",
				new double[]{ 0.002, 0, 0 },
				new double[]{ 1, 0, 0 },
				0.025,
				new Medium(new BK7()),
				IsoIsoInterface.ideal(),
				0.160,
				wavelength);
		
		/*public Material lensMat = new IsotropicFixedIndexGlass(3.5);
		public Interface lensIFace = IsoIsoInterface.ideal();	
		public Medium lens1Medium = new Medium(lensMat);
		public SimpleDoubleConvexLens objLens = new SimpleDoubleConvexLens("objLens", 
				new double[]{ focalLength1, 0, 0 }, 
				new double[]{ -1, 0, 0 }, 	
				cellApature*0.82, lens1Medium, lensIFace, focalLength1, wavelength);
			//*/
		/*public DoubleGaussLens objLens = new DoubleGaussLens("objLens", 
				new double[]{ focalLength1 + focusAdjustObj, 0, 0 }, 
				focalLength1);//*/
		
		
		objLensIris = new Iris("objLensIris", 
				new double[]{ focalLength1, 0, 0 }, 
				new double[]{ -1, 0, 0}, 0.050, 0.999*/*(cellApature*0.82)*/objLensCaseRadius, Absorber.ideal());//*/
		
		//objLensCellEnd = new Disc("objLensCellEnd", 
		//		new double[]{ focalLength1 + objLensPlaneToCell - 0.005, 0, 0},
		//		new double[]{ 1,0,0 },  0.050, null, null, NullInterface.ideal());
		
		cell = new Cylinder("cell", 
				new double[]{ focalLength1 + objLensPlaneToCell + cellLength/2, 0, 0 },
				new double[]{ 1,0,0 }, cellApature/2, cellLength, Absorber.ideal());
		
		cellFrontIris = new Iris("cellFrontIris", 
				new double[]{ focalLength1 + objLensPlaneToCell, 0, 0 },
				new double[]{ 1,0,0 }, 4*cellRadius, cellApature/2, null, null, Absorber.ideal());
		
		double plateSetShift = 0.000;
		plate1 = new Disc("plate1", 
				new double[]{ focalLength1 + objLensPlaneToCell + cellLength/2 - plateSetLength/2 + plateSetShift, 0, 0 }, 
				new double[]{ 1, 0, 0}, //normal in ray prop dir to get polarisation sense correct
				plate1Apature/2, NullInterface.ideal());
		
		plate1Iris = new Iris("plate1Iris", 
				new double[]{ focalLength1 + objLensPlaneToCell + cellLength/2 - plateSetLength/2 + plateSetShift, 0, 0 }, 
				new double[]{ -1, 0, 0}, 1.001*cellApature/2, plate1Apature/2, Absorber.ideal());
		
		plate2 = new Disc("plate2", 
				new double[]{ focalLength1 + objLensPlaneToCell + + cellLength/2 + plateSetLength/2 + plateSetShift, 0, 0 }, 
				new double[]{ 1, 0, 0},  //normal in ray prop dir to get polarisation sense correct
				plate2Apature/2, NullInterface.ideal());
		
		plate2Iris = new Iris("plate2Iris", 
				new double[]{ focalLength1 + objLensPlaneToCell + + cellLength/2 + plateSetLength/2 + plateSetShift, 0, 0 }, 
				new double[]{ -1, 0, 0}, 1.001*cellApature/2, plate2Apature/2, Absorber.ideal());
		
	
		/*public SimpleDoubleConvexLens imgLens = new SimpleDoubleConvexLens("imgLens", 
				new double[]{ focalLength1 + lensSpacing - 0.005, 0, 0 }, 
				new double[]{ -1, 0, 0 }, 
				0.050*0.28/2, lens1Medium, lensIFace, focalLength2, wavelength);
				//*/
		
		
		
		/*public DoubleGaussLens imgLens = new DoubleGaussLens("objLens", 
				new double[]{ focalLength1 + lensSpacing + focusAdjustImg, 0, 0 }, 
				focalLength2);
		//*/
		imgLensIris = new Iris("imgLensIris", 
				new double[]{ imgLensPos(), 0, 0 }, 
				new double[]{ -1, 0, 0}, 
				0.050, 
				0.999*/*(0.050*0.28/2)*/imgLensCaseRadius, Absorber.ideal());
		
	
		ccd = new Square("ccd", new double[]{ imgLensPos() + focalLength2 + focusAdjustImg, 0, 0}, new double[]{ 1, 0, 0 }, 
										new double[]{ 0, 0, 1}, 0.020, 0.020, null, null, Absorber.ideal());
		
		if(rotateObjLens)
			Util.rotateOnZ(objLens, new double[]{ focalLength1 + focusAdjustImg, 0, 0 }, Math.PI);
		
		addElement(filterAtPEMs);
		
		addElement(fibrePlane);
		//addElement(fibrePlaneFieldLens1);
		addElement(filterAtImage);
		//addElement(fibrePlaneFieldLens2);
		addElement(objLens);
		addElement(objLensIris);
		//addElement(objLensCellEnd);
		addElement(cellFrontIris);
		addElement(cell);
		addElement(plate1);
		addElement(plate1Iris);
		addElement(plate2);
		addElement(plate2Iris);
		addElement(filterAtField);
		addElement(imgLens);
		addElement(imgLensIris);
		addElement(ccd);
		
	}
	
	public void setupForPulse(int pulse){
		if(pulse > 29000)
			throw new IllegalArgumentException(pulse + " looks like an AUG pulse number, this currently only takes the GMDS pulse number.");
		
		if(pulse >= 0/*154*/ && pulse <= 185){ 
			//Jan2013 last day. Matched to pulse 178 (IMSE comparison pulse)	
			
			//camera was /roughly/ straight. It's base plate was bolted to the frame via
			//two large nuts, a flat wedge was inserted between baseplate and camera body
			//and then camera was strapped down to all of that with a luggage strap
			
			// ... I can't remember if I had put the restraining bolts/pins in as well at that point
			
			//to match transform (postEPS13)
			//ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getNormal(), -3.0 * Math.PI/180));
			
			//to match hard edge of image with something in the tube (field lens?)
			//For adjusting by looking at /work/sci/ipp/scrap/transformRaytraceMatch.py: 
			///    U: +ve moves pikn circle up/down , R: +ve moves pink circle left/right 
			ccd.shift(Util.plus(Util.mul(ccd.getUp(), 0.000700), Util.mul(ccd.getRight(), -0.000450)));
			
			// GA optimised, p=178, θ = 26.84211581544059, φ = -162.73290525863897
			//  α = 3.147460741626234, (Δr.n) = 0.021795069790915316
			//mainMirror.setCentre(new double[]{ -0.32506631212756376, -2.268120982499499, -0.38563791576031314 });
			//mainMirror.setNormal(new double[]{ -0.8520415329808587, -0.26484467902652936, 0.4515335226391977 });
			//ccd.setUp(new double[]{ -0.011693163219413175, 0.17821859034865759, 0.9839214419800301 });
			
			// for 135_50_rescaled
			// GA optimised, p=178, θ = 26.86783965689429, φ = -162.4157715256032
			//  α = 3.164104641666381, (Δr.n) = 0.005903814301694178
			//mainMirror.setCentre(new double[]{ -0.31141002029072773, -2.2636751288576695, -0.39244027573301155 });
			//mainMirror.setNormal(new double[]{ -0.8503692297073675, -0.269495400248522, 0.4519340686558025 });
			//ccd.setUp(new double[]{ -0.011975078569145148, 0.1782868570599602, 0.9839056835352381 });
			savartToIMSEFrame = -19.4 * Math.PI/180;
			savartAngleSign = -1;
			
		}else if(pulse > 250 && pulse <= 368){
			//April2013 Hydrogen week (after the weekend). Matched to pulse 358 (the last W-melting pulse)
			
			//Camera was rotated relative to the frame. This was measured from a photo of the camera on the frame
			// as 10.1°. 
			// Later, the savart plate angle to the camera was measured by the FFT component angles as 37.0° ±0.6 (or 36.5)
			// (maybe that needs a 45° shift, so camera is only 8.0° out from Savart)
			// the Savart measures θa=-19.4° for frame vertical θf=0. I.e the Savart 0 is 19.4 to the clockwise side of vertical.			
			// the camera is 8.0° back in the anticlockwise direction, putting it at 11.1° (or 10.9)
			// ... so let's say 11.0, to be in the middle somewhere
			//ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getNormal(), (-10.1) * Math.PI/180)); //from the photo
			ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getNormal(), (-11.1) * Math.PI/180)); //from the complicated thing
			
			//we now can't adjust that, but we can adjust the frame and mirror angles
			
			//tweaked to match postEPS2013 transform
			//For adjusting by looking at /work/sci/ipp/scrap/transformRaytraceMatch.py: 
			///    U: +ve moves pikn circle up , R: +ve moves pink circle left 
			//ccd.shift(Util.plus(Util.mul(ccd.getRight(), -0.000650), Util.mul(ccd.getUp(), -0.000300)));
			//ccd.shift(Util.plus(Util.mul(ccd.getUp(), 0.000600), Util.mul(ccd.getRight(), -0.000550)));
			
			//to match hard edge of image with something in the tube (field lens?)
			ccd.shift(Util.plus(Util.mul(ccd.getRight(), -0.000550), Util.mul(ccd.getUp(), 0.000620)));
			//ccd.shift(Util.plus(Util.mul(ccd.getRight(), 0.000200), Util.mul(ccd.getUp(), -0.000200))); //opposite shift, does it change it?? ... not much
			//ccd.rotate(ccd.getCentre(), 
			//Algorithms.rotationMatrix(ccd.getNormal(), (4.0) * Math.PI/180));
			

			//--- vvv ------------------ Cut Here ------------------ vvv ---
			// GA optimised, p=351, θ = 26.68012723894003, φ = -162.10240734974573
			//  α = 0.02326173654971623, (Δr.n) = 8.991698565806314E-5
			//mainMirror.setCentre(new double[]{ -0.29178646779323864, -2.2946808745667355, -0.39013124172472646 });
			//mainMirror.setNormal(new double[]{ -0.8502870011673638, -0.27459576598445473, 0.44900911009602273 });
			//ccd.setUp(new double[]{ 0.17430109207350022, 0.12352324034158836, 0.9769140895685249 });
			//--- /666 ------------------ Cut Here ------------------ ^^^ ---
			
			savartToIMSEFrame = -19.4 * Math.PI/180;
			savartAngleSign = -1;
			
		}else if(pulse >= 438 && pulse <= 617){ // augMay2014,  from 07/05/2014 onwards camera and frame were not moved
			
			//camera is roughly 45° CW
			ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getNormal(), (-45) * Math.PI/180)); //from the complicated thing
			
			//reverse the CCD, because the camera reports it's image backwards
			ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getUp(), Math.PI)); 
			ccd.shift(Util.plus(Util.mul(ccd.getRight(), -0.000830), Util.mul(ccd.getUp(), 0.000000)));
			
			if(pulse >= 560){ //Plates were rotated 14/05/2014
				savartToIMSEFrame = -(90 - 9.8) * Math.PI/180; //very roughly (see gmds/SPECLAB/920)
				savartAngleSign = +1; // We're now in different segment, see pres/notes2014)
			}else{
				savartToIMSEFrame = -19.4 * Math.PI/180;
				savartAngleSign = -1;
			}
				
		}else if(pulse == -20130701){ //that's a date, roughly the EPS, this means 'setup as in he EPS paper'
			//rotate CCD by ~10° (+ve clockwise camera POV) to IMSE frame for April2013 data
			// Measured from photo in HGW lab before disassembly. See notes2013
			
			ccd.rotate(ccd.getCentre(), Algorithms.rotationMatrix(ccd.getNormal(), (-10.1) * Math.PI/180));
			savartToIMSEFrame = -19.4 * Math.PI/180;
			savartAngleSign = -1;
			
		}else{
			throw new IllegalArgumentException("Unknown optics setup for pulse " + pulse);
		}
	}
	
	public static void main(String[] args) {
		String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/augImse";
		IMSEOptics imseOptics = new IMSEOptics135_50();
		
		
		VRMLDrawer vrmlOut = new VRMLDrawer(outPath + "/imseOptics/model.vrml", 0.0005);
		vrmlOut.drawOptic(imseOptics);
		vrmlOut.destroy();
		
		double w = 0.3;
		SVGRayDrawing svgOut = new SVGRayDrawing(outPath + "/imseOptics", new double[]{ 0, -w, -w, 2, w, w }, true);
		
		svgOut.setSkipRays(97);
		svgOut.generateLineStyles(new double[0][0], 0.0001);
		
		//imseOptics.fixFocus();
		
		/*AutoFocus af = new AutoFocus();		
		// Imaging lens
		af.setTracingElements(imseOptics, imseOptics.ccd);
		af.initRaysParallel(imseOptics.imgLens.getSurfaces().get(0), new double[]{1,0,0}, 4*Math.PI/180, imseOptics.objLens.getBoundarySphereRadius()+0.010, imseOptics.wavelength, 5, 5000);
		af.setMovement(imseOptics.imgLens, new double[]{1,0,0}, -0.005, 0.005);
		//*/
		
		//Objective lens
		/*af.setTracingElements(imseOptics, imseOptics.fibrePlane);
		af.initRaysParallel(imseOptics.objLens.getSurfaces().get(0), new double[]{-1,0,0}, 4*Math.PI/180, imseOptics.objLens.getBoundarySphereRadius()+0.010, imseOptics.wavelength, 5, 5000);
		af.setMovement(imseOptics.objLens, new double[]{1,0,0}, -0.020, 0.020);
		//*/
		/*
		af.setSVGOut(outPath + "/imseOptics/autoFocus", 777);
		//af.setHitsDebugFile(outPath + "/autoFocusHits.bin");
		af.optimise(20);
		//*/
		

		svgOut.drawElement(imseOptics);
		svgOut.destroy();
	}
}
