/* ----------------------------------------
 * Origin  : GFI INFORMATIQUE 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 * Version : 05/05/2021 : LBE
 * DM-ID : Redmine#7059 : 05/05/2021 : n point instead of 3 for trajectories drawing
 * VERSION : V2.13
 * FA-ID : redmine#12154 : 16/09/2025 : KHK : Fix bug on SC position
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools.jmap;

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

import fr.tlse.irap.pt.client.controller.common.SpaceElementCollection;
import fr.tlse.irap.pt.client.controller.tools.common.FXImageHelper;
import fr.tlse.irap.fit.FitPoint;
import fr.tlse.irap.map.JMap;
import fr.tlse.irap.map.JMapHeader;
import fr.tlse.irap.pos.Position;
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 java.util.GregorianCalendar;
import javafx.scene.Node;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.QuadCurveTo;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JMapHelper {

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

    
    //private static final Logger log = LoggerFactory.getLogger(JMapHelper.class);
    /**
     * Create list of Space Elements label nodes
     *
     * @return List<Node>
     */
    public static List<Node> createSpaceElementsLabels() {
        List<Node> nodes = new ArrayList<Node>();
        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());
        }

        Path node = new Path();
        MoveTo moveTo;
        QuadCurveTo quadCurveTo;

        moveTo = new MoveTo(0, 0);
        node.getElements().add(moveTo);
        if (spaceElement instanceof Probe) {
            quadCurveTo = new QuadCurveTo(1, 0, 1, 0);
            node.getElements().add(quadCurveTo);
            moveTo = new MoveTo(5, 0);
            node.getElements().add(moveTo);
            quadCurveTo = new QuadCurveTo(6, 0, 6, 0);
            node.getElements().add(quadCurveTo);
            moveTo = new MoveTo(10, 0);
            node.getElements().add(moveTo);
            quadCurveTo = new QuadCurveTo(11, 0, 11, 0);
            node.getElements().add(quadCurveTo);
            moveTo = new MoveTo(15, 0);
            node.getElements().add(moveTo);
            quadCurveTo = new QuadCurveTo(16, 0, 16, 0);
            node.getElements().add(quadCurveTo);
        } else if (spaceElement instanceof Planet) {
            quadCurveTo = new QuadCurveTo(5, 0, 5, 0);
            node.getElements().add(quadCurveTo);
            moveTo = new MoveTo(10, 0);
            node.getElements().add(moveTo);
            quadCurveTo = new QuadCurveTo(15, 0, 15, 0);
            node.getElements().add(quadCurveTo);
        } else if (spaceElement instanceof Comet) {
            quadCurveTo = new QuadCurveTo(5, 0, 5, 0);
            node.getElements().add(quadCurveTo);
            moveTo = new MoveTo(10, 0);
            node.getElements().add(moveTo);
            quadCurveTo = new QuadCurveTo(15, 0, 15, 0);
            node.getElements().add(quadCurveTo);
        }
        node.setFill(c);
        node.setStroke(c);
        node.setId(spaceElement.getName() + spaceElement.getClass().getSimpleName());
        node.setUserData(null);

        return node;
    }

    /**
     * Create node + label
     *
     * @param spaceElement
     * @return
     */
    private 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;
        }
    }

    /**
     *
     * @param nodes
     * @param id
     * @return
     */
    public static XYChart.Series<Number, Number> findByName(XYChart<Number, Number> chart, String name) {
        for (XYChart.Series<Number, Number> series : chart.getData()) {
            if (series.getName() != null) {
                if (series.getName().contains(name)) {
                    return series;
                }
            }
        }
        return null;
    }

    /**
     *
     * @param id
     * @return
     */
    public static SpaceElement findById(String id) {
        for (Planet p : SpaceElementCollection.getPlanets()) {
            if (id.contains(p.getName())) {
                return p;
            }
        }
        //for (Probe p : SpaceElementCollection.getProbes()) {
        for (Probe p : SpaceElementCollection.getSpaceElementWrapper().getProbes()) {
            if (p.isDefined()) {
                if (id.contains(p.getName())) {
                    return p;
                }
            }
        }
        for (Comet c : SpaceElementCollection.getComets()) {
            if (id.contains(c.getName())) {
                return c;
            }
        }
        return null;
    }

    /**
     *
     * @param elongation
     * @return
     */
    public static boolean isValidElongation(Double elongation) {
        if (elongation != null) {
            return (elongation >= 0.0 && elongation <= 90.0);
        } else {
            return false;
        }

    }

    /**
     *
     * @param fitPoint
     * @return
     */
    public static boolean isValidFit(FitPoint fitPoint[]) {
        if (fitPoint == null) {
            return false;
        }
        for (int i = 0; i < fitPoint.length; i++) {
            
            if (fitPoint[i]!=null && !isValidElongation(fitPoint[i].getElongation())) {
                return false;
            }
        }
        return (fitPoint.length > 0);
    }

    /**
     * Create image from jMap
     *
     */
    public static javafx.scene.image.Image createImage(JMap jMap) throws IOException {
        return FXImageHelper.createImage(jMap.dataCompressed, jMap.getHeader().getWidth(), jMap.getHeader().getHeight(), null);
    }

    /**
     * Create an image between 2 points (zoom)
     *
     */
    public static javafx.scene.image.Image createImage(JMap jMap, FitPoint p1, FitPoint p2) throws IOException {
        byte[] pixelValues;
        int xSize = jMap.getHeader().getWidth();

        int[] xRange = computePixelXRange(p1.getTime(), p2.getTime(), jMap.getHeader());
        int witdh = xRange[1] - xRange[0];

        int[] yRange;
        if (p2.getElongation() > p1.getElongation()) {
            yRange = computePixelYRange(p1.getElongation(), p2.getElongation(), jMap.getHeader());
        } else {
            yRange = computePixelYRange(p2.getElongation(), p1.getElongation(), jMap.getHeader());
        }
        int height = yRange[1] - yRange[0];

        pixelValues = new byte[witdh * height];
        int k = 0;
        for (int j = yRange[0]; j < yRange[1]; j++) {
            for (int i = xRange[0]; i < xRange[1]; i++) {
                pixelValues[k] = jMap.dataCompressed[i + j * xSize];
                k++;
            }
        }

        return FXImageHelper.createImage(pixelValues, witdh, height, null);
    }

    /**
     * Define a range of indexes corresponding to the indexes of the byte array (pixels) of JMap
     * int[0] = index of date t1 int[1] = index of date t2
     */
    private static int[] computePixelXRange(Long t1, Long t2, JMapHeader jmapHeader) {
        long tStart = jmapHeader.getStartDate().getTime();
        long tEnd = jmapHeader.getEndDate().getTime();
        long period = (tEnd - tStart) / jmapHeader.getWidth();

        int indexStart = (int) ((t1 - tStart) / period);
        int indexEnd = (int) ((t2 - tStart) / period);
        //log.debug("Pixel X Range between  : " + indexStart + "_" + indexEnd);
        int[] range = {indexStart, indexEnd};

        return range;
    }

    /**
     * Define a range of indexes corresponding to the indexes of the byte array (pixels) of JMap
     * int[0] = index of date elong1 int[1] = index of date elong2
     */
    private static int[] computePixelYRange(double el1, double el2, JMapHeader jmapHeader) {
        double elMin = jmapHeader.getMinElongation();
        double elMax = jmapHeader.getMaxElongation();
        double period = (elMax - elMin) / jmapHeader.getHeight();

        int indexStart = jmapHeader.getHeight() - (int) ((el2 - elMin) / period);
        int indexEnd = jmapHeader.getHeight() - (int) ((el1 - elMin) / period);
        //log.debug("Pixel Y Range between  : " + indexStart + "_" + indexEnd);
        int[] range = {indexStart, indexEnd};

        return range;
    }

    /**
     * Call get position element web service
     *
     * @param spaceElement
     * @param t
     * @return
     */
    public static SpaceElement getPositionElement(SpaceElement spaceElement, Date t) {
        return ServiceFactory.getPositionService().getPositionElement(spaceElement, t);
    }

    /**
     * Call get positionsElement web service
     * Put values in positions list in spaceElement
     * @param spaceElement input used and returned in output.
     * @param times
     * @return updated spaceElement with positions
     */
    public static SpaceElement getPositionsElement(SpaceElement spaceElement, ArrayList<Date> times) {
        
//        ArrayList<XMLGregorianCalendar> xmlTimes = new ArrayList<XMLGregorianCalendar>();
//        
//        for (Date lDate : times) {
//            xmlTimes.add(date2Xgc(lDate));
//        }
        spaceElement.setTimeValues(times);
        

        //12154
        //spaceElement.setPositions(ServiceFactory.getPositionService().getPositionsElement(spaceElement).getPositions());
        List<Position> positions = ServiceFactory.getPositionService().getPositionsElement(spaceElement).getPositions();
        spaceElement.setPositions(positions);
        
        return spaceElement;
    }

    
    /**
     * Converts an XMLGregorianCalendar to java.util.Date
     * @param xGc Calendar to convert.
     * @return null if an error occurs
     */
    public static Date xGc2Date(XMLGregorianCalendar xGc) {
        if (xGc == null) {
            return null;
        } else {
            return xGc.toGregorianCalendar().getTime();
        }
    }
    
    /**
     * Converts java.util.Date to XMLGregorianCalendar Format
     * @param date Date to convert.
     * @return null if an error occurs
     */
    public static XMLGregorianCalendar date2Xgc(Date date) {
    	if (date == null) {
            return null;
        } else {
        	GregorianCalendar calendar = new GregorianCalendar();  
            calendar.setTimeInMillis(date.getTime());
            try {
                return DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);
            } catch (DatatypeConfigurationException ex) {
                return null;
            }
        }
    }
    
    
    
}
