/* ----------------------------------------
 * 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)
 *  Not automatically displayed warning of not existing Carrington view  
 *  New Catalogues
 * Version : 06/11/2017 : SCA : V2.2.1 
 *  Redmine #5403 - DMPT3 
 * Version : 09/04/2018 : SCA : V2.2.4
 *  Redmine DMPT4
 * Version : 29/06/2022 : LBE : V2.5
 * DM-ID : Redmine 9229: PFSS mode
 * Version : 30/11/2022 : LBE : V2.6
 * DM-ID : Redmine #l0481 : 30/11/2022 : HCS and PFSS FL added to carrington map
 * Version : 05/08/2024 : LBE : V2.11
 * DM-ID : Redmine#l0763 : 05/08/2024 : Grid field lines
 * VERSION : V2.12
 * DM-ID : redmine#11799 : 08/04/2025 : KHK : PFSS mode – Br Factor
 * Version : V2.13
 * DM-ID : redmine#12279 : 10/09/2025 : LBE : Add field lines in 3d panel
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools.carrington;

import java.util.Date;
import java.util.List;

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

import fr.tlse.irap.pt.client.component.CustomComponent;
import fr.tlse.irap.pt.client.component.ICustomComponent;
import fr.tlse.irap.pt.client.controller.common.FilterKeyEvent;
import fr.tlse.irap.pt.client.controller.common.FlareCollection;
import fr.tlse.irap.pt.client.controller.common.Formatter;
import fr.tlse.irap.pt.client.controller.common.ShockCollection;
import fr.tlse.irap.pt.client.controller.common.SpaceElementCollection;
import fr.tlse.irap.pt.client.controller.common.Style;
import fr.tlse.irap.pt.client.controller.common.constant.TOOLTIP;
import fr.tlse.irap.pt.client.controller.dialogbox.DialogBox;
import fr.tlse.irap.pt.client.controller.drawingPlane.DrawHelper;
import fr.tlse.irap.pt.client.controller.drawingPlane.DrawHelper.DrawingCmeType;
import fr.tlse.irap.pt.client.controller.tools.IToolController.JMapTool;
import fr.tlse.irap.pt.client.controller.tools.corotation.CorotationDashboardController;
import fr.tlse.irap.pt.client.controller.tools.sep.SepDashboardController;
import fr.tlse.irap.fieldLine.FieldLine;
import fr.tlse.irap.fieldLine.FieldLineWrapper;
import fr.tlse.irap.fieldLine.FieldPoint;
import fr.tlse.irap.fieldLine.Footpoint;
import fr.tlse.irap.fieldLine.Footpoints;
import fr.tlse.irap.flare.Flare;
import fr.tlse.irap.flare.Flare.FlareType;
import fr.tlse.irap.map.CarringtonMap;
import fr.tlse.irap.map.CarringtonMapType;
import fr.tlse.irap.map.HCSLine;
import fr.tlse.irap.pos.Position;
import fr.tlse.irap.pos.PositionWrapper;
import fr.tlse.irap.pt.client.services.ServiceFactory;
import fr.tlse.irap.shock.Shock;
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.SpaceElementWrapper;
import fr.tlse.irap.spaceElement.Sun;
import fr.tlse.irap.tool.corotation.Corotation;
import fr.tlse.irap.tool.corotation.CorotationEnd;
import fr.tlse.irap.tool.radial.CMEType;
import fr.tlse.irap.tool.radial.Radial;
import fr.tlse.irap.tool.radial.RadialEnd;
import fr.tlse.irap.tool.sep.Sep;
import fr.tlse.irap.tool.sep.SepEnd;
import fr.tlse.irap.utils.CoordConverter;
import java.util.ArrayList;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class CarringtonDashboardController extends GridPane implements ICustomComponent, ICarringtonMenu {

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

    private CarringtonModel carringtonModel = new CarringtonModel();
    private CarringtonCMEModel cmeModel = new CarringtonCMEModel();
    private CarringtonShockModel shockModel = new CarringtonShockModel();
    private ICarringtonMenu carringtonMenu;
    //private TimestampController timestampController = null;
    /**
     * If true, propagate through parker + PFSS
     */
    private boolean pfssMode = false;

    private BooleanProperty hasChanged = new SimpleBooleanProperty();
    private BooleanProperty hasFieldChanged = new SimpleBooleanProperty();
    private BooleanProperty hasFieldLinesChanged = new SimpleBooleanProperty();
    private BooleanProperty hasGridFieldLinesChanged = new SimpleBooleanProperty();
    private BooleanProperty hasGridFieldLinesRadiusChanged = new SimpleBooleanProperty();
    private StringProperty pfssGridAlt = new SimpleStringProperty("2.2");//overriden by value in fxml

    private ObjectProperty<Date> startDate;
    /**
     * End date from corotation, radial or sep models
     */
    private Date endDate = null;
    private Date currentDate = null;
    private SpaceElement startSource;
    private SpaceElement endSource;
    private double swSpeed;

    @FXML
    private CarringtonComponent carringtonComponent;
    @FXML
    private TextField longExtent;
    @FXML
    private TextField longSource;
    @FXML
    private Label timeLabel;
    @FXML
    private Slider timeSlider;
//    @FXML
//    private Label radiusLabel;
//    @FXML
//    private Slider radiusSlider;
    @FXML
    private CheckBox cbxPfssMode;
    @FXML
    private CheckBox cbxPfssGrid;
    @FXML
    private TextField tfPfssGridAlt;

    private CarringtonWorker worker = new CarringtonWorker();
    private JMapTool jMapTool;
    
    //11799  Get Br factor in PFSS mode (checkbox) and Start = SUN 
    private double brFactor_Corotation = 2; // brFactor is 2 by default
    private double brFactor_SEP = 2;
    private CorotationDashboardController corotationDashboardController;
    private SepDashboardController sepDashboardController;
   
    public CarringtonDashboardController() {
        super();
        CustomComponent.initCustomComponent(this);

        carringtonComponent.setModel(carringtonModel);
        carringtonComponent.setCmeModel(cmeModel);
        carringtonComponent.setShockModel(shockModel);

        // Create tooltips
        longExtent.setTooltip(new Tooltip(TOOLTIP.CME_EXTENT));
        longSource.setTooltip(new Tooltip(TOOLTIP.CME_FOOTPOINT));
        Style.setEditMode(tfPfssGridAlt, true);

        // Bind Carrington CME change to current change 
        carringtonComponent.getHasChanged().addListener(
                new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                log.debug("******** Carrington component changed " + newValue);
                if (newValue) {
                    // update model to compute extent
                    cmeModel.updateExtent();
                    // Notify change
                    getHasChanged().set(true);
                    getHasChanged().set(false);
                }
            }
        });

        // Bind probe visibiliity change to current change 
        carringtonComponent.carringtonElements.getVisibilityAllProbes().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                log.debug("******** All probes changed " + newValue);
                if (newValue) {
                    // Notify change
                    getHasFieldLinesChanged().set(true);
                    getHasFieldLinesChanged().set(false);
                }
            }
        });
        // Bind probe visibiliity change to current change 
        carringtonComponent.carringtonElements.getVisibilityProbe().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                log.debug("******** Single probe changed " + newValue);
                if (newValue) {                    
                    // Notify change
                    getHasFieldLinesChanged().set(true);
                    getHasFieldLinesChanged().set(false);
                }
            }
        });

        cmeModel.getLongExtent().bindBidirectional(longExtent.textProperty());
        cmeModel.getLongSource().bindBidirectional(longSource.textProperty());
        pfssGridAlt.bindBidirectional(tfPfssGridAlt.textProperty());

        longExtent.addEventFilter(KeyEvent.ANY, new FilterKeyEvent(longExtent, 1, 359, Formatter.getFormatter(), hasFieldChanged, longExtent.focusedProperty()));
        longSource.addEventFilter(KeyEvent.ANY, new FilterKeyEvent(longSource, 0, 359, Formatter.getFormatter(), hasFieldChanged, longSource.focusedProperty()));
        tfPfssGridAlt.addEventFilter(KeyEvent.ANY, new FilterKeyEvent(tfPfssGridAlt, 1, 2.5, Formatter.getFormatter(), hasGridFieldLinesRadiusChanged, tfPfssGridAlt.focusedProperty()));

        hasFieldChanged.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if (!newValue) {
                    // Notify change
                    getHasChanged().set(true);
                    getHasChanged().set(false);
                }
            }
        });

        cbxPfssMode.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                //radiusSlider.setMin(1.0);
                //radiusSlider.setMax(2.5);
                pfssMode = !pfssMode;               
                updateFieldLinesFromVisibleNodes();
                  
                //11799
                log.debug("Listen event on cbxPfssMode");
                if(getCorotationDashboardController()!=null){
                    getCorotationDashboardController().updatingWithBrFactor();
                    //log.debug("--- brFactor for Corotation " + brFactor_Corotation);                   
                }
                               
                if (getSepDashboardController()!= null) {
                    getSepDashboardController().updatingWithBrFactor();
                    //log.debug("--- brFactor SEP " + brFactor_SEP);
                }
            }
        });

        cbxPfssGrid.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if (newValue && (carringtonComponent.getGridFieldLines().getFieldLines().isEmpty() //if no current lines
                        || carringtonComponent.getGridFieldLines().getStartRadius() != Float.parseFloat(pfssGridAlt.getValue()) //if start point radius has changed
                        )) {
                    //compute all computed carrington element
                    worker.updateGridFieldLines("PFSS", "BEST_OF", 20, 20, Float.parseFloat(pfssGridAlt.getValue()));
                } else {
                    carringtonComponent.getGridFieldLines().show(currentDate, newValue);
                    tfPfssGridAlt.setDisable(!newValue);
                }
            }
        });
        hasGridFieldLinesRadiusChanged.addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                //log.debug("************RadiusFL changed !!!!"+newValue);
                if (cbxPfssGrid.isSelected() && !newValue
                        && carringtonComponent.getGridFieldLines().getStartRadius() != Float.parseFloat(pfssGridAlt.getValue()) ) {//if start point radius has changed
                    //recompute all computed carrington element
                    worker.updateGridFieldLines("PFSS", "BEST_OF", 20, 20, Float.parseFloat(pfssGridAlt.getValue()));
                }
                
            }
        });

        timeSlider.valueProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observedNumber, Number oldValue, Number newValue) {
                shockModel.updateViewWith(newValue.intValue());
                // update date label
                try {
                    timeLabel.setText(Formatter.getSdf().format(shockModel.getShockDate(newValue.intValue())));
                } catch (Exception e) {
                    timeLabel.setText("");
                }
            }
        });

//        radiusSlider.valueProperty().addListener(new ChangeListener<Number>() {
//            @Override
//            public void changed(ObservableValue<? extends Number> observedNumber, Number oldValue, Number newValue) {
//                double dVal = newValue.doubleValue();
//                if (dVal > 1 && dVal < 2.5) {
//                    if (dVal > 1.75) {
//                        radiusSlider.setValue(2.5);
//                    } else {
//                        radiusSlider.setValue(1);
//                    }
//                } else if (dVal != cmeModel.getFpRadius().get()) {
//                    // update and draw CME model with new radius
//                    log.debug("update CME model view with new Radius : " + dVal);
//                    cmeModel.updateViewWith(dVal);
//                }
//            }
//        });
    }

    /**
     * dynamic init
     */
    public void initComponent(DrawingCmeType cmeType) {
        carringtonComponent.initCme(cmeType);
        carringtonMenu = this;
        carringtonComponent.loadMenu(carringtonMenu, startSource);
        //timestampController = pTimestampController;

        SpaceElementCollection.getHasChanged().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                if (!newValue) {
                    carringtonComponent.loadMenu(carringtonMenu, startSource);
                    // init drawing elements from computed sun time
                    carringtonComponent.initCarringtonElements();
                }
            }
        });

        carringtonModel.setAltitudeChange(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                CarringtonMapType cmType = new CarringtonMapType();
                cmType.setProbe(carringtonModel.getObserver().get());
                cmType.setInstrument(carringtonModel.getInstrument().get());
                cmType.setMeasurement(carringtonModel.getMeasurement().get());
                cmType.getRadialDistances().add(newValue);
                worker.updateImage(cmType);
            }
        });

    }

    public void setStartDate(ObjectProperty<Date> selectedDate) {
        // bind carrington date to the selected start date
        startDate = selectedDate;
        currentDate = selectedDate.get();
        log.debug("setStartDate: " + startDate);
        startDate.addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                log.debug("startDate invalidated: date change");
                worker.updateImage();
                worker.updateFlares();
                worker.updateHCSLine();
                worker.updateShocks();
                timeLabel.setVisible(false);
                timeSlider.setVisible(false);
                updateFieldLinesFromVisibleNodes();
                
                
                //11799
//                if (getCorotationDashboardController() != null) {
//                    getCorotationDashboardController().updatingWithBrFactor();
//                    //log.debug("--- brFactor for Corotation " + brFactor_Corotation);                   
//                }
//
//                if (getSepDashboardController() != null) {
//                    getSepDashboardController().updatingWithBrFactor();
//                    //log.debug("--- brFactor SEP " + brFactor_SEP);
//                }
//                if (cbxPfssMode.isSelected()){
//                    cbxPfssMode.setSelected(false);
//                }
               

            }
        });
        // setting initialization
        log.debug("setStartDate: init image, flares, hcs, shocks");
        worker.updateImage();
        worker.updateFlares();
        worker.updateHCSLine();
        worker.updateShocks();
        updateFieldLinesFromVisibleNodes();
    }

    /**
     * Updates time slider dependant components as grid field lines
     *
     * @param pDate
     */
    public void setCurrentTime(Date pDate) {
        currentDate = pDate;
        if (cbxPfssGrid.selectedProperty().get()) {
            carringtonComponent.gridFieldLines.show(pDate, true);
        }
    }

    /**
     * Updates time slider dependant components as grid field lines
     *
     * @param pDate
     */
    public void setEndTime(Date pDate) {
        log.debug("set end time " + pDate);
        if(endDate!=null && endDate.equals(pDate)) return;
        endDate = pDate;
        //For now, unselect box, clear all
        carringtonComponent.getGridFieldLines().setFieldLines(null, null);
        cbxPfssGrid.setSelected(false);

    }

    /**
     * Configure view according to JMap tool
     *
     * @param jMapTool
     */
    public void setJMapTool(JMapTool jMapTool) {
        this.jMapTool = jMapTool;
        setSourceEditMode(startSource != null && (jMapTool == null || jMapTool.equals(JMapTool.INSITU)) && (startSource instanceof Sun));
        setExtentEditMode(true);
        FlareCollection.init();
    }

    private void setStartSource(SpaceElement startElement) {
        // set start source 
        startSource = startElement;
        carringtonModel.setStartLabel("START : " + startSource.getName());
        setSourceEditMode((jMapTool == null || jMapTool.equals(JMapTool.INSITU)) && (startSource instanceof Sun));
        // show default view (Start/End)
        carringtonComponent.showStartEnd(startSource, endSource, true);
        carringtonComponent.loadMenu(carringtonMenu, startSource);
    }

    public void setEndSource(ObjectProperty<SpaceElement> endSourceProperty, final BooleanProperty endVisibility) {
        // bind end property change
        endSourceProperty.addListener(new ChangeListener<SpaceElement>() {
            @Override
            public void changed(ObservableValue<? extends SpaceElement> observable, SpaceElement oldValue, SpaceElement newValue) {
                if (newValue != null) {
                    if (endVisibility.get()) {
                        endSource = newValue;
                    } else {
                        // not set the default end source
                        endSource = null;
                    }
                    carringtonComponent.showStartEnd(startSource, endSource, true);
                }
            }
        });
    }

    public BooleanProperty getHasChanged() {
        return hasChanged;
    }

    public void setHasChanged(BooleanProperty hasChangedProperty) {
        hasChanged = hasChangedProperty;
    }

    /**
     * Update Carrington CME from another model
     *
     * @param longAttitudeSun
     * @param sunDate
     */
    public void updateCMEWith(double pLongSource, double pLongExtent) {
//        radiusLabel.setVisible(false);
//        radiusSlider.setVisible(false);
        cmeModel.updateViewWith(pLongSource, pLongExtent);
    }

    public void updateFieldLinesFromVisibleNodes() {        
        List<CarringtonElement> lCes = new ArrayList<>();
        for (CarringtonElement lCe : carringtonComponent.carringtonElements.getCarringtonProbes()) {
            Node node = DrawHelper.findByName(carringtonComponent.carringtonElements.probeNodes.getChildren(), lCe.getName() + Probe.class.getSimpleName());
            if (node.isVisible()) {
                log.debug("Found visible node " + lCe.getName());
                lCes.add(lCe);
            }
        }
        if (pfssMode && !lCes.isEmpty()) {
            //compute all computed carrington element
            worker.updateFieldLines("PFSS", "BEST_OF", lCes);
            //field lines change event will be sent when worker has finished job
        } else {
            carringtonComponent.setFieldLines(null);
            if (!pfssMode) {
                for (CarringtonElement lCe : lCes) {
                    lCe.setComputed(false);
                    lCe.compute();
                    carringtonComponent.carringtonElements.refreshProbe(lCe);
                    //carringtonComponent.carringtonElements.updateProbes();
                }
            }
            getHasFieldLinesChanged().set(true);
            getHasFieldLinesChanged().set(false);
        }
    }

    /**
     * Update view from Radial
     *
     * @param radial
     */
    public void updateView(Radial radial, RadialEnd radialEnd) {
//        radiusLabel.setVisible(false);
//        radiusSlider.setVisible(false);
        setExtentEditMode(radial.getCmeType().equals(CMEType.FIXED_POINT));
        // update CME model from Radial 
        cmeModel.updateViewWith(radial.getLongSource(), radial.getExtentCME());
        // init drawing elements from computed sun time
        carringtonComponent.initCarringtonElements(radial);
        // set start source
        setStartSource(radial.getStartElement());
        swSpeed = radial.getSpeedSolarWind();
        //show/hide pfss checkbox
        cbxPfssMode.setVisible(false);
        setEndTime(radialEnd.getEndTime().getTime());
        updateFieldLinesFromVisibleNodes();
    }

    /**
     * Update view from Corotation
     *
     * @param corotationModel
     */
    public void updateView(Corotation corotation, CorotationEnd corotationEnd) {
//        radiusLabel.setVisible(false);
//        radiusSlider.setVisible(false);
        // update CME model from Radial
        
        //11799 brFactor       
        //cmeModel.updateViewWith(corotation.getLongSource(), corotation.getSpreadCIR() * 2);
        // (see corotation getBrFactor())       
        // In Carrington dashboard, 
        // In PFSS mode 
        //      Extent = Spread * 2  / brFactor
        //      Spread = Extent * brFactor / 2
        // by default 
        //      Extent = Spread * 2
        //      Spread = Extent / 2
        
        cmeModel.updateViewWith(corotation.getLongSource(), corotation.getSpreadCIR() * brFactor_Corotation );
             
        // init drawing elements 
        carringtonComponent.initCarringtonElements(corotation);
        // set start source
        setStartSource(corotation.getStartElement());
        swSpeed = corotation.getSpeedSolarWind();
        //show/hide pfss checkbox
        if (startSource == null || startSource.getId() == 10) {
            cbxPfssMode.setVisible(true);
        } else {
            cbxPfssMode.setVisible(false);
        }
        setEndTime(corotationEnd.getEndTime().getTime());
        updateFieldLinesFromVisibleNodes();

    }

    /**
     * Update view from Sep
     *
     * @param sep
     */
    public void updateView(Sep sep, SepEnd sepEnd) {
//        radiusLabel.setVisible(false);
//        radiusSlider.setVisible(false);
        // update CME model from Radial 
       
        //11799 brFactor       
        //cmeModel.updateViewWith(sep.getLongSource(), sep.getSpreadCIR() * 2);   
        // In Carrington dashboard, 
        // In PFSS mode, Extend = Spread / brFactor
        // by default Extend = Spread * 2
        // (see corotation getBrFactor())
        cmeModel.updateViewWith(sep.getLongSource(), sep.getSpreadCIR() *  brFactor_SEP);
        
        // init drawing elements 
        carringtonComponent.initCarringtonElements(sep);
        // set start source
        setStartSource(sep.getStartElement());
        swSpeed = sep.getSpeedSolarWind();
        //show/hide pfss checkbox
        if (startSource == null || startSource.getId() == 10) {
            cbxPfssMode.setVisible(true);
        } else {
            cbxPfssMode.setVisible(false);
        }
        setEndTime(sepEnd.getEndTime().getTime());
        updateFieldLinesFromVisibleNodes();
    }

    /**
     * Update Carrington CME model from fields properties and draw the CME
     */
    public void updateCME() {
//        radiusLabel.setVisible(false);
//        radiusSlider.setVisible(false);
        cmeModel.updateView();
    }

    private void setSourceEditMode(boolean editMode) {
        Style.setEditMode(longSource, editMode);
        carringtonComponent.setDisableSource(!editMode);
    }

    private void setExtentEditMode(boolean editMode) {
        Style.setEditMode(longExtent, editMode);
        carringtonComponent.setDisableExtent(!editMode);
    }

    public Double getLongSource() {
        try {
            return Double.parseDouble(cmeModel.getLongSource().getValue());
        } catch (Exception e) {
            return null;
        }
    }

    public Double getLongExtent() {
        try {
            return Double.parseDouble(cmeModel.getLongExtent().getValue());
        } catch (Exception e) {
            return null;
        }
    }

    public boolean isValidCME() {
        return cmeModel.isValid();
    }

    public CarringtonCMEModel getCarringtonCME() {
        return cmeModel;
    }

    public List<FieldLine> getFieldLines() {
        return carringtonComponent.getFieldLines().getFieldLines();
    }
    public CarringtonGridFieldLines getGridFieldLines() {
        return carringtonComponent.getGridFieldLines();
    }

    /**
     * ************************************************************************
     ********************** Contextual menu actions ***************************
     * ************************************************************************
     */
    @Override
    public void runCMEClickSource() {
        carringtonComponent.setCmeSourceInProgress(true);
    }

    @Override
    public void runCMEClickExtent() {
        carringtonComponent.setCmeRadiusInProgress(true);
    }

    @Override
    public void runShowFlares(FlareType ft) {
        carringtonComponent.getFlares().showHideBy(ft);
    }

    @Override
    public void runShowAllFlares() {
        carringtonComponent.getFlares().showHideAll();
    }

    @Override
    public void runShowShocks() {
        carringtonComponent.carringtonShocks.showHide();
        boolean visibility = carringtonComponent.carringtonShocks.isVisible();
        timeLabel.setVisible(visibility);
        timeSlider.setVisible(visibility);
    }

    @Override
    public void runShowHcs() {
        carringtonComponent.hcsLine.showHide();
        //boolean visibility = carringtonComponent.hcsLine.isVisible();
    }

    @Override
    public void runCarringtonMap(CarringtonMapType cmType) {
        log.debug("run Carrington map");
        worker.updateImage(cmType);
    }

    @Override
    public void runShowStartEnd() {
        carringtonComponent.showStartEnd(startSource, endSource);
    }

    @Override
    public void runReset() {
        carringtonComponent.showStartEnd(startSource, endSource, true);
    }

    @Override
    public void runShowAllPlanets() {
        carringtonComponent.showAllPlanets();
    }

    @Override
    public void runShowPlanet(SpaceElement spaceElement) {
        carringtonComponent.showPlanet((Planet) spaceElement);
    }

    @Override
    public void runShowAllProbes() {
        //if normal mode do as always, radial, coro or sep
        if (!pfssMode) {
            carringtonComponent.showAllProbes();
        } else {
            worker.updateFieldLines("PFSS", "BEST_OF", carringtonComponent.carringtonElements.getCarringtonProbes());
            carringtonComponent.carringtonElements.showHideProbes(!carringtonComponent.carringtonElements.isVisibleAllProbe());
        }
    }

    @Override
    public void runShowProbe(SpaceElement spaceElement) {
        //if normal mode do as always, radial, coro or sep
        if (!pfssMode) {
            carringtonComponent.showProbe((Probe) spaceElement);
        } else {
            List<CarringtonElement> lCes = new ArrayList<>();
            lCes.add(carringtonComponent.carringtonElements.findByName(carringtonComponent.carringtonElements.getCarringtonProbes(), spaceElement.getName()));
            worker.updateFieldLines("PFSS", "BEST_OF", lCes);

        }
    }

    @Override
    public void runShowAllComets() {
        carringtonComponent.showAllComets();
    }

    @Override
    public void runShowComet(SpaceElement spaceElement) {
        carringtonComponent.showComet((Comet) spaceElement);
    }

    private void hideShocks() {
        if (carringtonComponent.carringtonShocks.isVisible()) {
            carringtonComponent.carringtonShocks.showHide();
            timeLabel.setVisible(false);
            timeSlider.setVisible(false);
        }
    }

//    private void hideFieldLines() {
//        //timeSlider.setVisible(false);
//        //timeLabel.setVisible(false);
//        shockModel.clearView();
//
//    }

    /**
     * Workers sub-class - Upload data from server (Carrington map, flares,
     * shocks) and display on IHM
     */
    private class CarringtonWorker {

        CarringtonMap carringtonMap;
        HCSLine hcsLine;
        CarringtonMapType cmType = new CarringtonMapType();
        boolean actionCancelled;
        List<Flare> flares;
        List<Shock> shocks;
        List<FieldLine> fieldLines;
        List<Date> times;
        Footpoints footpoints;

        /**
         * Get Fits Carrington map from a new observer/measurement at the
         * current date. => Update image in the Carrington model
         */
        public void updateImage(CarringtonMapType cmType) {
            this.cmType = cmType;
            actionCancelled = true;
            update();
        }

        /**
         * Get Fits Carrington map from a new date. => Update image in the
         * Carrington model
         */
        public void updateImage() {
            //carringtonModel.reset();
            carringtonModel.reset(startDate.get());
            cmType.setProbe(carringtonModel.getObserver().get());
            cmType.setMeasurement(carringtonModel.getMeasurement().get());
            cmType.setInstrument(carringtonModel.getInstrument().get());
            cmType.getRadialDistances().clear();
            actionCancelled = false;
            update();
        }

        private void waitCursor() {
            carringtonComponent.setDisable(true);
            setCursor(Cursor.WAIT);
        }

        private void endCursor() {
            carringtonComponent.setDisable(false);
            setCursor(null);
        }

        private boolean setUpImage() {
            try {
                carringtonModel.setCarringtonImg(CarringtonHelper.createImage(carringtonMap));

            } catch (Exception e) {
                log.warn("Image not available - " + e.getMessage());
                DialogBox.warn("Image not available !");
                return false;
            }
            return true;
        }

        private void update() {
            final Task<Object> updateImageWorker = this.updateImageWorker();
            updateImageWorker.stateProperty().addListener(new ChangeListener<Worker.State>() {
                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        if (setUpImage()) {
                            carringtonComponent.resetAltCBox(carringtonModel.getAltitudeChange());
                            carringtonModel.setUpView(cmType);
                            carringtonComponent.initAltCBox(cmType, carringtonModel.getAltitudeChange());
                            carringtonModel.setRotationNumber("CR" + carringtonMap.getHeader().getRotationNumber());
                            carringtonComponent.setCarringtonMap(carringtonMap);
                        }
                        endCursor();

                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        log.warn("Carrington map not found - " + updateImageWorker.getException().getMessage());
                        if (actionCancelled) {
                            DialogBox.warn("Image not available !");
                            carringtonModel.update(cmType);
                            carringtonComponent.initAltCBox(cmType, carringtonModel.getAltitudeChange());
                        } else {
                            carringtonModel.setCarringtonImg(null);
                            carringtonComponent.resetAltCBox(carringtonModel.getAltitudeChange());
                        }
                        endCursor();
                    }
                }
            });
            new Thread(updateImageWorker).start();
            waitCursor();
        }

        private Task<Object> updateImageWorker() {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    carringtonMap = ServiceFactory.getCarringtonMapService().getCarringtonMap(startDate.getValue(), cmType);
                    return true;
                }
            };
        }

        private void updateHCSLine() {
            final Task<Object> updateWorker = this.updateHCSWorker();
            updateWorker.stateProperty().addListener(
                    new ChangeListener<Worker.State>() {
                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        // thread is done with success
                        carringtonComponent.setHCSLine(hcsLine);
                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        // thread is aborted
                    }
                    endCursor();
                }
            });
            new Thread(updateWorker).start();
            waitCursor();
        }

        private Task<Object> updateHCSWorker() {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    log.debug("************ Calling getCarringtonMapService().getHCSLine " + startDate.getValue());
                    hcsLine = ServiceFactory.getCarringtonMapService().getHCSLine(startDate.getValue());
                    return true;
                }
            };
        }

        private void updateFlares() {
            final Task<Object> updateFlaresWorker = this.updateFlaresWorker();
            updateFlaresWorker.stateProperty().addListener(
                    new ChangeListener<Worker.State>() {
                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        // thread is done with success
                        carringtonComponent.setFlares(flares);
                        endCursor();
                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        // thread is aborted
                        endCursor();
                    }
                }
            });
            new Thread(updateFlaresWorker).start();
            waitCursor();
        }

        private Task<Object> updateFlaresWorker() {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    // Flares WS call
                    flares = FlareCollection.getFlaresAround(startDate.getValue());
                    return true;
                }
            };
        }

        /**
         * Thread dedicated to update the shocks
         */
        private void updateShocks() {
            final Task<Object> updateShocksWorker = this.updateShocksWorker();
            updateShocksWorker.stateProperty().addListener(
                    new ChangeListener<Worker.State>() {
                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        // thread is done with success
                        carringtonComponent.carringtonShocks.setVisibility(false);
                        shockModel.clearView();
                        if (!shocks.isEmpty()) {
                            shockModel.setShocks(shocks);

                            shockModel.initView(startDate.get());
                            timeLabel.setText(Formatter.getSdf().format(startDate.get()));
                            timeSlider.setMin(0);
                            timeSlider.setMax(shockModel.getSteps() - 1);
                        } else {
                            timeSlider.setVisible(false);
                            timeLabel.setVisible(false);
                        }
                        endCursor();

                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        // thread is aborted
                        carringtonComponent.carringtonShocks.setVisibility(false);
                        timeSlider.setVisible(false);
                        timeLabel.setVisible(false);
                        shockModel.clearView();
                        endCursor();
                    }
                }
            });
            new Thread(updateShocksWorker).start();
            waitCursor();
        }

        private Task<Object> updateShocksWorker() {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    // Shocks WS call
                    shocks = ShockCollection.getShocksFrom(startDate.getValue());
                    return true;
                }
            };
        }

        /**
         * Compute pfss field lines for probes
         *
         * @param pModel Only "PFSS"
         * @param pSource Only "BEST_OF"
         */
        private void updateFieldLines(final String pModel, final String pSource, List<CarringtonElement> pCarringtonElements) {
            final Task<Object> updateFieldLinesWorker = this.updateFieldLinesWorker(pModel, pSource, pCarringtonElements);
            updateFieldLinesWorker.stateProperty().addListener(new ChangeListener<Worker.State>() {

                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    if (newState == Worker.State.SUCCEEDED) {
                        // thread is done with success
                        cmeModel.clearFieldLines();
                        carringtonComponent.setFieldLines(null);
                        if (footpoints != null && footpoints.getFootpoints() != null && !footpoints.getFootpoints().isEmpty()) {
                            if (footpoints.getFootpoints().get(0).getXYZ() == null) { //not used anymore
                                //cme model first item is start, add a second element with result
                                cmeModel.getFieldLines().add(footpoints);
//                                radiusLabel.setText("Radius (Rs)");
//                                radiusLabel.setVisible(true);
//                                radiusSlider.setVisible(true);
//                                radiusSlider.setShowTickLabels(true);
//                                radiusSlider.setShowTickMarks(true);
                                cmeModel.updateViewWith(1.0);
                            } else {
                                //probes pfss footprint mode
                                int lFpIdx = 0;
                                /*List<Node> lElements = new ArrayList<Node>();
                                    lElements.addAll(carringtonComponent.carringtonElements.probeNodes.getChildren());
                                    lElements.addAll(carringtonComponent.carringtonElements.planetNodes.getChildren());*/
                                //for (int i = 0; i < carringtonComponent.carringtonElements.getCarringtonProbes().size(); i++) {
                                for (int i = 0; i < pCarringtonElements.size(); i++) {
                                    CarringtonElement lCe = pCarringtonElements.get(i);
//                                    if (lProbeNode.isVisible()) {
                                    //CarringtonElement lCe = carringtonComponent.carringtonElements.findByName(carringtonComponent.carringtonElements.getCarringtonProbes(), lSe.getName());
                                    if (pfssMode) {
                                        //Set field line color based on carrington element color
                                        FieldLine lFl = fieldLines.get(lFpIdx);
                                        Footpoint lFp = footpoints.getFootpoints().get(lFpIdx++);
                                        log.info("***Change point for " + lCe.getName() + " " + lFp.getLon() + "/" + lFp.getLat());

                                        //lCe.getElement().setPosition(lFp.getXYZ()[0],lFp.getXYZ()[1],lFp.getXYZ()[2]);
                                        //lCe.compute();//recompute carrington position from HAE(eclipJ2000) coordinates
                                        lCe.setLongitude(lFp.getLon());
                                        lCe.setLatitude(lFp.getLat());
                                        //System.out.println("Set CarringTonElement FieldLine " + lCe.getName() + " " + lCe.getElement().getColor());
                                        lFl.setColor(lCe.getElement().getColor());
                                        lCe.setFieldLine(lFl);
                                        lCe.setComputed(true);

                                        Node node = DrawHelper.findByName(carringtonComponent.carringtonElements.probeNodes.getChildren(), lCe.getName() + Probe.class.getSimpleName());
                                        if (!node.isVisible()) {
                                            carringtonComponent.carringtonElements.showHide((Probe) lCe.getElement());
                                        }
                                        //show new element coords in graphic node coordinates
                                        carringtonComponent.carringtonElements.refreshProbe(lCe);
//                                        } else {
//                                            //should not be used. refresh probes before call to field lines
//                                            lCe.setComputed(false);
//                                            lCe.compute();//recompute carrington position from HAE(eclipJ2000) coordinates
//                                            log.info("***Changed back point for " + lCe.getName() + " " + lCe.getLongitude() + "/" + lCe.getLatitude());
//                                        }
                                    }
                                }
                                if (pfssMode) {
                                    //Add all probes field lines and redraw all
                                    List<FieldLine> lFls = new ArrayList<>();
                                    for (CarringtonElement lCe : carringtonComponent.carringtonElements.getCarringtonProbes()) {
                                        System.out.println("Adding Field line for update: " + lCe.getName());
                                        if (lCe.getFieldLine() != null) {
                                            lFls.add(lCe.getFieldLine());
                                        }
                                    }
                                    carringtonComponent.setFieldLines(lFls);
                                    hasFieldLinesChanged.set(true);
                                    hasFieldLinesChanged.set(false);

                                }
                            }
                        } else {
                            DialogBox.warn("No field lines found !");
                            System.out.println("Changed => no field lines mode pfss = " + pfssMode);
                        }
                        endCursor();

                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        // thread is aborted
                        endCursor();
                    }
                }
            });

            //TODO: test if there's something to compute
            new Thread(updateFieldLinesWorker).start();
            waitCursor();
        }

        private Task<Object> updateFieldLinesWorker(final String model, final String source, List<CarringtonElement> pCarringtonElements) {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    try {
                        // Field lines WS call
//                        if (source.equals("WSO")) {// not used anymore
//                            //for CME source
//                            footpoints = ServiceFactory.getFieldLineService().getEndFieldLineFP(
//                                    cmeModel.getFieldLines().get(0),
//                                    startDate.get(), model, source);
//                        } else {

                        log.debug("getting footpoints for " + source + " elements "
                                + carringtonComponent.carringtonElements.getCarringtonProbes().size()
                                + " 1=" + carringtonComponent.carringtonElements.getCarringtonProbes().get(0)
                                + " " + carringtonComponent.carringtonElements.probeNodes.getChildren().size());

                        if (pfssMode) {
                            //should be best of PFSS cubes
                            //compute footPoints with parker spiral/PFSS cube
                            //get input points from selected objects
                            SpaceElementWrapper lstStartPoints = new SpaceElementWrapper();
                            //List<CarringtonElement> lstElements = new ArrayList<>();
                            //lstElements.addAll(carringtonComponent.carringtonElements.getCarringtonProbes());
                            //lstElements.addAll(carringtonComponent.carringtonElements.getCarringtonPlanets());
                            //int lNbProbeItems = carringtonComponent.carringtonElements.getCarringtonProbes().size();
                            for (int i = 0; i < pCarringtonElements.size(); i++) {
                                CarringtonElement lCe = pCarringtonElements.get(i);
                                Position lP = lCe.getElement().getPosition();
                                log.debug("***Adding probe start point for " + lCe.getName() + " " + lP.getX() + "/" + lP.getY() + "/" + lP.getZ());
                                //Footpoint lFp = new Footpoint();
                                //lFp.setXYZ((float) lP.getX(), (float) lP.getY(), (float) lP.getZ());
                                //lstStartPoints.add(lFp);
                                lstStartPoints.getProbes().add((Probe) lCe.getElement());
//                                    } else {//planets, not used for now
//                                        if (carringtonComponent.carringtonElements.planetNodes.getChildren().get(i - lNbProbeItems).isVisible()) {
//                                            CarringtonElement lCe = lstElements.get(i - lNbProbeItems);
//                                            Position lP = lCe.getElement().getPosition();
//                                            log.info("***Adding planet start point for " + lCe.getName() + " " + lP.getX() + "/" + lP.getY() + "/" + lP.getZ());
//                                            lstStartPoints.getPlanets().add((Planet) lCe.getElement());
//                                        }
//                                    }
                            }
                            //log.debug("***Added " + lstStartPoints.getProbes().size() + " / " + lstStartPoints.getPlanets().size() + " start positions.");
                            //Footpoints lFps = new Footpoints(lstStartPoints);

                            SpaceElement lSunWithAtt = ServiceFactory.getPositionService().getPositionElement(startSource, startDate.get());
                            FieldLineWrapper lFls = ServiceFactory.getFieldLineService().getPSPFSSFieldLine(
                                    lstStartPoints, startDate.get(), swSpeed, lSunWithAtt.getAttitude());
                            footpoints = new Footpoints();
                            fieldLines = lFls.getFieldLines();
                            for (FieldLine lFl : lFls.getFieldLines()) {
                                FieldPoint lFp = lFl.getPoints().get(lFl.getPoints().size() - 1);
                                double[] lSphericCoords = CoordConverter.toSpheric(lFp.getX(), lFp.getY(), lFp.getZ());
                                //log.debug("**** Found foot point " + lSphericCoords[0] + " " + lSphericCoords[1] + " at " + lSphericCoords[2]);                               
                                Footpoint lFootp = new Footpoint((float) lSphericCoords[0], (float) lSphericCoords[1]);
                                lFootp.setXYZ((float) lFp.getX(), (float) lFp.getY(), (float) lFp.getZ());
                                footpoints.getFootpoints().add(lFootp);
                            }
                        } else {
                            //back to standard positions
                        }
//                        }

                    } catch (Exception e) {
                        log.warn("No field lines found ! " + e.getMessage());
                        log.debug("Worker => no field lines mode pfss = " + pfssMode);
                        e.printStackTrace();
                        footpoints = null;
                    }

                    return true;
                }
            };
        }

        /**
         * Compute pfss grid field lines
         *
         * @param pModel Only "PFSS"
         * @param pSource Only "BEST_OF"
         * @param pNbLong
         * @param pNbLat
         * @param pAlt
         */
        private void updateGridFieldLines(final String pModel, final String pSource, final int pNbLong, final int pNbLat, final float pAlt) {
            final Task<Object> updateGridFieldLinesWorker = this.updateGridFieldLinesWorker(pModel, pSource, pNbLong, pNbLat, pAlt);
            
            //show progress bar
            Stage lDialog = showGridCubeLoadingProgressBar(updateGridFieldLinesWorker, carringtonComponent.getScene().getX()+50, carringtonComponent.getScene().getY()-50);
            //lDialog.
            
            updateGridFieldLinesWorker.stateProperty().addListener(new ChangeListener<Worker.State>() {

                @Override
                public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                    
                    
                    
                    
                    if (newState == Worker.State.SUCCEEDED) {

                        log.debug("gridFieldline returned ok. Found "+fieldLines.size()+" lines.");
                        
                        //close progress dialog
                        lDialog.close();
                        
                        // thread is done with success
                        List<Date> lTimes = new ArrayList<>();
                        lTimes.addAll(times);
                        List<List<FieldLine>> lAllFls = new ArrayList<>();
                        int lGridSize = pNbLat * pNbLong;
                        List<FieldLine> lFls = new ArrayList<>();
                        for (int i = 0; i < fieldLines.size(); i++) {
                            if (i % lGridSize == 0 && i > 0) {
                                lAllFls.add(lFls);
                                log.debug("WS back gridsFL: add a set of " + lFls.size() + " lines");
                                lFls = new ArrayList<>();
                            }
                            lFls.add(fieldLines.get(i));
                        }
                        //add last one
                        lAllFls.add(lFls);

                        //set grid values for each 
                        carringtonComponent.setGridFieldLines(lTimes, lAllFls, pAlt);
                        carringtonComponent.getGridFieldLines().show(currentDate, true);
                        log.debug("*** Change hasGridFieldLinesChanged to true");
                        hasGridFieldLinesChanged.set(false);
                        hasGridFieldLinesChanged.set(true);
                        
                        
//                        if (fieldLines == null) { //Cannot be true
//                            DialogBox.warn("No field lines found !");
//                            log.debug("Changed => no field lines mode pfss = " + pfssMode);
//                        }
                        tfPfssGridAlt.setDisable(false);
                        endCursor();

                    } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                        // thread is aborted
                        endCursor();
                    }
                }
            });

            //TODO: test if there's something to compute
            new Thread(updateGridFieldLinesWorker).start();
            waitCursor();

        }

        private Task<Object> updateGridFieldLinesWorker(final String model, final String source, int pNbLong, int pNbLat, final double pAlt) {
            return new Task<Object>() {
                @Override
                //protected Object call() throws Exception {
                protected Object call() {
                    try {

                        log.info("getting pfss lines for " + pNbLong + " " + pNbLat + " End: " + endDate + " Alt: " + pAlt);
                        ArrayList<PositionWrapper> lStartPts = new ArrayList<>();
                        ArrayList<Date> lTimes = new ArrayList<>();
                        /**
                         * Compute list of dates to request. Take one each 24
                         * hours (should be best_of cubes frequency)
                         *
                         */
                        long lStartTime = startDate.get().getTime();
                        long lStopTime = lStartTime;
                        if (endDate != null) {
                            lStopTime = endDate.getTime();
                        }
                        long lTimeInc = 24 * 60 * 60 * 1000;
                        for (long lTime = lStartTime; lTime <= lStopTime; lTime += lTimeInc) {
                            lTimes.add(new Date(lTime));
                            log.debug("Adding time {}",lTime);
                        }
                        times = lTimes;

                        PositionWrapper lPw = new PositionWrapper();
                        lStartPts.add(lPw);//only one timestamp for now
                        for (int i = 0; i < 360; i += 360d / pNbLong) {
                            for (int j = -90; j < 90; j += 180d / pNbLat) {
                                double[] lPt = CoordConverter.toCartesian(i, j, pAlt);
                                lStartPts.get(0).getPositions().add(new Position(lPt[0], lPt[1], lPt[2]));
                            }
                        }

                        long lStartCompute = System.currentTimeMillis();
                        //get as many field lines grid from server as times in array
                            FieldLineWrapper lFls = ServiceFactory.getFieldLineService().getPFSSGridFieldLine(
                                    lPw, lTimes.toArray(new Date[lTimes.size()]));
                            log.debug("PFSS FL compute lasted " + (System.currentTimeMillis() - lStartCompute) / 1000d + "s");

                            fieldLines = lFls.getFieldLines();

                    } catch (Exception e) {
                        log.warn("No field lines found: {} ", e.getMessage(),e);
                        e.printStackTrace();

                    }

                    return true;
                }
            };
        }

    }

    /**
     * Show Dialog With progress bar
     */
    private Stage showGridCubeLoadingProgressBar(final Task<?> pTask, double pX, double pY) {
        Stage dialogStage = new Stage();
        dialogStage.initStyle(StageStyle.UTILITY);
        dialogStage.setResizable(false);
        dialogStage.initModality(Modality.APPLICATION_MODAL);

        // PROGRESS BAR
        ProgressBar lPb = new ProgressBar();
        lPb.setPrefWidth(200);

        lPb.setProgress(-1F);

        final HBox hb = new HBox();
        hb.setSpacing(5);
        hb.setAlignment(Pos.CENTER);
        hb.getChildren().addAll(lPb);

        Scene scene = new Scene(hb);
        dialogStage.setScene(scene);
        dialogStage.setTitle("Loading PFSS cubes");
        //dialogStage.setX(pX);
        //dialogStage.setY(pY);
        
        scene.getStylesheets().add(getClass().getResource("/fr/tlse/irap/pt/client/styles/StyleBtn.css").toString());


        Task<Void> lProgressBarTask = new Task<Void>() {
            @Override
            public Void call() throws InterruptedException {
                //compute number of days between start and stop
                long lStartTime = startDate.get().getTime();
                long lStopTime = lStartTime;
                if (endDate != null) {
                    lStopTime = endDate.getTime();
                }
                double lMsInADay = 24 * 60 * 60 * 1000;
                int lNbCube = (int)Math.ceil((lStopTime - lStartTime) / lMsInADay);
                long lCubeLoadingTime = 5 * 1000; //5s
                //System.out.println("*** Starting loop for "+lNbCube+" days.");
                for (int i = 0; i < lNbCube*100; i++) {
                    updateProgress(i, lNbCube*100);
                    //System.out.println("*** update progress "+i+"/"+lNbCube*100);
                    Thread.sleep(lCubeLoadingTime/100);
                }
                updateProgress(lNbCube, lNbCube);
                return null;
            }
        };

        lPb.progressProperty().bind(lProgressBarTask.progressProperty());
        dialogStage.show();

        lProgressBarTask.setOnSucceeded(event -> {
            //System.out.println("****** progressBar succeeded!");
            dialogStage.setTitle("Getting Lines ...");
        });
        Thread thread = new Thread(lProgressBarTask);
        thread.start();

        return dialogStage;
    }

    /**
     * @return the hasFieldLinesChanged
     */
    public BooleanProperty getHasFieldLinesChanged() {
        return hasFieldLinesChanged;
    }

    //11799
    public boolean isPfssMode() {
        return pfssMode;
    }

    public void setPfssMode(boolean pfssMode) {
        this.pfssMode = pfssMode;
    }

    public CheckBox getCbxPfssMode() {
        return cbxPfssMode;
    }
    
    
    public double getBrFactorCorotation() {
        return brFactor_Corotation;
    }

    public void setBrFactorCorotation(double brFactor) {
        this.brFactor_Corotation = brFactor;
    }

    public double getBrFactor_SEP() {
        return brFactor_SEP;
    }

    public void setBrFactor_SEP(double brFactor_SEP) {
        this.brFactor_SEP = brFactor_SEP;
    }

    public SepDashboardController getSepDashboardController() {
        return sepDashboardController;
    }

    public void setSepDashboardController(SepDashboardController sepDashboardController) {
        this.sepDashboardController = sepDashboardController;
    }

    public CorotationDashboardController getCorotationDashboardController() {
        return corotationDashboardController;
    }

    public void setCorotationDashboardController(CorotationDashboardController corotationDashboardController) {
        this.corotationDashboardController = corotationDashboardController;
    }

    /**
     * @return the hasGridFieldLinesChanged
     */
    public BooleanProperty getHasGridFieldLinesChanged() {
        return hasGridFieldLinesChanged;
    }

    /**
     * @param hasGridFieldLinesChanged the hasGridFieldLinesChanged to set
     */
    public void setHasGridFieldLinesChanged(BooleanProperty hasGridFieldLinesChanged) {
        this.hasGridFieldLinesChanged = hasGridFieldLinesChanged;
    }
 
    
}
