package imseProc.proc.pitch;

import imseProc.proc.transform.FeatureTransformCubic;
import jafama.FastMath;

import java.util.List;

import mds.GMDSFetcher;
import oneLiners.OneLiners;
import otherSupport.RandomManager;
import otherSupport.SettingsManager;
import otherSupport.StatusOutput;
import seed.minerva.MinervaOpticsSettings;
import seed.minerva.apps.imse.WhichWayIsUpForProc;
import seed.minerva.aug.mse.AugMSESystem;
import seed.minerva.imse.IMSEOptics135_50_rescaled;
import seed.minerva.optics.Util;
import seed.minerva.optics.drawing.VRMLDrawer;
import seed.minerva.optics.interfaces.IsoIsoInterface;
import seed.minerva.optics.surfaces.Plane;
import seed.minerva.optics.tracer.Tracer;
import seed.minerva.optics.types.Element;
import seed.minerva.optics.types.Intersection;
import seed.minerva.optics.types.Pol;
import seed.minerva.optics.types.RaySegment;
import binaryMatrixFile.BinaryMatrixWriter;

/** The raytracing part of the pitch angle processor */
public class PitchProcRaytracer {
	
	/** What rays to trace */
	public double minIntensity = 0.01;
	public boolean traceReflections = false;
	
	/** Optical system setup */
	private AugMSESystem sys;
	
	private Plane imagePlane;
	private Plane polarisationPlane;
	
	private double wavelen = 653e-9;
	private double globaUp[] = new double[]{ 0, 0, 1 };
	
	private VRMLDrawer vrmlOut;
			
	private double targetPos[];

	public PitchProcRaytracer(int pulse, double targetPos[]) {
		this.targetPos = targetPos;
		
		sys = new AugMSESystem(new IMSEOptics135_50_rescaled());
		sys.setupForPulse(pulse);
		
		imagePlane = sys.imseOptics.ccd;
		polarisationPlane = sys.imseOptics.plate1;
		
	
		System.out.println("optics hash: " + sys.hashCode());
		
		//ignore the protection cover coating (or lack of)
		sys.mirrorBox.protectionCoverFront.setInterface(IsoIsoInterface.ideal());
		sys.mirrorBox.protectionCoverBack.setInterface(IsoIsoInterface.ideal());
		
		vrmlOut = null;//new VRMLDrawer(System.getProperty("java.io.tmpdir") + "/pitchRaytrace.vrml");
		if(vrmlOut != null){
			vrmlOut.setSmallLineLength(0.0001);
			vrmlOut.setDrawPolarisationFrames(true);
			vrmlOut.addVRML(AugMSESystem.vrmlScaleToAUGDDD);
		}
		
				
	}
	
	/** returns psi(pitch=0) and dPsi/dPitch */
	public double[] getPolPitchInfo(int beamIdx, double startPos[], double dPitch){	
		
		//global test element for polarisation definition consistent for all bundles - whatever that means
			
		//Bφ field direction at emission point
		double Bphi[] = Util.reNorm(Util.cross(startPos, globaUp));
		
		// VxBφ direction at emission, for Q3
		double VxB[] = Util.reNorm(Util.cross(sys.nbiUnitAll[beamIdx], Bphi)); // V x B for toroidal only
		
		Pol.recoverAll();
		
		RaySegment ray = new RaySegment();
		ray.wavelength = wavelen;
		ray.startPos =  startPos.clone();
		ray.dir = Util.reNorm(Util.minus(targetPos, startPos));
				
		ray.length = Double.POSITIVE_INFINITY;
		
		//ray.up = Util.cross(Util.reNorm(Util.cross(ray.dir, globaUp)), ray.dir); // This is MSE-like emission
		
		//randomise initial sense of up, as it shouldn't matter because we use ray.rotatePolRefFrame()
		ray.up = Util.reNorm(new double[]{
				RandomManager.instance().nextNormal(0, 1),
				RandomManager.instance().nextNormal(0, 1),
				RandomManager.instance().nextNormal(0, 1)
			});
		
	
		ray.E0 = new double[2][4];
		
		//B with Bphi plus some Bz (downwards is normal +ve Ip)
		double Bpitched[] = Util.plus( Util.mul(Bphi, FastMath.cos(dPitch)), new double[]{ 0, 0, -FastMath.sin(dPitch) });
		
		double starkE0[] = Util.reNorm(Util.cross(sys.nbiUnitAll[beamIdx], Bphi)); // V x B
		double starkE1[] = Util.reNorm(Util.cross(sys.nbiUnitAll[beamIdx], Bpitched)); // V x B
		
		ray.rotatePolRefFrame(starkE0);		
		ray.E0[0] = new double[]{ 1, 0, 0, 0 };
		
		ray.rotatePolRefFrame(starkE1);		
		ray.E0[1] = new double[]{ 1, 0, 0, 0 };	
									
		Tracer.trace(sys, ray, 100, minIntensity, traceReflections);
		
		List<Intersection> hits = ray.getIntersections(imagePlane); 

		if(vrmlOut != null){
			vrmlOut.drawRay(ray);
		}
		
		if(hits.size() > 0){
		
			
			Intersection imgHit = hits.get(0);
			
			Intersection polHit = imgHit.incidentRay.findFirstEarlierIntersection(polarisationPlane);
			if(polHit == null)
				return new double[]{ Double.NaN, Double.NaN };
						
			double Eproj[][] = Pol.projectToPlanesView(polHit, false);
			
			double psi0 = Pol.psi(Eproj[0]);
			double psi1 = Pol.psi(Eproj[1]);

			return new double[]{ psi0, (psi1 - psi0) / dPitch };
			
			
		}else{
			//no hits, it can't see this point
			return new double[]{ Double.NaN, Double.NaN };
		}
	}

	public void destroy() {
		if(vrmlOut != null){
			vrmlOut.drawOptic(sys);
			vrmlOut.addVRML("}"); //end of rotate/transform
			vrmlOut.destroy();	
		}
	}
}
