package fusionOptics.optics;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import fusionOptics.interfaces.IsoIsoStdFresnel;
import fusionOptics.surfaces.Dish;
import fusionOptics.types.Interface;
import fusionOptics.types.Material;
import fusionOptics.types.Medium;
import fusionOptics.types.Optic;
import fusionOptics.types.Surface;

import algorithmrepository.exceptions.NotImplementedException;

/** Two equally curved spherical surfaces 
 * 
 * @author oliford
 */
public class SimpleDoubleConvexLens extends Optic {

	private double centre[];
	private double radiusOfCurv;
	private Dish frontSurface, backSurface;
	private Medium lensMedium;
	
	/** Creates a lens with the given focal length for the given wavelength at room temperature */
	public SimpleDoubleConvexLens(String name, double centre[], double normal[], double radius, Medium lensMedium, Interface iface, double focalLength, double wavelength) {
		super(name);
		
		double n = lensMedium.getMaterial().getRefractiveIndex(0, wavelength, 300);
		
		double R = focalLengthToRadCurv(focalLength, radius, n);

		init(centre,  normal, R, radius, lensMedium, iface);
	}
	
	public final static double focalLengthToRadCurv(double f, double lensRadius, double n){
		double R = 2*(n-1)*f;
		for(int i=0 ; i < 10; i++)
			R = 1 / ( 1/(2*(n-1)*f) - (n-1)*lensRadius*lensRadius/(2*n*R*R*R));
		return R;
	}
	
	public SimpleDoubleConvexLens(String name, double centre[], double normal[], double radiusOfCurvature, double radius, Medium lensMedium, Interface iface) {
		super(name);

		init(centre,  normal, radiusOfCurvature, radius, lensMedium, iface);
	}
	
	private void init(double centre[], double normal[], double radiusOfCurvature, double radius, Medium lensMedium, Interface iface) {
		this.radiusOfCurv = radiusOfCurvature;
		this.centre = centre; 
		
		double halfWidth;
		if(radiusOfCurvature > 0){
			halfWidth = radiusOfCurv - Math.sqrt(radiusOfCurv*radiusOfCurv - radius*radius);
		}else{
			halfWidth = -radius / 20;
			radiusOfCurv = - radiusOfCurv;
		}
		init(centre, normal, radiusOfCurvature, radius, halfWidth,lensMedium, iface);
	}
		
	public SimpleDoubleConvexLens(String name, double centre[], double normal[], double radiusOfCurvature, double radius, double halfWidth, Medium lensMedium, Interface iface) {
		super(name);
		this.radiusOfCurv = radiusOfCurvature;
		this.centre = centre; 
		
		init(centre,  normal, radiusOfCurvature, radius, halfWidth, lensMedium, iface);
	}
	
	private void init(double centre[], double normal[], double radiusOfCurvature, double radius, double halfWidth, Medium lensMedium, Interface iface) {
		this.lensMedium = lensMedium;
		
		double frontCentre[] = new double[3];
		double backCentre[] = new double[3];
		double frontNormal[] = new double[3];
		
		//dishes have their normal facing toward the centre of their radius of curvature 
		for(int i=0;i<3;i++){
			frontCentre[i] = centre[i] + halfWidth * normal[i];
			backCentre[i] = centre[i] - halfWidth * normal[i];
			frontNormal[i] = -normal[i];
		}
		
		if(halfWidth < 0){
			backSurface = new Dish(getName() + "-back", frontCentre, frontNormal, radiusOfCurv, radius, null, lensMedium, iface);
			frontSurface = new Dish(getName() + "-front", backCentre, normal, radiusOfCurv, radius, null, lensMedium, iface);
		}else{
			frontSurface = new Dish(getName() + "-front", frontCentre, frontNormal, radiusOfCurv, radius, lensMedium, null, iface);
			backSurface = new Dish(getName() + "-back", backCentre, normal, radiusOfCurv, radius, lensMedium, null, iface);
			
		}

		addElement(frontSurface);
		addElement(backSurface);
				
	}
		
	public static double thickLensFocalLength(double radius1, double radius2, double thickness, double n) {
		return 1.0 / ((n-1.0)*( 1/radius1 - 1/radius2 + (n-1)*thickness/(n*radius1*radius2) ));
	}

	public void setFocalLength(double f, double wavelength) {
		
		double n= lensMedium.getMaterial().getRefractiveIndex(0, wavelength, 300);
		double lensRadius = frontSurface.getDishDiameter()/2;
		
		radiusOfCurv = focalLengthToRadCurv(f, lensRadius, n);
		
		frontSurface.setRadiusOfCurv(radiusOfCurv);
		backSurface.setRadiusOfCurv(radiusOfCurv);
		
	}
	
}
