/* ----------------------------------------
 * Origin  : GFI INFORMATIQUE 
 * Project : CDPP Propagation Tool 
 * ----------------------------------------
 * History 
 * Version : 01/05/2015 : SCA : V2.1
 * Version : 07/04/2016 : SCA : V2.2 (PSWS-H2020)
 * => Add Z axis in propagation computation 
 * Version : 30/11/2023 : LBE : avoid triggering changes before init
 * ----------------------------------------
 */
package fr.tlse.irap.pt.client.controller.tools.sep;

import java.util.Date;

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

import fr.tlse.irap.pt.client.controller.common.Formatter;
import fr.tlse.irap.pt.client.services.ServiceFactory;
import fr.tlse.irap.spaceElement.SpaceElement;
import fr.tlse.irap.spaceElement.Sun;
import fr.tlse.irap.tool.sep.Sep;
import fr.tlse.irap.tool.sep.Sep.Particle;
import fr.tlse.irap.utils.CoordConverter;
import fr.tlse.irap.utils.ToolHelper;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class SepStartModel {

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

    ObjectProperty<SpaceElement> startSource = new SimpleObjectProperty<SpaceElement>();
    StringProperty startTime = new SimpleStringProperty();
    StringProperty speed = new SimpleStringProperty();
    StringProperty deltaSpeed = new SimpleStringProperty();
    StringProperty rotPeriod = new SimpleStringProperty();
    StringProperty spread = new SimpleStringProperty();
    StringProperty startLength = new SimpleStringProperty();
    ObjectProperty<Particle> particle = new SimpleObjectProperty<Particle>();
    StringProperty particleEnergy = new SimpleStringProperty();
    StringProperty longStart = new SimpleStringProperty();

    SpaceElement src;
    Sun sun = new Sun();

    /**
     * Set up start fields from model
     *
     * @param radialModel
     */
    public void setUpViewWith(Sep sep) {
        if (sep != null) {
            setSpeed(Formatter.toDecimal(sep.getSpeedSolarWind()));
            setDeltaSpeed(Formatter.toDecimal(sep.getSpeedError()));
            setStartLength(Formatter.toDecimal(sep.getSpiralLengthStart()));
            setRotPeriod(Formatter.toDecimal(sep.getRotationPeriod()));
            setSpread(Formatter.toDecimal(sep.getSpreadCIR()));
            setParticle(sep.getParticle());
            setParticleEnergy(Formatter.toDecimal4D(sep.getParticleEnergy()));
            setLongStart(Formatter.toDecimal1D(sep.getLongStartT()));
        }
    }

    /**
     * Update Radial model from Start fields
     *
     * @param radialModel
     */
    public void update(Sep sep) {
        sep.setSpeedSolarWind(Formatter.toDouble(speed.getValue()));
        sep.setSpeedError(Formatter.toDouble(deltaSpeed.getValue()));
        sep.setSpiralLengthStart(Formatter.toDouble(startLength.getValue()));
        sep.setRotationPeriod(Formatter.toDouble(rotPeriod.getValue()));
        sep.setSpreadCIR(Formatter.toDouble(spread.getValue()));
        sep.setParticle(particle.getValue());
        sep.setParticleEnergy(Formatter.toDouble(particleEnergy.getValue()));
        sep.setLongStartT(Formatter.toDouble(longStart.getValue()));
    }

    /**
     *
     * Compute Start fields from date
     */
    public void compute(Sep sep, Date date) {
        src = ServiceFactory.getPositionService().getPositionElement(startSource.get(), date);
        if (src != null) {
            sep.setStartTime(date);
            if (startIsSun()) {
                // HAE spheric coordinates of attitude sun at time t
                sep.setLongStartT(src.getAttitude()[0]);
                sep.setLatStartT(src.getAttitude()[1]);
            } else {
                // HAE spheric coordinates of start at time t
                double[] longLat = CoordConverter.toSpheric(src.getX(), src.getY(), src.getZ());
                sep.setLongStartT(longLat[0]);
                sep.setLatStartT(longLat[1]);
            }

            //log.debug("Computed longStartT, latStartT = " + sep.getLongStartT() + ", " + sep.getLatStartT());
            // Radial distance away SUN of START point.
            double radialDistanceStart = ToolHelper.computeRadialDistance(src);
            sep.setRadialDistanceStart(radialDistanceStart);
            //log.debug("radialDistanceStart=" + CoordConverter.toAU(radialDistanceStart) + " AU");
            double radialDistanceStartXY = ToolHelper.computeRadialDistanceXY(src);
            sep.setRadialDistStartXY(radialDistanceStartXY);
            //log.debug("radialDistanceStartXY=" + CoordConverter.toAU(radialDistanceStartXY) + " AU");
            // compute source
            computeSource(sep);
        }
    }

    /**
     *
     * Compute Start fields from Carrington source
     */
    public void computeFromSource(Sep sep) {
        if (sep.startIsSun()) {
            sep.setLongStartT(CoordConverter.normalizeLongitude(sep.getSunAttitudeT()[0] + sep.getLongSource()));
            sep.setLatStartT(CoordConverter.normalizeLongitude(sep.getSunAttitudeT()[1] + sep.getLatSource()));
            // set the Carrington source of the CIR spiral at time tsun
            sep.setLongSourceT(sep.getLongStartT());
            sep.setLatSourceT(sep.getLatStartT());
        }
    }

    /**
     * Compute carrington source from start fields and a new date
     *
     * @param radial
     */
    public void computeSource(Sep sep, Date date) {
        src = ServiceFactory.getPositionService().getPositionElement(startSource.get(), date);
        if (src != null) {
            // Radial distance away SUN of START point.
            double radialDistanceStart = ToolHelper.computeRadialDistance(src);
            sep.setRadialDistanceStart(radialDistanceStart);
            //log.debug("radialDistanceStart=" + CoordConverter.toAU(radialDistanceStart) + " AU");
            double radialDistanceStartXY = ToolHelper.computeRadialDistanceXY(src);
            sep.setRadialDistStartXY(radialDistanceStartXY);
            //log.debug("radialDistanceStartXY=" + CoordConverter.toAU(radialDistanceStartXY) + " AU");
            // compute source
            computeSource(sep);
        }
    }

    /**
     * Compute 1. Carrington source footpoint of the CIR spiral from start
     * fields. 2. HAE source footpoint longitude of the CIR spiral at time t 3.
     * Spiral start length from Sun to Start
     *
     * @param sep
     */
    public void computeSource(Sep sep) {
        if (src == null) {
            return;
        }

        // Compute sun time
        computeSunTime(sep);

        if (startIsSun()) {
            // set the Carrington source of the CIR spiral at time tsun
            sep.setLongSource(CoordConverter.normalizeLongitude(sep.getLongStartT() - sep.getSunAttitudeT()[0]));
            //sep.setLatSource(CoordConverter.normalizeLatitude(0.0 - sep.getSunAttitudeT()[1]));
            sep.setLatSource(CoordConverter.normalizeLatitude(sep.getLatStartT() - sep.getSunAttitudeT()[1]));
            // set the HAE longitude of the CIR spiral footpoint at time t
            sep.setLongSourceT(sep.getLongStartT());
            sep.setLatSourceT(sep.getLatStartT());
        } else {
            double[] longLat = CoordConverter.rectangularToCarrington(src.getX(), src.getY(), src.getZ(), sep.getSunAttitudeT());
            double omega = Math.toDegrees(ToolHelper.getRotationFrequency(sep.getRotationPeriod()));
            // compute delta longitude between START and spiral footpoint
            double dPhi = (sep.getRadialDistStartXY() * omega) / sep.getSpeedSolarWind();
            // set the Carrington source of the CIR spiral at time tsun
            sep.setLongSource(CoordConverter.normalizeLongitude(longLat[0] + dPhi));
            sep.setLatSource(longLat[1]);
            // set the HAE longitude of the CIR spiral footpoint at time t
            sep.setLongSourceT(CoordConverter.normalizeLongitude(sep.getLongStartT() + dPhi));
            sep.setLatSourceT(CoordConverter.normalizeLongitude(sep.getLatStartT()));
        }

        computeSpiralLength(sep);
    }

    /**
     * Compute the length of the Parker spiral between SUN and START
     *
     * @param sep
     */
    public void computeSpiralLength(Sep sep) {
        if (startIsSun()) {
            sep.setSpiralLengthStart(0.0);
        } else {
            sep.setSpiralLengthStart(ToolHelper.computeSpiralLength(sep.getSpeedSolarWind(), CoordConverter.toAU(sep.getRadialDistanceStart()), sep.getRotationPeriod()));
        }
    }

    /**
     * Compute attitude sun at time t
     *
     * @param c
     */
    private void computeSunTime(Sep sep) {
        if (startIsSun()) {
            // Sun time = start time => attitude at sun time = attitude at start time
            sep.setSunTime(sep.getStartTime().getTime());
            // Get attitude sun at tSun
            sep.setSunAttitudeT(src.getAttitude());
        } else {
            // Compute Sun time
            double initialSeconds = sep.getStartTime().getTimeInMillis() / 1000.0;
            // Compute the departure time of CIR (tSUN), see equation (3)
            double deltaMinutes = ToolHelper.computePropagationTime(sep.getParticleEnergy(), sep.getParticle(), sep.getSpiralLengthStart());
            double sunSeconds = initialSeconds - (deltaMinutes * 60);
            Date sunTime = new Date();
            sunTime.setTime((long) sunSeconds * 1000);
            sep.setSunTime(sunTime);
            // Get attitude sun at t
            //log.debug("Get position of " + sun.getName() + " at time " + sep.getStartTime().getTime());
            sun = (Sun) ServiceFactory.getPositionService().getPositionElement(sun, sep.getStartTime().getTime());
            if (sun != null) {
                sep.setSunAttitudeT(sun.getAttitude());
            }
        }
    }

    public Boolean startIsSun() {
        try {
            return (this.startSource.get() instanceof Sun);
        } catch (Exception e) {
            return false;
        }
    }

    public StringProperty getStartTime() {
        return startTime;
    }

    public void setStartTime(String startTime) {
        this.startTime.set(startTime);
    }

    public StringProperty getSpeed() {
        return speed;
    }

    public void setSpeed(String speed) {
        this.speed.set(speed);
    }

    public StringProperty getDeltaSpeed() {
        return deltaSpeed;
    }

    public void setDeltaSpeed(String deltaSpeed) {
        this.deltaSpeed.set(deltaSpeed);
    }

    public StringProperty getLongStart() {
        return longStart;
    }

    public void setLongStart(String longStart) {
        this.longStart.set(longStart);
    }

    public ObjectProperty<SpaceElement> getStartSource() {
        return startSource;
    }

    public void setStartSource(SpaceElement startSource) {
        this.startSource.set(startSource);
    }

    public StringProperty getSpread() {
        return spread;
    }

    public void setSpread(String spread) {
        this.spread.set(spread);
    }

    public StringProperty getRotPeriod() {
        return rotPeriod;
    }

    public void setRotPeriod(String rotPeriod) {
        this.rotPeriod.set(rotPeriod);
    }

    public ObjectProperty<Particle> getParticle() {
        return particle;
    }

    public void setParticle(Particle particle) {
        this.particle.set(particle);
    }

    public StringProperty getParticleEnergy() {
        return particleEnergy;
    }

    public void setParticleEnergy(String particleEnergy) {
        this.particleEnergy.set(particleEnergy);
    }

    public StringProperty getStartLength() {
        return startLength;
    }

    public void setStartLength(String startLength) {
        this.startLength.set(startLength);
    }
}
