package ipp.imse.fusionOptics.permIMSE;

import algorithmrepository.Algorithms;

import com.sun.org.apache.xml.internal.security.algorithms.Algorithm;

import ipp.imse.fusionOptics.imse.IMSEOptics;
import fusionOptics.Util;
import fusionOptics.interfaces.Absorber;
import fusionOptics.interfaces.IsoIsoInterface;
import fusionOptics.interfaces.NullInterface;
import fusionOptics.interfaces.Reflector;
import fusionOptics.materials.SchottSFL6;
import fusionOptics.optics.DoubleGaussLens;
import fusionOptics.optics.SimpleDoubleConvexLens;
import fusionOptics.optics.SimplePlanarConvexLens;
import fusionOptics.surfaces.Disc;
import fusionOptics.surfaces.Dish;
import fusionOptics.surfaces.Iris;
import fusionOptics.surfaces.Square;
import fusionOptics.types.Medium;
import fusionOptics.types.Optic;
import jafama.FastMath;
import oneLiners.OneLiners;

/** Flexibile version of the permenant IMSE, with lots of lenses
 * for the purpose of the big all-out optimisation */
public class PermIMSEFlexible extends Optic {
	

	/** NBI Geometry, auto generated by 
	 * 		minerva-aug/seed.minerva.neutralBeams.aug.AugHeatingBeams.main()
	 */

	public static final double nbiStartAll[][] = { 
		{ -1.9022743, -1.3847706, 0.0000000 }, //SE Q1 NORM LOW
		{ -1.8792922, -1.4524737, 0.0000000 }, //SE Q2 TANG LOW
		{ -1.8792922, -1.4524737, 0.0000000 }, //SE Q3 TANG HIGH (MSE)
		{ -1.9022743, -1.3847706, 0.0000000 }, //SE Q4 NORM HIGH
		{ 2.3980239, 1.4752788, 0.0000000 },
		{ 3.3800075, 1.6501967, 0.0000000 },
		{ 3.3800075, 1.6501967, 0.0000000 },
		{ 2.3980239, 1.4752788, 0.0000000 },
	};
	
	public static final double nbiUnitAll[][] = {
		{ 0.9181567, 0.3869007, -0.0854169 }, //SE Q1 NORM LOW
		{ 0.9639529, 0.2519894, -0.0854169 }, //SE Q2 TANG LOW
		{ 0.9639529, 0.2519894, 0.0854169 }, //SE Q3 TANG HIGH (MSE)
		{ 0.9181567, 0.3869007, 0.0854169 }, //SE Q4 NORM HIGH
		{ -0.9659023, -0.2444109, -0.0854169 },
		{ -0.9878308, -0.1038252, -0.1158040 },
		{ -0.9878308, -0.1038252, 0.1158040 },
		{ -0.9659023, -0.2444109, 0.0854169 },
	};

	/** The beam indecies actually match the AUG naming convention (except for the 1-based indexing)
	 * I think Q means 'Quelle' ('source' in German) */  
	public static final int BEAM_Q1 = 0;
	public static final int BEAM_Q2 = 1;
	public static final int BEAM_Q3 = 2;	
	public static final int BEAM_Q4 = 3;
	public static final int BEAM_Q5 = 4;
	public static final int BEAM_Q6 = 5;
	public static final int BEAM_Q7 = 6;
	public static final int BEAM_Q8 = 7;
	
	public double globalUp[] = new double[]{ 0, 0, 1 };
	public static double rotationAngle = -11.25*3.1415927/180.0;
	
	//public double coordTest1[] = new double[]{ 0, 0, 0 };
	//public double coordTest2[] = new double[]{ 0, 1, 0 };
	//public double coordTest3[] = new double[]{ 0, 1.5, 0 };
	public double rotationAxis[] = new double[]{0, 0, 1};
	
	public double portCentreInPortCoords[] = new double[]{0.3545, 3.5977, -0.0222};
	public double mirrorCentreInPortCoords[] = new double[]{-0.2417, 2.3858, 0.1375};
	
	public double portCentre[] = OneLiners.rotateVectorAroundAxis(rotationAngle, rotationAxis, portCentreInPortCoords);
	public double mirrorCentre[] = OneLiners.rotateVectorAroundAxis(rotationAngle, rotationAxis, mirrorCentreInPortCoords);
	//public double portCentre[] = new double[]{ 1.150,  3.5977,  -0.0222 };
	//public double mirrorCentre[] = new double[]{ 0.200,  2.3858,    0.1375   };
	public double pointOnBeam[] = Util.mul(Util.plus(Util.plus(nbiStartAll[BEAM_Q8], Util.mul(nbiUnitAll[BEAM_Q8], 1.1)), Util.plus(nbiStartAll[BEAM_Q5], Util.mul(nbiUnitAll[BEAM_Q5], 1.1))) , 0.5);
	
	// mirrorNorm = (port/2 + beam/2 - mirror
	
	public double mirrorNormal[] = Util.reNorm(Util.plus(
							Util.reNorm(Util.minus(pointOnBeam, mirrorCentre)),
							Util.reNorm(Util.minus(portCentre, mirrorCentre))
						));
	
	double portNormal[] =  Util.reNorm(Util.minus(portCentre, mirrorCentre));
	double sourceNormal[] =  Util.reNorm(Util.minus( mirrorCentre, pointOnBeam));
	
	double smallTargetDiscPos[] = Util.plus(mirrorCentre, Util.mul(sourceNormal, -0.090));
	double l0Centre[] = Util.plus(mirrorCentre, Util.mul(sourceNormal, -0.050));
	double l1Centre[] = Util.plus(mirrorCentre, Util.mul(portNormal, 0.050));
	
	//since out mirror is partly the primary lens, we call the first glass one L2
	double l2Centre[] = Util.plus(portCentre, Util.mul(portNormal, -0.250)); //L2 out on inside of port
	double imgPlaneCentre[] = Util.plus(portCentre, Util.mul(portNormal, 0.232));
	double systemUp[] = Util.reNorm(Util.cross(portNormal, Util.cross(globalUp, portNormal))); 
	double mirrorUp[] = Util.reNorm(Util.cross(mirrorNormal, Util.cross(globalUp, mirrorNormal))); 
			
	public double mirrorRadiusOfCurv = 0.5;
	
	public double portRadius = 0.044;
	
	public double wavelen = 653e-9;
	
	//public Dish mirror = new Dish("mirror", mirrorCentre, mirrorNormal, mirrorRadiusOfCurv, 0.050, null, null, Reflector.ideal());
//	public Square mirror = new Square("mirror", mirrorCentre, mirrorNormal, mirrorUp, 0.05, 0.080, null, null, Reflector.ideal());
	public Square mirror = new Square("mirror", mirrorCentre, mirrorNormal, mirrorUp, 0.095, 0.115, null, null, Reflector.ideal());
	public Disc portA11 = new Disc("portA11", portCentre, portNormal, portRadius, NullInterface.ideal());
	public Square mirrorCentreBeam = new Square("mirrorCentreBeam", pointOnBeam, mirrorNormal, mirrorUp, 0.01, 0.01, null, null, NullInterface.ideal());
	
	//public Square test1 = new Square("mirrorCentreBeam", coordTest1, mirrorNormal, mirrorUp, 0.01, 0.01, null, null, NullInterface.ideal());
	//public Square test2 = new Square("mirrorCentreBeam", coordTest2, mirrorNormal, mirrorUp, 0.01, 0.01, null, null, NullInterface.ideal());
	//public Square test3 = new Square("mirrorCentreBeam", coordTest3, mirrorNormal, mirrorUp, 0.01, 0.01, null, null, NullInterface.ideal());

	//This is something like the viewing aperture near the little heat shield behind the ICRH limitier
	//We use it here only for firing the rays at, as it makes everything more efficient
	public Disc smallTargetDisc = new Disc("smallTargetdisc", smallTargetDiscPos, sourceNormal, 0.012, NullInterface.ideal()); 
	
	public Medium l1Medium = new Medium(new SchottSFL6());
	public SimplePlanarConvexLens lens0 = new SimplePlanarConvexLens("lens0", l0Centre, sourceNormal, 0.030, l1Medium, IsoIsoInterface.ideal(), 0.064, wavelen);
	public SimplePlanarConvexLens lens1 = new SimplePlanarConvexLens("lens1", l1Centre, portNormal, 0.030, l1Medium, IsoIsoInterface.ideal(), 0.250, wavelen);
	public Iris l1Iris = new Iris("l1Iris", l1Centre, portNormal, 0.1, 0.029, null, null, Absorber.ideal());

	public Disc l1Surface = new Disc("lens1Surface", l1Centre, portNormal, 0.030, null, null, NullInterface.ideal());
	
	public Medium l2Medium = new Medium(new SchottSFL6());
	public SimplePlanarConvexLens lens2 = new SimplePlanarConvexLens("lens2", l2Centre, portNormal, 0.06, l2Medium, IsoIsoInterface.ideal(), 0.36, wavelen);
	public SimplePlanarConvexLens lens3 = new SimplePlanarConvexLens("lens3", portCentre, portNormal, 0.03, l2Medium, IsoIsoInterface.ideal(), 1.0, wavelen);
	
	public Iris portCatch = new Iris("portCatch", portCentre, portNormal, 0.200, portRadius, null, null, Absorber.ideal());

	public Square imagePlane = new Square("imagePlane", imgPlaneCentre, portNormal, systemUp, 0.300, 0.300, null, null, Absorber.ideal());

	public IMSEOptics imseOptics;
	
	public PermIMSEFlexible() {
		this(null);
	}
	
	public PermIMSEFlexible(IMSEOptics imseOptics) {
		super("permIMSE");
		
		OneLiners.dumpArray(pointOnBeam);
		System.out.println(FastMath.sqrt(pointOnBeam[0]*pointOnBeam[0]+pointOnBeam[1]*pointOnBeam[1]));
	 	
		addElement(smallTargetDisc);
		addElement(mirror);
		addElement(lens0);
		addElement(lens1);
		addElement(l1Surface);
		addElement(lens2);
		addElement(lens3);
		addElement(l1Iris);
		addElement(portA11);
		addElement(mirrorCentreBeam);
		addElement(portCatch);
		//addElement(test1);
		//addElement(test2);
		//addElement(test3);
		
		//addElement(portCatch);
		addElement(imagePlane);
		
		if(imseOptics != null){
			this.imseOptics = imseOptics;
			imagePlane.setInterface(NullInterface.ideal()); //image plane is now virtual, so let light through
			
			
			double forwardOpticsAxis[] = Util.reNorm(imagePlane.getNormal());
			double imseBuildAxis[] = Util.reNorm(new double[]{ 1.0, 0.0, 0.0 }); //optic axis in IMSE's internal built gemeotry
			
			double rotateVector[] = Util.cross(forwardOpticsAxis, imseBuildAxis);
			System.out.println("Rotated by " + (rotationAngle * 180 / Math.PI));
			double rotateAngle = -FastMath.asin(Util.length(rotateVector));
			rotateVector = Util.reNorm(rotateVector);
			double rot[][] = Algorithms.rotationMatrix(rotateVector, rotateAngle);
					
			//now rotate and shift imse optics into position
			imseOptics.shift(imagePlane.getCentre());
			imseOptics.rotate(imagePlane.getCentre(), rot);
			
			OneLiners.dumpArray(imseOptics.ccd.getNormal());
			OneLiners.dumpArray(imagePlane.getNormal());
			
			
			addElement(imseOptics);
		}
	}
}
