package imseProc.proc.transform;

import java.util.ArrayList;
import mds.GMDSFetcher;

import signals.gmds.GMDSSignal;
import descriptors.gmds.GMDSSignalDesc;

/** Hold info about the 3D - image - beam coordinate transform.
 * 
 * The primary 2D-2D transform is from image coordinates to lat/lon angle away from 
 * the view position
 * 
 *  It's used by IMSEProc, minerva-imse, and the IMSE ray tracing apps. 
 * */
public abstract class FeatureTransform {

	private final static int SIG_IMG_X = 0; //coordinates of feature in image, (0.0 to 1.0) left to right, top to bottom
	private final static int SIG_IMG_Y = 1;
	private final static int SIG_3D_X = 2; // coordinates of feature in vessel (cart)
	private final static int SIG_3D_Y = 3;
	private final static int SIG_3D_Z = 4;
	private final static int SIG_ANG_LON = 5; //longitude angle (0 // to x, +90° // to y)
	private final static int SIG_ANG_LAT = 6; //latitude angle (+ve up from horizontal plane)
	private final static int SIG_EN = 7; //enable
	private final static int SIG_BI_R = 8; //beam intersection R/Z for selected beam
	private final static int SIG_BI_Z = 9;
	private final static int nSIGEntries = 10;
	
	
	public final static int EN_IGNORE = 0;
	public final static int EN_FIT = 1;
	public final static int EN_FIT_AND_MATRIX = 2;
	public final static int EN_BACK_TRANSLATE = 3;
	public final static int EN_VIEW = 4; // the effective camera/view 'from' position (3D only)
	
	public static class Point {
		
		/** Some identifying name */
		public String name;
		
		/** Position in image (user selected) in range 0.0 - 1.0 */
		public double imgX, imgY;
		
		/** 3D position in machine coords */
		public double x, y, z;
		
		/** Longitude and latitude angles */
		public double lat, lon;
		
		/** R,Z of nearest intersection with selected beam */
		public double R, Z;
		
		/** Enable status */
		public int enable = FeatureTransform.EN_IGNORE;
		
		public Point(String name) {
			this.name = name;
		}
		
		public Point(String name, double[] data) {
			this.name = name;
			this.imgX = data[SIG_IMG_X];
			this.imgY = data[SIG_IMG_Y];
			this.x = data[SIG_3D_X];
			this.y = data[SIG_3D_Y];
			this.z = data[SIG_3D_Z];
			this.lon = data[SIG_ANG_LON];
			this.lat = data[SIG_ANG_LAT];
			this.enable = (int)data[SIG_EN];
			this.R = data[SIG_BI_R];
			this.Z = data[SIG_BI_Z];
						
			if(imgX > 1.0){ //some old data was in pixels (320x240 fixed)
				imgX /= 320;
				imgY /= 240;
			}			
		}
		
		public double[] toArray() {			
			return new double[]{
					imgX, imgY,
					x, y, z,
					lon, lat,
					enable, 
					R, Z
			};
		}

		public double[] getPosXYZ(){ return new double[]{ x, y, z }; }

		public void setField(int col, double val) {
			switch(col){
				case SIG_IMG_X: this.imgX = val; break;
				case SIG_IMG_Y: this.imgY = val; break;
				case SIG_3D_X: this.x = val; break;
				case SIG_3D_Y: this.y = val; break;
				case SIG_3D_Z: this.z = val; break;
				case SIG_ANG_LON: this.lon = val; break;
				case SIG_ANG_LAT: this.lat = val; break;
				case SIG_EN: this.enable = (int)val; break;
				case SIG_BI_R: this.R = val; break;
				case SIG_BI_Z: this.Z = val; break;
			}
			
		}

		public boolean includeInFit() {
			return enable == FeatureTransform.EN_FIT || enable == FeatureTransform.EN_FIT_AND_MATRIX;
		}
		
	}
	
	protected ArrayList<Point> points = new ArrayList<FeatureTransform.Point>();
	
	public FeatureTransform(){
		
	}
		
	public ArrayList<Point> getPoints(){ return points; }	
	public Point getPointByName(String name){
		for(Point p : points){
			if(name.equals(p.name))
				return p;					
		}
		return null;
	}
	
	public double[] getViewPosition(){
		for(Point p : points){
			if(p.enable == EN_VIEW)
				return p.getPosXYZ();
		}
		return null;
	}
	

	public void saveToSignal(GMDSFetcher gmds, String experiment, int pulse){
		savePoints(gmds, experiment, pulse);
	}
		
	public void savePoints(GMDSFetcher gmds, String experiment, int pulse){
		String pointNames[] = new String[points.size()];
		double pointData[][] = new double[points.size()][];
		
		int i=0;
		for(Point p : points){
			pointNames[i] = p.name;
			pointData[i] = p.toArray();
			i++;
		}
		
		GMDSSignalDesc sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/pointNames");
		GMDSSignal sig = new GMDSSignal(sigDesc, pointNames);
		gmds.writeToCache(sig);

		sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/pointData");
		sig = new GMDSSignal(sigDesc, pointData);
		gmds.writeToCache(sig);
	
	}

	/** Construct frmo signal */
	public FeatureTransform(GMDSFetcher gmds, String experiment, int pulse) {
		loadPoints(gmds, experiment, pulse);
	}
	
	public void loadPoints(GMDSFetcher gmds, String experiment, int pulse){
		points.clear();
		
		//try reading from this specific pulse first
		String pointNames[] = null;
		double pointData[][] = null;
		
		GMDSSignal sig;
		GMDSSignalDesc sigDesc;
			
		sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/pointData");		
		sig = (GMDSSignal)gmds.getSig(sigDesc);
		pointData = (double[][])sig.get2DData();
			
		try{
			sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/pointNames");		
			sig = (GMDSSignal)gmds.getSig(sigDesc);
			pointNames = (String[])sig.get1DData();
				
		}catch(RuntimeException e){
			//hack for HDF5 string reading problem = regenerate now and save as netCDF from now on
			pointNames = new String[pointData.length];
			for(int i=0; i <pointNames.length; i++){
				pointNames[i] = "point_" + i;
			}				
		}
		
		for(int i=0; i < pointNames.length; i++){
			points.add(new Point(pointNames[i], pointData[i]));
		}
	}
	
	public abstract double[] latlonToXY(double lat, double Lon);
	public abstract double[] xyToLatLon(double x, double y);
	public abstract boolean isValid();
	
}
