/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2023-05-15
 * Description : geolocation engine based on Marble.
 *               (c) 2007-2022 Marble Team
 *               https://invent.kde.org/education/marble/-/raw/master/data/credits_authors.html
 *
 * SPDX-FileCopyrightText: 2023-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * ============================================================ */

#pragma once

// Local includes

#include "attlib.h"
#include "digikam_export.h"

namespace Marble
{

class DIGIKAM_EXPORT SolarSystem     // Calculate Positions of Solar System Bodies
{
public:

    SolarSystem();
    ~SolarSystem();

public:

    void setTimezone(double d);  // set timezone for I/O
    void setDeltaTAI_UTC(double d);  // set IERS Parameter TAI - UTC
    void setAutoTAI_UTC();  // IERS Parameter TAI - UTC to auto
    void setCurrentMJD(int year, int month, int day, int hour, int min, double sec); // set current time
    void setCurrentMJD();  // sets current MJD to R/T
    double getMJD(int year, int month, int day, int hour, int min, double sec); // get MJD from date
    void getDatefromMJD(double mjd, int& year, int& month, int& day,
                        int& hour, int& min, double& sec); // convert MJD into date and time
    void setEpoch(double yr);   // set epoch of coordinates
    void setNutation(bool nut);   // specify whether nutation is to be included.
    void setCentralBody(char* pname);  // select which planet is central body
    void includeUser(bool uact);   // activate user defined object

    void getSun(double& ra, double& decl);   // RA and Dec for the Sun
    void getMoon(double& ra, double& decl);   // RA and Dec for the Moon
    void getMercury(double& ra, double& decl);   // RA and Dec for Mercury
    void getVenus(double& ra, double& decl);   // RA and Dec for Venus
    void getEarth(double& ra, double& decl);   // RA and Dec for Earth (with regard to the Sun)
    void getMars(double& ra, double& decl);   // RA and Dec for Mars
    void getJupiter(double& ra, double& decl);   // RA and Dec for Jupiter
    void getSaturn(double& ra, double& decl);   // RA and Dec for Saturn
    void getUranus(double& ra, double& decl);   // RA and Dec for Uranus
    void getNeptune(double& ra, double& decl);   // RA and Dec for Neptune
    void getIo(double& ra, double& decl);   // RA and Dec for Io
    void getEuropa(double& ra, double& decl);   // RA and Dec for Europa
    void getGanymede(double& ra, double& decl);   // RA and Dec for Ganymede
    void getCallisto(double& ra, double& decl);   // RA and Dec for Callisto
    void getRhea(double& ra, double& decl);   // RA and Dec for Rhea
    void getTitan(double& ra, double& decl);   // RA and Dec for Titan
    void getMimas(double& ra, double& decl);   // RA and Dec for Mimas
    void getEnceladus(double& ra, double& decl);   // RA and Dec for Enceladus
    void getDione(double& ra, double& decl);   // RA and Dec for Dione
    void getUser(double& ra, double& decl);   // RA and Dec for user defined object

    void getPhysSun(double& pdiam, double& pmag);   // Physical elements Sun
    void getPhysMercury(double& pdiam, double& pmag, double& pphase);  // Physical elements Mercury
    void getPhysVenus(double& pdiam, double& pmag, double& pphase);  // Physical elements Venus
    void getPhysEarth(double& pdiam, double& pmag, double& pphase);  // Physical elements Earth
    void getPhysMars(double& pdiam, double& pmag, double& pphase);  // Physical elements Mars
    void getPhysJupiter(double& pdiam, double& pmag, double& pphase);  // Physical elements Jupiter
    void getPhysSaturn(double& pdiam, double& pmag, double& pphase);  // Physical elements Saturn
    void getPhysUranus(double& pdiam, double& pmag, double& pphase);  // Physical elements Uranus
    void getPhysNeptune(double& pdiam, double& pmag, double& pphase);  // Physical elements Neptune
    double getDiamMoon();  // Apparent diameter for the Moon
    void getLunarLibration(double& lblon, double& lblat, double& termt);   // librations of the Moon and terminator position
    void getLunarPhase(double& phase, double& ildisk, double& amag);   // phase and mag of Moon

    void getPhysIo(double& pdiam, double& pmag, double& pphase);  // Physical elements Io
    void getPhysEuropa(double& pdiam, double& pmag, double& pphase);  // Physical elements Europa
    void getPhysGanymede(double& pdiam, double& pmag, double& pphase);  // Physical elements Ganymede
    void getPhysCallisto(double& pdiam, double& pmag, double& pphase);  // Physical elements Callisto
    void getPhysRhea(double& pdiam, double& pmag, double& pphase);  // Physical elements Rhea
    void getPhysTitan(double& pdiam, double& pmag, double& pphase);  // Physical elements Titan
    void getPhysMimas(double& pdiam, double& pmag, double& pphase);  // Physical elements Mimas
    void getPhysEnceladus(double& pdiam, double& pmag, double& pphase);  // Physical elements Enceladus
    void getPhysDione(double& pdiam, double& pmag, double& pphase);  // Physical elements Dione
    void getPhysUser(double& pdiam, double& pmag, double& pphase);  // Physical elements user defined object

    Vec3 getPlanetocentric(double ra, double decl);   // planetocentric position
    void getPlanetographic(double ra, double decl, double& lng, double& lat);
    void getSkyRotAngles(double& raz1, double& rax, double& raz2);

    void putOrbitElements(double t0, double pdist, double ecc, double ran, double aper, double inc, double eclep);
    void putEllipticElements(double t0, double a, double m0, double ecc, double ran, double aper, double inc, double eclep);
    void getOrbitPosition(double& ra, double& decl);
    double getDistance();  // distance in AU of Kepler object
    double getCometMag(double g, double k);  // apparent magnitude of comet
    double getAsteroidMag(double h, double g);   // apparent magnitude of asteroid
    void putOrbitUser(double t0, double pdist, double ecc, double ran, double aper, double inc, double eclep);
    void putEllipticUser(double t0, double a, double m0, double ecc, double ran, double aper, double inc, double eclep);
    void putConstUser(double j2, double r0, double flat, double axl0, double axl1, double axb0, double axb1, double w, double wd, double gm);  // store physical user constants

    double DmsDegF(double h);  // conversion from Format DDD.MMSS into d.fff
    double DegFDms(double h);  // conversion from Format d.fff into DDD.MMSS

private:

    void ssinit();  // initialize SolarSystem
    double atan23(double y, double x);   // atan without singularity for x,y=0
    void DefTime();   // Get System Time and Date
    void getRaDec(const Vec3& r1, double& ra, double& decl);  // convert r1 into RA and DEC
    void updateSolar();  // update all positions of planets according to current MJD.

    void MoonDetails();
    void MoonLibr(double jd, Vec3 rm, Vec3 sn, double& lblon, double& lblat, double& termt);
    Vec3 SnPos(double& ep2, double& els);
    Vec3 MnPos(double& ep2, double& els);
    Vec3 PosUser(double dt);  // position of User defined object
    Mat3 getSelenographic();
    void getConstSun();  // Sun constants
    void getConstMoon();  // Moon planetary constants
    void getConstMercury();  // Mercury planetary constants
    void getConstVenus();  // Venus planetary constants
    void getConstEarth();  // Earth planetary constants
    void getConstMars();  // Mars planetary constants
    void getConstJupiter(); // Jupiter planetary constants
    void getConstSaturn(); // Saturn planetary constants
    void getConstUranus(); // Uranus planetary constants
    void getConstNeptune(); // Neptune planetary constants
    void getConstIo(); // Io planetary constants
    void getConstEuropa(); // Europa planetary constants
    void getConstGanymede(); // Ganymede planetary constants
    void getConstCallisto(); // Callisto planetary constants
    void getConstRhea(); // Rhea planetary constants
    void getConstTitan(); // Titan planetary constants
    void getConstMimas(); // Mimas planetary constants
    void getConstEnceladus(); // Enceladus planetary constants
    void getConstDione(); // Dione planetary constants
    void getConstUser(); // User planetary constants

    void getPlanMat();  // get Matrix to convert from J2000.0 into planetary coordinates

private:

    // data fields

    bool ss_update_called   = false;    // true if updateSolar() has already been called
    bool ss_moon_called     = false;    // true if special Moon calculation called
    bool ss_planmat_called  = false;    // true if matrix for planetary coordinates called
    bool ss_kepler_stored   = false;    // true if Kepler elements for comet or asteroid stored
    bool ss_kepler_called   = false;    // true if comet or asteroid position calculated
    bool ss_user_stored     = false;    // true if orbit elements for user defined object stored
    bool ss_user_active     = false;    // true if user defined object activated

    int ss_day              = 0;        // date
    int ss_month            = 0;
    int ss_year             = 0;
    int ss_hour             = 0;
    int ss_minute           = 0;
    int ss_second           = 0;
    double ss_time          = 0.0;      // currently used MJD
    double ss_tzone         = 0.0;      // timezone in hours
    double ss_del_tdut      = 0.0;      // TDT - UT in sec
    int ss_del_auto         = 0;        // 1 = automatic del_tdut,  0 = manual
    bool ss_RT              = false;    // true if calculations in Real Time, false if time manually set to MJD.
    bool ss_nutation        = false;    // true if position corrected for nutation
    double ss_epoch         = 0.0;      // epoch of coordinates in MJD
    int ss_central_body     = 0;        // body defining the coordinate origin (0=sun,1=moon,2=mercury etc.; -1=user)

    Vec3 ss_rm, ss_rs;                                                                  // position of Moon and Sun in ecliptic of date (A.U.)
    Vec3 ss_pmer, ss_pven, ss_pearth, ss_pmars, ss_pjup, ss_psat, ss_pura, ss_pnept;    // positions of planets
    Vec3 ss_pio, ss_peuropa, ss_pganymede, ss_pcallisto;                                // positions of Jupiter moons
    Vec3 ss_prhea, ss_ptitan, ss_pmimas, ss_penceladus, ss_pdione;                      // positions of Saturn moons
    Vec3 ss_user;                                                                       // position of user defined object

    double ss_moon_mag      = 0.0;  // apparent magnitude of the Moon
    double ss_moon_lblon    = 0.0;  // longitude of lunar libration
    double ss_moon_lblat    = 0.0;  // latitude of lunar libration
    double ss_moon_term     = 0.0;  // position of lunar terminator
    double ss_moon_ildisk   = 0.0;  // fraction of Moon's illuminated disk
    double ss_moon_phase    = 0.0;  // phase of Moon

    double ss_GM            = 0.0;  // gravitational constant (m^3/s^2)
    double ss_J2            = 0.0;  // J2 gravitational term
    double ss_R0            = 0.0;  // equatorial radius (km)
    double ss_flat          = 0.0;  // flattening factor
    double ss_axl0          = 0.0;  // l-direction of rotation axis
    double ss_axl1          = 0.0;  // delta of axl0
    double ss_axb0          = 0.0;  // b-direction of rotation axis
    double ss_axb1          = 0.0;  // delta of axb0
    double ss_W             = 0.0;  // location of prime meridian
    double ss_Wd            = 0.0;  // daily variation of W.

    // data for user defined object:

    double ss_user_GM       = 0.0;  // gravitational constant (m^3/s^2)
    double ss_user_J2       = 0.0;  // J2 gravitational term
    double ss_user_R0       = 0.0;  // equatorial radius (km)
    double ss_user_flat     = 0.0;  // flattening factor
    double ss_user_axl0     = 0.0;  // l-direction of rotation axis
    double ss_user_axl1     = 0.0;  // delta of axl0
    double ss_user_axb0     = 0.0;  // b-direction of rotation axis
    double ss_user_axb1     = 0.0;  // delta of axb0
    double ss_user_W        = 0.0;  // location of prime meridian
    double ss_user_Wd       = 0.0;  // daily variation of W.
    double ss_user_t0       = 0.0;  // time of perihelion passage or epoch of elliptical elements (MJD
    double ss_user_m0       = 0.0;  // Mean Anomaly in degrees
    double ss_user_a        = 0.0;  // semi-major axis in AU
    double ss_user_ecc      = 0.0;  // eccentricity
    double ss_user_ran      = 0.0;  // right ascension of ascending node in degrees
    double ss_user_aper     = 0.0;  // argument of perihelion
    double ss_user_inc      = 0.0;  // inclination in degrees
    double ss_user_eclep    = 0.0;  // epoch of ecliptic and equator for these elements

    Mat3 ss_planmat;                // matrix to convert from J2000.0 into planetary coordinates

    double ss_lat           = 0.0;  // planetary latitude (decimal degrees)
    double ss_lng           = 0.0;  // planetary longitude (decimal degrees)
    double ss_height        = 0.0;  // height above reference ellipsoid (km)

    double ss_t0            = 0.0;  // time of perihelion passage or epoch of elliptical elements (MJD)
    double ss_m0            = 0.0;  // Mean Anomaly in degrees
    double ss_a             = 0.0;  // semi-major axis in AU
    double ss_ecc           = 0.0;  // eccentricity
    double ss_ran           = 0.0;  // right ascension of ascending node in degrees
    double ss_aper          = 0.0;  // argument of perihelion
    double ss_inc           = 0.0;  // inclination in degrees
    double ss_eclep         = 0.0;  // epoch of ecliptic and equator for these elements

    Vec3 ss_comet;                  // position vector of comet or asteroid in AU
};

} // namespace Marble
