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 physical space coordinates on the CCD (in m) to lat/lon angle away from 
 * the view position. 
 * 
 * The physical CCD coordinates are generally relative to the center of the 'full frame' CCD.
 * The size of the CCD and the actual coverage of the CCD by the image depends on camera and it's settings (e.g. ROI)
 * 
 * It's used by IMSEProc, minerva-imse, and the IMSE ray tracing apps.
 * 
 * Conversion to 'beam plane' (R,Z) coordinates are not done here.
 */
public abstract class FeatureTransform {

	
	/** Range info for lat/lon (defines the LL-->XY interpolation grid) */	
	protected double minLon, maxLon, minLat, maxLat;

	/** Definition of the image in physical CCD space (relevant to a particular image, because of ROI, CCD info etc) 
	 * X0 is the left of the left most pixel. X1 is the right of the right most pixel. 
	 * Y0 is the top of the top most pixel. Y1 is the bottom of the bottom most pixel.  */
	protected double imgX0, imgY0, imgX1, imgY1;
	protected int imgWidthInPixels, imgHeightInPixels;
	
	protected ArrayList<TransformPoint> points = new ArrayList<TransformPoint>();
	
	public FeatureTransform(){
		
	}
		
	public ArrayList<TransformPoint> getPoints(){ return points; }	
	public TransformPoint getPointByName(String name){
		for(TransformPoint p : points){
			if(name.equals(p.name))
				return p;					
		}
		return null;
	}
	
	public double[] getViewPosition(){
		for(TransformPoint p : points){
			if(p.mode == TransformPoint.MODE_VIEW)
				return p.getPosXYZ();
		}
		return null;
	}
	

	public void saveToSignal(GMDSFetcher gmds, String experiment, int pulse){
		savePoints(gmds, experiment, pulse);
		saveImageXY(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(TransformPoint 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);
	
	}
	
	public void saveImageXY(GMDSFetcher gmds, String experiment, int pulse){
		GMDSSignalDesc sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/imageXY");
		GMDSSignal sig = new GMDSSignal(sigDesc, new double[]{ imgX0, imgY0, imgX1, imgY1, imgWidthInPixels, imgHeightInPixels });
		gmds.writeToCache(sig);
	}

	/** Construct frmo signal */
	public FeatureTransform(GMDSFetcher gmds, String experiment, int pulse) {
		loadPoints(gmds, experiment, pulse);
		loadImageXY(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 TransformPoint(pointNames[i], pointData[i]));
		}
	}
	
	public void loadImageXY(GMDSFetcher gmds, String experiment, int pulse){

		GMDSSignalDesc sigDesc = new GMDSSignalDesc(pulse, experiment, "Transform/imageXY");		
		GMDSSignal sig = (GMDSSignal)gmds.getSig(sigDesc);
		double xyData[] = (double[])sig.get1DData();
		this.imgX0 = xyData[0];
		this.imgY0 = xyData[1];
		this.imgX1 = xyData[2];
		this.imgY1 = xyData[3];
		this.imgWidthInPixels = (int)xyData[4];
		this.imgHeightInPixels = (int)xyData[5];
	}
		
	public abstract double[] latlonToXY(double lat, double Lon);
	public abstract double[] xyToLatLon(double x, double y);
	public abstract boolean isValid();
	
	public void setImageDimensions(double imgX0, double imgY0, double imgX1, double imgY1, int imgWidthInPixels, int imgHeightInPixels){
		this.imgX0 = imgX0;
		this.imgY0 = imgY0;
		this.imgX1 = imgX1;
		this.imgY1 = imgY1;
		this.imgWidthInPixels = imgWidthInPixels;
		this.imgHeightInPixels = imgHeightInPixels;
	}


	public final double getMinLon() { return minLon; }
	public final double getMaxLon() { return maxLon; }
	public final double getMinLat() { return minLat; }
	public final double getMaxLat() { return maxLat; }
	
	public double getImgX0() { return imgX0; }
	public double getImgX1() { return imgX1; }
	public double getImgY0() { return imgY0; }
	public double getImgY1() { return imgY1; }
	public double getImgPhysW() { return imgX1 - imgX0; }
	public double getImgPhysH() { return imgY1 - imgY0; }
	public int getImgPixelW() { return imgWidthInPixels; }
	public int getImgPixelH() { return imgHeightInPixels; }
	
}
