/* ----------------------------------------
 * Origin  : Akkodis 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 *  Move Radial End computation on server side (WS)  
 * Version : 25/11/2024 : LBE : V2.11
 * DM-ID : Redmine#l0763 : 25/11/2024 : PFSS grid lines
 * Version : V2.12
 * DM-ID : Redmine#11103 : 25/03/2025 : LBE : Radio spectro interface added
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools;

import java.util.Date;

import fr.tlse.irap.pt.client.component.CustomComponent;
import fr.tlse.irap.pt.client.component.ICustomComponent;
import fr.tlse.irap.pt.client.controller.dialogbox.DialogBox;
import fr.tlse.irap.pt.client.controller.eclipticPlane.EclipticPlaneController;
import fr.tlse.irap.pt.client.controller.timestamp.TimestampController;
import fr.tlse.irap.pt.client.controller.tools.catalog.RadialCatalogDialogBox;
import fr.tlse.irap.pt.client.controller.tools.externalLinks.AmdaDialogBox;
import fr.tlse.irap.pt.client.controller.tools.externalLinks.ApisDialogBox;
import fr.tlse.irap.pt.client.controller.tools.externalLinks.MedocDialogBox;
import fr.tlse.irap.pt.client.controller.tools.fitParameter.FitParameterComponent;
import fr.tlse.irap.pt.client.controller.tools.jmap.JMapRadialComponent;
import fr.tlse.irap.pt.client.controller.tools.radial.RadialDashboardController;
import fr.tlse.irap.pt.client.controller.tools.radio.RadioFitParameterComponent;
import fr.tlse.irap.pt.client.controller.tools.radio.RadioRadialComponent;
import fr.tlse.irap.pt.client.controller.tools.samp.JSampController;
import fr.tlse.irap.pt.client.controller.tools.timetable.TimeTableRadialController;
import fr.tlse.irap.spaceElement.SpaceElement;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.GridPane;

public class RadialController extends GridPane implements ICustomComponent, IToolController {

    private BooleanProperty hasChanged = new SimpleBooleanProperty();
    private ObjectProperty<JMapTool> jMapTool = new SimpleObjectProperty<JMapTool>();

    @FXML
    protected RadialController rootRadial;
    @FXML
    public RadialDashboardController radialDashboardComponent;
    @FXML
    protected JMapRadialComponent jMapRadialComponent;
    @FXML
    protected RadioRadialComponent radioComponent;
    @FXML
    protected TimeTableRadialController timeTableComponent;
    @FXML
    protected FitParameterComponent fitParameterComponent;
    @FXML
    protected RadioFitParameterComponent radioFitParameterComponent;
    @FXML
    protected Tab fitResultTab;
    @FXML
    protected TabPane radioTabPane;
    @FXML
    protected Tab radioFitResultTab;
    @FXML
    protected Tab timetableTab;
//    @FXML protected Button amdaBtn;
//    @FXML protected Button medocBtn;
//    @FXML protected Button apisBtn;
//    @FXML protected Button sampBtn;

    private boolean isInitialized;

    public RadialController() {
        super();
        CustomComponent.initCustomComponent(this);

        // listen for Radial Start changed
        radialDashboardComponent.getHasChanged().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                if (newValue) {
                    computeRadialDrag();
                }
            }
        });
        // listen for jMapTool changed
        jMapTool.addListener(new ChangeListener<JMapTool>() {
            @Override
            public void changed(ObservableValue<? extends JMapTool> observableObject, JMapTool oldValue, JMapTool newValue) {
                if (newValue != null) {
                    radialDashboardComponent.setJMapTool(newValue);
                    jMapRadialComponent.setJMapTool(newValue);
                    jMapRadialComponent.setFitResultTab(fitResultTab);
                    radioComponent.setJMapTool(newValue);
                    radioTabPane.getSelectionModel().selectFirst();
                    radioComponent.setFitResultTab(radioFitResultTab);
                }
            }
        });
        // listen for jMapRadialComponent changed
        jMapRadialComponent.getHasChanged().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                if (newValue) {
                    // update Radial from Jmap
                    jMapRadialComponent.updateRadial(radialDashboardComponent.radial);
                    // set LINKCAT mode
                    radialDashboardComponent.updateLinkcatMode();

                    if (jMapTool.get().equals(JMapTool.INSITU)) {
                        // update carrington
                        radialDashboardComponent.updateCarringtonFromRadial();
                        // update start fields and notify changed
                        radialDashboardComponent.updateStartView();
                    } else {
                        // Set InSitu mode to false => Radial is computed from J-map parameters (computed or selected)
                        radialDashboardComponent.radial.setInSitu(false);
                        // update start date
                        radialDashboardComponent.getStartDate().set(radialDashboardComponent.radial.getStartTime().getTime());
                    }
                }
            }
        });
        // listen for Radial End change
        this.getHasChanged().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observedBoolean, Boolean oldValue, Boolean newValue) {
                if (newValue) {
                    // compute radial propagation 
                    computeRadial();
                }
            }
        });

    }

    @Override
    public void initComponentWith(EclipticPlaneController eclipticPlaneComponent, TimestampController timestampComponent) {
        // Init dynamic part of components
        if (isInitialized) {
            // update only the active tool
            if (eclipticPlaneComponent.getTool() == Tool.RADIAL) {
                // refresh ecliptic from radial + Jmap
                radialDashboardComponent.updateEcliptic();
                radialDashboardComponent.updateResultTime();
                //radialDashboardComponent.updateCarringtonFromRadial();
                jMapRadialComponent.updateEclipticFOV();
            }
        } else {
            // first init
            isInitialized = true;
            // Init dynamic part of components
            radialDashboardComponent.initComponentWith(eclipticPlaneComponent, timestampComponent);
            jMapRadialComponent.initComponentWith(eclipticPlaneComponent, timestampComponent.selectedDateProperty());
            // Set start Source to controller which need it
            jMapRadialComponent.setStartSource(radialDashboardComponent.radialStartEndController.getStartModel().getStartSource());
            radioComponent.initWith(timestampComponent.selectedDateProperty(), radialDashboardComponent.radialStartEndController.getStartModel().getSpeed());
            //radioComponent.setStartDate(timestampComponent.selectedDateProperty());
            radioComponent.setFitParameterComponent(radioFitParameterComponent);
            
            // Listen for end source change
            radialDashboardComponent.radialStartEndController.getEndSource().addListener(new ChangeListener<SpaceElement>() {
                @Override
                public void changed(ObservableValue<? extends SpaceElement> observable, SpaceElement oldValue, SpaceElement newValue) {
                    if (newValue == null || (oldValue != null && oldValue.getName().equals(newValue.getName()))) {
                        return;
                    }
                    if (radialDashboardComponent.radialStartEndController.isEndVisible()) {
                        radialDashboardComponent.radial.setEndElement(newValue);
                    } else {
                        radialDashboardComponent.radial.setEndElement(null);
                    }
                    // notify for computation
                    getHasChanged().set(true);
                }
            });
            // bind fitParamComponent with JMap
            fitParameterComponent.setJMapRadialComponent(jMapRadialComponent);
        }
    }

    /**
     * Thread dedicated to Compute radial process
     */
    private void computeRadial() {
        final Task<Object> computeRadialWorker = computeRadialWorker();

        computeRadialWorker.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) {
                    radialDashboardComponent.endCursor();
                    // update all components from radial model
                    radialDashboardComponent.updateView();
                    jMapRadialComponent.updateView(
                            radialDashboardComponent.radialEnd,
                            radialDashboardComponent.radialStartEndController.isEndVisible());
                    radialDashboardComponent.updateEcliptic();

                    getHasChanged().set(false);

                } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                    // thread is failed or cancelled
                    getHasChanged().set(false);
                    radialDashboardComponent.endCursor();
                    String target = radialDashboardComponent.radialStartEndController.getEndSource().get().getName();
                    //radialDashboardComponent.resetEndSource();
                    radialDashboardComponent.radialStartEndController.resetEndSource();
                    DialogBox.warn("Position of " + target + " at start time has not be found.\nDefault End has been reset");
                    computeRadialWorker.getException().printStackTrace();
                }
            }

        });

        new Thread(computeRadialWorker).start();
        radialDashboardComponent.waitCursor();
    }

    private Task<Object> computeRadialWorker() {
        return new Task<Object>() {
            @Override
            protected Object call() throws Exception {
                radialDashboardComponent.computeRadial();
                return true;
            }
        };
    }

    /**
     * Thread dedicated to Compute radial drag process
     */
    private void computeRadialDrag() {
        final Task<Object> computeRadialDragWorker = computeRadialDragWorker();

        computeRadialDragWorker.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) {
                    radialDashboardComponent.endCursor();
                    // update all components from radial model
                    radialDashboardComponent.updateDragView();
                    timeTableComponent.updateModel(radialDashboardComponent.radial);
                    if (timetableTab.isSelected()) {
                        timeTableComponent.updateView();
                    }
                    jMapRadialComponent.updateView(radialDashboardComponent.radial);
                    radialDashboardComponent.updateEcliptic();
                    // notify change
                    getHasChanged().set(true);

                } else if (newState == Worker.State.FAILED || newState == Worker.State.CANCELLED) {
                    // thread is failed or cancelled
                    getHasChanged().set(true);
                    radialDashboardComponent.endCursor();
                    jMapRadialComponent.updateView(radialDashboardComponent.radial);
                    radialDashboardComponent.updateEcliptic();

                    DialogBox.error("Error while computing Radial propagation with drag model");
                    computeRadialDragWorker.getException().printStackTrace();
                    System.out.println("computeRadialDragWorker. " + computeRadialDragWorker.getException().getMessage());
                }
            }

        });

        new Thread(computeRadialDragWorker).start();
        radialDashboardComponent.waitCursor();
    }

    private Task<Object> computeRadialDragWorker() {
        return new Task<Object>() {
            @Override
            protected Object call() throws Exception {
                if (radialDashboardComponent.radial.isDragModel()) {
                    radialDashboardComponent.radial.computeDrag();
                }
                return true;
            }
        };
    }

    /**
     * Buttons Action
     */
    @FXML
    void catalogBtnAction(ActionEvent event) {
        //Date tStart = radialDashboardComponent.radial.getStartTime().getTime();
        RadialCatalogDialogBox.show(radialDashboardComponent.radial);
    }

    @FXML
    void amdaBtnAction(ActionEvent event) {
        Date tStart = radialDashboardComponent.radial.getStartTime().getTime();
        Date tEnd = radialDashboardComponent.radialEnd.getEndTime().getTime();
        AmdaDialogBox.show(tStart, tEnd);
    }

    @FXML
    void medocBtnAction(ActionEvent event) {
        Date tStart = radialDashboardComponent.radial.getStartTime().getTime();
        Date tEnd = radialDashboardComponent.radialEnd.getEndTime().getTime();
        MedocDialogBox.show(tStart, tEnd);
    }

    @FXML
    void apisBtnAction(ActionEvent event) {
        SpaceElement endSource = radialDashboardComponent.radialEnd.getEndSource();
        Date tEnd = radialDashboardComponent.radialEnd.getEndTime().getTime();
        ApisDialogBox.show(endSource.getName(), tEnd);
    }

    @FXML
    void sampBtnAction(ActionEvent event) {
        JSampController.getJSampFrame().setVisible(true);
    }

    @FXML
    void timetableTabChange(Event event) {
        if (timetableTab.isSelected()) {
            timeTableComponent.updateView();
        }
    }

    public BooleanProperty getHasChanged() {
        return hasChanged;
    }

    public void setHasChanged(BooleanProperty hasChanged) {
        this.hasChanged = hasChanged;
    }

    public ObjectProperty<JMapTool> getJMapTool() {
        return jMapTool;
    }

    public void setJMapTool(JMapTool jMapTool) {
        this.jMapTool.set(jMapTool);
    }

}
