/* ----------------------------------------
 * Origin  : GFI INFORMATIQUE 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 * Creation 
 * Version : 24/03/2016 : SCA : V2.2 (PSWS-H2020)
 * Version : 06/11/2017 : SCA : V2.2.1 
 *  Redmine #5403 - DMPT3 
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools.carrington;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.tlse.irap.pt.client.controller.common.SpaceElementCollection;
import fr.tlse.irap.pt.client.controller.tools.common.FXImageHelper;
import fr.tlse.irap.pt.client.controller.tools.common.ImageHelper;
import fr.tlse.irap.flare.Flare;
import fr.tlse.irap.map.CarringtonMap;
import fr.tlse.irap.pt.client.services.ServiceFactory;
import fr.tlse.irap.spaceElement.Comet;
import fr.tlse.irap.spaceElement.Planet;
import fr.tlse.irap.spaceElement.Probe;
import fr.tlse.irap.spaceElement.SpaceElement;
import fr.tlse.irap.spaceElement.Sun;
import fr.tlse.irap.utils.CoordConverter;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;

public class CarringtonHelper {

	protected static final Logger log = LoggerFactory.getLogger(CarringtonHelper.class);

	public static double imgWidth;
	public static double imgHeight;

	public static double longToX(Double longitude) {
		return longitude * imgWidth / 360.0;
	}

	public static double latToY(Double latitude) {
		return (90.0 - latitude) * imgHeight / 180.0;
	}

	public static Double xToLong(double x) {
		return x * 360.0 / imgWidth;
	}

	public static Double yToLat(double y) {
		return 90.0 - y * 180.0 / imgHeight;
	}

	public static Path createXAxis() {

		final Path xAxis = new Path();
		MoveTo moveTo;
		LineTo lineTo;
		for (int x = 0; x < imgWidth; x += 10) {
			moveTo = new MoveTo(x, imgHeight / 2);
			xAxis.getElements().add(moveTo);
			lineTo = new LineTo(x + 5, imgHeight / 2);
			xAxis.getElements().add(lineTo);
		}

		xAxis.setStroke(Color.ORANGE);

		return xAxis;
	}

	public static Rectangle createFlare(Flare flare) {

		double x = longToX(flare.getDisplayLongitude());
		double y = latToY(flare.getLatitude());

		final Rectangle node = new Rectangle(x - 5, y - 5, 10, 10);
		//log.debug("Create flare " + flare.getCategory());
		switch (flare.getCategory()) {
			case A:
				// Orange
				node.setFill(Color.web("#73e600"));				
				break;
			case B:
				// Yellow
				node.setFill(Color.web("#ffff00"));				
				break;
			case C:
				// Green yellow
				node.setFill(Color.web("#73e600"));
				break;
			case M:
				// Cyan
				node.setFill(Color.web("#00e6e6"));				
				break;
			case X:
				// Blue violet
				node.setFill(Color.web("#cc33ff"));				
				break;
			default:
				// Green yellow
				node.setFill(Color.web("#73e600"));				
				break;
		}
		//node.setOpacity(0.8);
		node.setId("FLARE" + flare.getCategory());

		Tooltip tooltip = new Tooltip("Cat=" + flare.getCategory()
				+ ", Intensity=" + flare.getIntensity().toString() + ", Time="
				+ flare.getEventTime());

		Tooltip.install(node, tooltip);

		node.setVisible(false);
		return node;
	}

	/**
	 * Create list of Space Elements label nodes
	 * 
	 * @return List<Node>
	 */
	public static List<Node> createSpaceElementsLabels() {
		List<Node> nodes = new ArrayList<Node>();
		if (SpaceElementCollection.getSpaceElementWrapper() != null) {
			for (Planet planet : SpaceElementCollection.getPlanets()) {
				nodes.add(createNodeLabelFromSpaceElement(planet));
			}
			for (Probe probe : SpaceElementCollection.getSpaceElementWrapper().getProbes()) {
				if (probe.isDefined()) {
					nodes.add(createNodeLabelFromSpaceElement(probe));
				}
			}
			for (Comet comet : SpaceElementCollection.getComets()) {
				nodes.add(createNodeLabelFromSpaceElement(comet));
			}
			nodes.removeAll(Collections.singleton(null));
		}
		return nodes;
	}

	/**
	 * Create node from Space Element
	 * 
	 * @param spaceElement
	 * @return
	 */
	public static Node createNodeFromSpaceElement(SpaceElement spaceElement) {
		Color c;
		if (spaceElement.getColor() == null) {
			c = Color.WHITE;
		} else {
			c = Color.web(spaceElement.getColor());
		}

		RadialGradient radialGradient = new RadialGradient(0, 0, 0.2, 0.3, 1,
				true, CycleMethod.NO_CYCLE, new Stop[] {
						new Stop(0, Color.rgb(250, 250, 255)), new Stop(1, c) });
		Node node = null;
		if (spaceElement instanceof Probe) {
			node = new Rectangle(12.0, 8.0, radialGradient);
		} else if (spaceElement instanceof Planet) {
			node = new Circle(10, radialGradient);
		} else if (spaceElement instanceof Comet) {
			node = new Circle(5, radialGradient);
		} else {
			return null;
		}
		node.setId(spaceElement.getName()
				+ spaceElement.getClass().getSimpleName());
		// create tool tip
		Tooltip tooltip = new Tooltip(spaceElement.getName());
		Tooltip.install(node, tooltip);

		return node;
	}

	/**
	 * Create node + label
	 * 
	 * @param spaceElement
	 * @return
	 */
	public static Node createNodeLabelFromSpaceElement(SpaceElement spaceElement) {

		Node node = createNodeFromSpaceElement(spaceElement);

		if (node != null) {
			Label spaceElementLabel = new Label(spaceElement.getName(), node);
			spaceElementLabel.setId(spaceElement.getName()
					+ spaceElement.getClass().getSimpleName());
			return spaceElementLabel;

		} else {
			return null;
		}
	}

	/**
	 * Compute Carrington coordinates of space element at a time
	 * 
	 * @param element
	 * @param time
	 * @param attitudeSun
	 *            : already computed from the time
	 */
	public static double[] computeCarringtonCoordinates(SpaceElement element, Date time, double[] attitudeSun) {

		if (attitudeSun != null) {
			/* Get position of point at time tSun */
			SpaceElement e = ServiceFactory.getPositionService().getPositionElement(element, time);
			if (e != null) {
				return CoordConverter.rectangularToCarrington(e.getX(), e.getY(), e.getZ(), attitudeSun);
			}
		}
		return null;
	}

	/**
	 * Compute Carrington coordinates of space element at a time
	 * 
	 * @param element
	 * @param time
	 * @param getPosition
	 *            : true if position has to be requested for the time t
	 */
	public static double[] computeCarringtonCoordinates(SpaceElement e, Date time, boolean getPosition) {
		double[] attitudeSun = getAttitudeSun(time);
		if (attitudeSun != null) {
			if (getPosition) {
				return computeCarringtonCoordinates(e, time, attitudeSun);
			} else {
				return CoordConverter.rectangularToCarrington(e.getX(), e.getY(), e.getZ(), attitudeSun);
			}
		}
		return null;
	}

	/**
	 * Translate HAE coordinates to Carrington coordinates at time tsun
	 */
	public static double[] HAEToCarringtonCoordinates(double[] HAELongLat, Date time) {
		double[] attitudeSun = getAttitudeSun(time);
		if (attitudeSun != null) {
			return CoordConverter.HAEToCarrington(HAELongLat, attitudeSun);
		}
		return null;
	}

	/**
	 * get Carrington coordinates of space element at time t
	 */
	public static double[] getCarringtonCoordinates(SpaceElement spaceElement) {
		double[] HAELongLat = CoordConverter.toSpheric(spaceElement.getX(), spaceElement.getY(), spaceElement.getZ());
		double[] attSun = SpaceElementCollection.getSpaceElementWrapper().getSun().getAttitude();

		return CoordConverter.HAEToCarrington(HAELongLat, attSun);
	}

	public static double[] getAttitudeSun(Date time) {
		double[] attitudeSun = null;
		Sun sun = new Sun();
		try {
			sun = (Sun) ServiceFactory.getPositionService().getPositionElement(sun, time);
			if (sun != null) {
				attitudeSun = sun.getAttitude();
			}
		} catch (Exception e) {
			log.warn("Sun attitude not found for : " + time);
		}
		return attitudeSun;
	}

	/**
	 * Create image from Carrington Map
	 */
	public static javafx.scene.image.Image createImage(CarringtonMap map) throws IOException {
		java.awt.Color[] colorMap = null;
		if (map.getHeader().getProbe().equals("MODEL")) {
			colorMap = ImageHelper.raimbowColorMap;
		}
		int contrast = 2;
		if (map.getHeader().getMeasurement().startsWith("M")) {
			if (map.getHeader().getProbe().startsWith("SOHO")) {
				contrast = 1;
			} else {
				contrast = 6;
			}
		}
		return FXImageHelper.createImage(map.fDataCompressed, map.getHeader().getWidth(), map.getHeader().getHeight(), contrast, colorMap);
	}

	/**
	 * Extract a pixel value in Carrington Map
	 */
	public static Float extractValue(CarringtonMap map, double x, double y) {
		if (map != null) {
			int xIndex = (int) ((x / imgWidth) * map.getHeader().getWidth());
			int yIndex = (int) ((y / imgHeight) * map.getHeader().getHeight());

			if (xIndex < map.getHeader().getWidth()
					&& yIndex < map.getHeader().getHeight()) {
				return map.fDataCompressed[xIndex + yIndex * map.getHeader().getWidth()];
			}
		}
		return null;
	}

	/**
	 * Get value unit
	 */
	public static String getValueUnit(CarringtonMap map) {
		if (map != null) {
			if (map.getHeader().getUnit() != null) {
				return map.getHeader().getUnit();
			} else {
				return "";
			}
		}
		return "";
	}

}
