package seed.minerva.apps.imse;

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

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

import descriptors.gmds.GMDSSignalDesc;

import mds.GMDSFetcher;

import algorithmrepository.Algorithms;
import binaryMatrixFile.BinaryMatrixWriter;
import oneLiners.OneLiners;
import otherSupport.ColorMaps;
import otherSupport.RandomManager;
import otherSupport.SettingsManager;
import otherSupport.StatusOutput;
import seed.minerva.MinervaOpticsSettings;
import seed.minerva.aug.mse.AugMSESystem;
import seed.minerva.aug.mse.AugMseFaroData;
import seed.minerva.imse.IMSEOptics135_50;
import seed.minerva.imse.IMSEOptics135_50_rescaled;
import seed.minerva.optics.Util;
import seed.minerva.optics.collection.IntensityInfo;
import seed.minerva.optics.collection.PlaneAngleInfo;
import seed.minerva.optics.drawing.SVGCylindricalProjection;
import seed.minerva.optics.drawing.SVGRayDrawing;
import seed.minerva.optics.drawing.VRMLDrawer;
import seed.minerva.optics.interfaces.IsoIsoInterface;
import seed.minerva.optics.interfaces.IsoIsoStdFresnel;
import seed.minerva.optics.interfaces.NullInterface;
import seed.minerva.optics.interfaces.Reflector;
import seed.minerva.optics.optics.Box;
import seed.minerva.optics.pointSpread.PointSpreadFunction;
import seed.minerva.optics.surfaces.Plane;
import seed.minerva.optics.surfaces.Square;
import seed.minerva.optics.tracer.Tracer;
import seed.minerva.optics.types.Element;
import seed.minerva.optics.types.Intersection;
import seed.minerva.optics.types.Optic;
import seed.minerva.optics.types.Pol;
import seed.minerva.optics.types.RaySegment;
import seed.minerva.optics.types.Surface;
import signals.gmds.GMDSSignal;

/** Calculate the final polarisation offset for VxBphi and r'o'c with VxBz
 * for the nearest point to beam axis for a low-res grid
 * of the FeatureTransform.
 * 
 * Hopefully, that can 
 * @author minerva3
 *
 */
public class WhichWayIsUpTransform {
		
	private final static String outPath = MinervaOpticsSettings.getAppsOutputPath() + "/rayTracing/augImse/whichWayIsUpGrid/";
	private final static int nRaysDraw = 1;
	//*/
	
	/** 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 */
	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 = 10, nGridY = 10;
	
	public static void main(String[] args) { (new WhichWayIsUpTransform()).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(1);
		}
		
		//fit 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();
		
		double losVecs[][][] = xform.calcPixelVectors(nGridX, nGridY);
		
		double psi0[][][] = new double[4][nGridY][nGridX];
		double dPsiDPitch[][][] = new double[4][nGridY][nGridX];
		
		StatusOutput stat = new StatusOutput(WhichWayIsUpTransform.class, 4*nGridX*nGridY);
		for(int iB=0; iB < 4; iB++){
			for(int iY=0; iY < nGridY; iY++){				
				for(int iX=0; iX < nGridX; iX++){
					
					double s = Algorithms.pointOnLineNearestAnotherLine(targetPos, losVecs[iY][iX], sys.nbiStartAll[iB], sys.nbiUnitAll[iB]);
					double startPos[] = Util.plus(targetPos, Util.mul(losVecs[iY][iX], s));
							
					double ret[] = getFinalAngles(iB, startPos, 6.0 * Math.PI / 180);
					psi0[iB][iY][iX] = ret[0];
					dPsiDPitch[iB][iY][iX] = ret[1];
					
					stat.doStatus((iB*nGridY + iY)*nGridX + iX);
				}
				
			}
		}
		stat.done();
		
		if(vrmlOut != null){
			vrmlOut.drawOptic(sys);
			vrmlOut.addVRML("}"); //end of rotate/transform
			vrmlOut.destroy();
		}
		
		double x[] = OneLiners.linSpace(0.0, 1.1, nGridX);
		double y[] = OneLiners.linSpace(0.0, 1.1, nGridY);
		gmds.writeToCache(new GMDSSignal(new GMDSSignalDesc(pulse, "AUG", "whichWayIsUp/psi0"), psi0, x, y));
		gmds.writeToCache(new GMDSSignal(new GMDSSignalDesc(pulse, "AUG", "whichWayIsUp/dPsiDPitch"), dPsiDPitch, x, y));
		
	}
	
	/** returns psi(pitch=0) and dPsi/dPitch */
	private double[] getFinalAngles(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);
		
		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 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 };
		}
		
	}
}
