package ipp.imse.fusionOptics.apps.imse;

import imseProc.proc.transform.FeatureTransform;
import imseProc.proc.transform.FeatureTransformCubic;
import ipp.imse.fusionOptics.imse.IMSEOptics135_50;
import ipp.imse.fusionOptics.imse.IMSEOptics135_50_rescaled;
import ipp.imse.fusionOptics.mse.AugMSESystem;
import ipp.imse.fusionOptics.mse.AugMseFaroData;
import jafama.FastMath;

import java.util.Arrays;
import java.util.List;

import descriptors.gmds.GMDSSignalDesc;
import fusionOptics.MinervaOpticsSettings;
import fusionOptics.Util;
import fusionOptics.collection.IntensityInfo;
import fusionOptics.collection.PlaneAngleInfo;
import fusionOptics.drawing.SVGCylindricalProjection;
import fusionOptics.drawing.SVGRayDrawing;
import fusionOptics.drawing.VRMLDrawer;
import fusionOptics.interfaces.IsoIsoInterface;
import fusionOptics.interfaces.IsoIsoStdFresnel;
import fusionOptics.interfaces.NullInterface;
import fusionOptics.interfaces.Reflector;
import fusionOptics.materials.FusedSilica;
import fusionOptics.media.MediumInMagneticField;
import fusionOptics.optics.Box;
import fusionOptics.pointSpread.PointSpreadFunction;
import fusionOptics.surfaces.Plane;
import fusionOptics.surfaces.Square;
import fusionOptics.tracer.Tracer;
import fusionOptics.types.Element;
import fusionOptics.types.Intersection;
import fusionOptics.types.Material;
import fusionOptics.types.Optic;
import fusionOptics.types.Pol;
import fusionOptics.types.RaySegment;
import fusionOptics.types.Surface;

import mds.GMDSFetcher;

import algorithmrepository.Algorithms;
import binaryMatrixFile.BinaryMatrixFile;
import binaryMatrixFile.BinaryMatrixWriter;
import oneLiners.OneLiners;
import otherSupport.ColorMaps;
import otherSupport.RandomManager;
import otherSupport.SettingsManager;
import otherSupport.StatusOutput;
import signals.gmds.GMDSSignal;

/** For every pixel of the image transform, calculate:
 * theta_0: Polarisation at IMSE frame from 'global up' polarised and no field coils (Faraday)
 * dTheta/dBt: Change of polarisation per unit field on axis (1.65m), due to protection glass.
 * 
 * and verify that theta is linear with both field and input angle.
 *  
 * 
 * @author oliford
 *
 */
public class PrettyPicturesZeeman {

	private double bvacMeasurementLocation = 1.65; // R / m
	
	private double savartPlateRotation = 19.4 * Math.PI / 180;
	
	private final static String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/augImse/prettyPictures/zeeman/";
	private final static int nRaysDraw = 1;
	//*/
	
	/** What rays to trace */
	public static double minIntensity = 0.01;
	public static boolean traceReflections = false;
		
	/** Optical system setup */
	private static AugMSESystem sys = new AugMSESystem(new IMSEOptics135_50_rescaled());
	
	// IMSE or AUG pulse number for matched hardware config
	private final static int pulse = 178; // Jan2013 primary
	//private final static int pulse = 351; // Apr2013 primary
	
	private static Plane imagePlane = sys.imseOptics.ccd;
	private static Plane polarisationPlane = sys.imseOptics.plate1;
	
	private final static double wavelen = 653e-9;
	private final static double globaUp[] = new double[]{ 0, 0, 1 };
	
	//output grid
	//int nGridX = 20, nGridY = 20;
	int nGridX = 32, nGridY = 24; //Full grid, for Zeeman stuff
	
	public static void main(String[] args) { (new PrettyPicturesZeeman()).run();	}
	
	private VRMLDrawer vrmlOut;
	private double targetPos[];
	
	private void run(){
		
		sys.setupForPulse(pulse);
	
		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 = new VRMLDrawer(outPath + "/pictures.vrml");
		if(vrmlOut != null){
			vrmlOut.setSmallLineLength(0.002);
			vrmlOut.setDrawPolarisationFrames(true);
			vrmlOut.addVRML(AugMSESystem.vrmlScaleToAUGDDD);			
			vrmlOut.setSkipRays(0);
		}
		
		//fire rays at the average location that the transform check saw them hit the mirror
		//rays from mid-beam axis dont work, something is very very wrong
		String serverID = SettingsManager.defaultGlobal().getProperty("imseProc.cis.gmdsServerID", "pccis");		
		GMDSFetcher gmds = GMDSFetcher.defaultInstance(serverID);
		FeatureTransformCubic xform = new FeatureTransformCubic(gmds, "AUG", pulse);		
		String viewPointName = "view (TransformMatch-"+sys.hashCode()+")";		
		//targetPos = xform.getPointByName(viewPointName).getPosXYZ();
		targetPos = xform.getViewPosition();
	
		double theta0[][] = new double[nGridY][nGridX];
		double dThetaDBt[][] = new double[nGridY][nGridX];
		
		getFinalAngles(new double[]{ -2.0, -0.8, 0.3 }, -0.1);
		getFinalAngles(new double[]{ -2.1, -0.25, 0.5 }, -0.1);
		getFinalAngles(new double[]{ -2.03, -0.95, 0.34 }, -0.1);
		getFinalAngles(new double[]{ -1.95, -0.76, 0.55 }, -0.1);
		
		if(vrmlOut != null){
			vrmlOut.drawOptic(sys);
			vrmlOut.addVRML("}"); //end of rotate/transform
			vrmlOut.destroy();
		}
		
		BinaryMatrixFile.mustWrite(outPath + "/theta0.bin", theta0, false);
		BinaryMatrixFile.mustWrite(outPath + "/dThetaDBt.bin", dThetaDBt, false);
				
	}
	
	/** returns psi(pitch=0) and dPsi/dPitch */
	private double[] getFinalAngles(double startPos[], double dBt){	
		Pol.recoverAll();
		
		double theta[] = new double[3];
		for(int iF=0; iF < 3; iF++){ //foreach field value
			
			//calc field vector at protection cover
			double pos[] = sys.mirrorBox.protectionCoverFront.getBoundarySphereCentre();
			double R = FastMath.sqrt(pos[0]*pos[0] + pos[1]*pos[1]);
			double B[] = Util.cross(globaUp, Util.minus(pos, new double[]{ 0, 0, pos[2] }));
			B = Util.mul(Util.reNorm(B), iF * dBt * bvacMeasurementLocation / R);
				
			//load into optics model
			Material mat = new FusedSilica();
			MediumInMagneticField magGlass = new MediumInMagneticField(mat, null, 300, B); //field will get filled by RayTracedCamera		
			sys.mirrorBox.protectionCoverFront.setBackMedium(magGlass);
			sys.mirrorBox.protectionCoverBack.setFrontMedium(magGlass);
			
			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.E0 = new double[3][4];
			
			//one pol as close as poss to globalup, one as close as poss to toroidal dir
			ray.up = Util.cross(Util.reNorm(Util.cross(ray.dir, globaUp)), ray.dir); //polar sense of globalUp
			ray.E0[0] = new double[]{ 1, 0, 0, 0 };
							
			double radial[] = Util.reNorm(Util.minus(startPos, new double[]{ 0, 0, startPos[2]} ));
			double toroidalDir[] = Util.reNorm(Util.cross(radial, globaUp));
			
			//check that the tor.up is really what the polarisation thing says it is
			//double rayUp[] = Util.reNorm(Util.cross(Util.cross(ray.dir, globaUp), ray.dir));
			//double rayTor[] = Util.reNorm(Util.cross(Util.cross(ray.dir, toroidalDir), ray.dir));
			//System.out.println("up-toroidal in torus = " + FastMath.acos(-Util.dot(rayUp, rayTor))*180/FastMath.PI);
						
			
			ray.rotatePolRefFrame(toroidalDir);
			ray.E0[1] = new double[]{ 1, 0, 0, 0 };
			
			System.out.println("up-toroidal in torus = " + (Pol.psi(ray.E0[0]) - Pol.psi(ray.E0[1]))*180/FastMath.PI);
			
			double aBitOfBZDir[] = Util.reNorm(Util.plus(toroidalDir, Util.mul(new double[]{ 0,  0, -1 }, 0.10)));
			ray.rotatePolRefFrame(aBitOfBZDir);
			ray.E0[2] = new double[]{ 1, 0, 0, 0 };
			
			
						
			vrmlOut.drawPolygonEdge(
					new double[][]{ 
							{ ray.startPos[0], ray.startPos[0] + toroidalDir[0] * 0.1 },
							{ ray.startPos[1], ray.startPos[1] + toroidalDir[1] * 0.1 },
							{ ray.startPos[2], ray.startPos[2] + toroidalDir[2] * 0.1 }
					});
			
			vrmlOut.drawPolygonEdge(
					new double[][]{ 
							{ ray.startPos[0], ray.startPos[0] + globaUp[0] * 0.1 },
							{ ray.startPos[1], ray.startPos[1] + globaUp[1] * 0.1 },
							{ ray.startPos[2], ray.startPos[2] + globaUp[2] * 0.1 }
					});
								
			Tracer.trace(sys, ray, 100, minIntensity, traceReflections);
			
			if(vrmlOut != null )
				vrmlOut.drawRay(ray);
			
			List<Intersection> hits = ray.getIntersections(imagePlane); 
					
			if(hits.size() > 0){
				if(vrmlOut != null){
					vrmlOut.drawRay(ray);
				}
				
				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 thetaBtA = (Pol.psi(Eproj[0]) + 2*Math.PI) % Math.PI;
				double thetaBtB = (Pol.psi(Eproj[1]) + 2*Math.PI) % Math.PI;
				
				theta[iF] = Pol.psi(Eproj[0]);				
				
				System.out.println("finalPol . plateUp = " + theta[iF] * 180 / Math.PI + "°");
				polHit.incidentRay.rotatePolRefFrame(globaUp);
				
				System.out.println("finalPol . globalUp = " + Pol.psi(polHit.incidentRay.E1[0]) * 180 / Math.PI + "°"); 
				
				
			}else{
				//no hits, it can't see this point
				return new double[]{ Double.NaN, Double.NaN };
			}
		}
		
		if(FastMath.abs((theta[2] + theta[0])/2 - theta[1]) > 0.0001){
			throw new RuntimeException("Theta response to Bt is non-linear:" +
					"0 T --> " + theta[0] * 180 / Math.PI +
					dBt + " T --> " + theta[0] * 180 / Math.PI +
					(2*dBt) + " T --> " + theta[0] * 180 / Math.PI);
		}
		
		return new double[]{ theta[0], (theta[1] - theta[0])/dBt };
		
	}
}
