/* ----------------------------------------
 * Origin  : Akkodis
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 05/08/2024 : LBE : V2.11
 * DM-ID : Redmine#l0763 : 05/08/2024 : Creation
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools.carrington;

import java.util.List;

import fr.tlse.irap.pt.client.controller.common.Position2D;
import fr.tlse.irap.fieldLine.FieldLine;
import fr.tlse.irap.fieldLine.FieldPoint;
import fr.tlse.irap.utils.CoordConverter;
import java.util.ArrayList;
import java.util.Date;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Displays pfss field lines from a grid moving in time
 *
 * @author Akkodis
 */
public class CarringtonGridFieldLines {

    //Main display group node
    private Group nodes = new Group();

    //private BooleanProperty visibilityChanged = new SimpleBooleanProperty();
    protected static final Logger log = LoggerFactory.getLogger(CarringtonGridFieldLines.class);
    /**
     * List of grid
     */
    private List<List<Shape>> fieldLinesPathes;
    /**
     * List of field lines data
     */
    private List<List<FieldLine>> fieldLines;
    /**
     * List of times for each grid of field lines
     */
    private List<Date> flTimes;
    /**
     * Radius used for start points to compute grid. In Sun radius.
     */
    private float startRadius = 2.4f;
    private int currDateIdx = 0;

    public CarringtonGridFieldLines() {
        fieldLines = new ArrayList<>();
        flTimes = new ArrayList<>();
        fieldLinesPathes = createPathLines(fieldLines);
        // Listen for property visibility changed => swap visibility 
//        visibilityChanged.addListener(
//                new ChangeListener<Boolean>() {
//            @Override
//            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
//                if (newValue) {
//                    drawLines();
//                } else {
//                    clearLines();
//                }
//            }
//        });
    }

//    public void setColor(Color c) {
//        ((Path) nodes.getChildren().get(0)).setFill(c);
//        ((Path) nodes.getChildren().get(0)).setStroke(c);
//    }
    public List<Position2D> getPositions(FieldLine pFl) {
        List<Position2D> positions = new ArrayList<Position2D>();
        double x, y;

        if (!pFl.getPoints().isEmpty()) {
            for (FieldPoint lFp : pFl.getPoints()) {
                double[] lSphericCoords = CoordConverter.toSpheric(lFp.getX(), lFp.getY(), lFp.getZ());
                //log.info("**** Found foot point " + lSphericCoords[0] + " " + lSphericCoords[1] + " at " + lSphericCoords[2]);
                x = CarringtonHelper.longToX(lSphericCoords[0]);
                y = CarringtonHelper.latToY(lSphericCoords[1]);
                positions.add(new Position2D(x, y));
            }
        }
        return positions;
    }

//    protected void drawLines() {
//        //List<List<Position2D>> lPos = new ArrayList<List<Position2D>>();
//        for (int i = 0; i < fieldLinesPathes.size() && i < fieldLines.size(); i++) {
//            //log.debug("***Drawing field line "+i);
//            updateLine(i);
//        }
//        show(true);
//    }
    protected void clearLines() {
        fieldLinesPathes.clear();
        nodes.getChildren().clear();
//        for (List<Shape> lShapes : fieldLinesPathes) {
//            for (Shape lShape : lShapes) {
//                if (lShape instanceof Path) {
//                    ((Path)lShape).getElements().clear();
//                }
//                lShape.setVisible(true);
//            }
//        }
    }

    public void show(Date pDate, boolean pView) {
        int i = -1;
        if (pDate == null || flTimes == null || flTimes.isEmpty()) {
            return;
        }
        //System.out.println("GFLines: show " + pDate + " in " + flTimes.size() + " array.");
        //find date
        for (Date lDate : flTimes) {
            //System.out.println("Compare " + lDate + " -- " + pDate);
            if (pDate.before(lDate)) {
                //System.out.println("Show " + lDate + " for " + pDate);
                break;
            }
            i++;
        }
        if (i > -1) {
            show(i, pView);
        }

    }

    public void show(int pIdx, boolean pView) {
        //if(pView && pIdx==currDateIdx) return;
        int i = 0;
        currDateIdx = pIdx;
        for (List<Shape> lShapes : fieldLinesPathes) {
            for (Shape lShape : lShapes) {
                lShape.setVisible(pView && i == pIdx);
            }
            i++;
        }
    }

//    /**
//     * Get number of field lines for each time step.
//     * @return 
//     */
//    private int getStepNbLines() {
//        if(fieldLines!=null && !fieldLines.isEmpty()) {
//            return fieldLines.get(0).size();
//        } else {
//            return 0;
//        }
//    }
//    
//    public void hide() {
//        show(false);
//    }
//    public boolean isVisible() {
//        return hcs.isVisible();
//    }
    /**
     * field lines and redraw
     *
     * @param pLines new field lines
     */
    public void setFieldLines(List<Date> pTimes, List<List<FieldLine>> pLines) {
        fieldLines = pLines;
        flTimes = pTimes;
        if (fieldLines == null) {
            //Avoid null list, create empty list
            fieldLines = new ArrayList<>();
        }
        if (flTimes == null) {
            //Avoid null list, create empty list
            flTimes = new ArrayList<>();
        }
        log.debug("Setting fieldLines: " + fieldLines.size() + " times: " + flTimes.size());
        clearLines();
        fieldLinesPathes = createPathLines(fieldLines);
        //nodes.getChildren().addAll(fieldLinesPathes);
        for (List<Shape> lShapes : fieldLinesPathes) {
            nodes.getChildren().addAll(lShapes);
        }
//        if (pLines != null && !pLines.isEmpty()) {
//            drawLines();
//        } else {
//            //nodes.getChildren().clear();
//        }
    }

    /**
     * List of field lines data
     *
     * @return the fieldLines
     */
    public List<List<FieldLine>> getFieldLines() {
        return fieldLines;
    }

    public Group getNodes() {
        return nodes;
    }

    /**
     * Prepare the Field line draw objects properties. For each time, a list of
     * field line is displayable.
     */
    private List<List<Shape>> createPathLines(List<List<FieldLine>> pFLines) {
        List<List<Shape>> lAllPathes = new ArrayList<>();
        for (List<FieldLine> lFls : pFLines) {
            Color c = Color.RED;

            List<Shape> lFlPathes = new ArrayList<>();

            for (FieldLine lFl : lFls) {
                Path lPath = new Path();
                //lPath.setFill(Color.RED);
                lPath.setStroke(getFLColor(lFl));
                lPath.setStrokeWidth(1);
                lPath.setStrokeLineCap(StrokeLineCap.ROUND);
                //lPath.getStrokeDashArray().addAll(4d);
                lPath.setId("FLine");
                lPath.setVisible(false);
                List<Position2D> lPositions = getPositions(lFl);
                if (!lPositions.isEmpty()) {
                    Position2D lPos = lPositions.get(0);
                    Position2D lPosLast = lPos;
                    MoveTo moveTo = new MoveTo(lPos.getX(), lPos.getY());
                    lPath.getElements().add(moveTo);
                    for (Position2D lFp : lPositions) {
                        if (Math.abs(lFp.getX() - lPosLast.getX()) > 10) {
                            lPath.getElements().add(new MoveTo(lFp.getX(), lFp.getY()));
                        } else {
                            lPath.getElements().add(new LineTo(lFp.getX(), lFp.getY()));
                        }
                        lPosLast = lFp;
                    }
                }
                lFlPathes.add(lPath);
            }

            lAllPathes.add(lFlPathes);
        }
        return lAllPathes;
    }

    /**
     * Compute Closed/open north or open south field lines
     *
     * @param pFl Field line to analyse.
     * @return Black for closed line, red for north et blue for south.
     */
    public static Color getFLColor(FieldLine pFl) {
        Color lColor = Color.BLACK;
        if (pFl != null && !pFl.getPoints().isEmpty()) {
            double lFp1Length = pFl.getPoints().get(0).length();
            double lFp2Length = pFl.getPoints().get(pFl.getPoints().size() - 1).length();

//            String lPtsStr = "";
//            for(FieldPoint lFp : pFl.getPoints()) {
//                lPtsStr+=Formatter.toDecimal1D(lFp.length()/CoordConverter.rSUN)+" ";
//            }
//            System.out.println("Grid line FP lengthes = "+lPtsStr) ;
            //System.out.println("Grid line FP length = "+Formatter.toDecimal1D(lFp1Length/CoordConverter.rSUN) +" / "+Formatter.toDecimal1D(lFp2Length/CoordConverter.rSUN)) ;
            //if south open line, set it blue
            if (lFp1Length < CoordConverter.rSUN * 1.1 && lFp2Length > CoordConverter.rSUN * 2) {
                lColor = Color.RED;
            }
            //if north open line, set it red
            if (lFp2Length < CoordConverter.rSUN * 1.1 && lFp1Length > CoordConverter.rSUN * 2) {
                lColor = Color.BLUE;
            }
        }
        return lColor;
    }

    /**
     * Radius used for start points to compute grid. In Sun radius.
     *
     * @return the startRadius
     */
    public float getStartRadius() {
        return startRadius;
    }

    /**
     * Radius used for start points to compute grid. In Sun radius.
     *
     * @param startRadius the startRadius to set
     */
    public void setStartRadius(float startRadius) {
        this.startRadius = startRadius;
    }

    /**
     * List of times for each grid of field lines
     * @return the flTimes
     */
    public List<Date> getFlTimes() {
        return flTimes;
    }

    /**
     * List of times for each grid of field lines
     * @param flTimes the flTimes to set
     */
    public void setFlTimes(List<Date> flTimes) {
        this.flTimes = flTimes;
    }

}
