#pragma once

#include <cstddef>
#include <vector>

#include "taylor_order_2.hpp"

namespace astrodyn_core {
namespace geqoe {

struct Order3Intermediates {
    double Xpp;
    double Ypp;
    double cosLp2;
    double sinLp2;
    double hpp;
    double dp2;
    double whp2;
    double Ipp;
    double Upp;
    double bpp;
    double cpp;
    double alphap2;
    double deltap2;
    double zgpp;
    double fUzp2;
    double GAMMApp_;
    double xi1p2;
    double xi2p2;
    double qsp2;
    double psp2;
    double Cpp;
    double Dpp;
    double rpnp2;
    double rp3;
    double rp3_nu;
    double rp3_Lr;
    double rp3_q1;
    double rp3_q2;
    double rp3_p1;
    double rp3_p2;
    double fiDp2;
    double fibp2;
    double ficp2;
    double fihp2;
    double fialphap2;
    double fibm1p2;
    double fihr3p2;
    double firp3;
    double fir2p3;
    double fir3p3;
    double f2rpp2;
    double f2cp_p;
    double r3p3;
    double hr3p2;
    double hrp;
    double Xpp_nu;
    double Xpp_Lr;
    double Xpp_q1;
    double Xpp_q2;
    double Xpp_p1;
    double Xpp_p2;
    double Ypp_nu;
    double Ypp_Lr;
    double Ypp_q1;
    double Ypp_q2;
    double Ypp_p1;
    double Ypp_p2;
    double cosLp2_nu;
    double cosLp2_Lr;
    double cosLp2_q1;
    double cosLp2_q2;
    double cosLp2_p1;
    double cosLp2_p2;
    double sinLp2_nu;
    double sinLp2_Lr;
    double sinLp2_q1;
    double sinLp2_q2;
    double sinLp2_p1;
    double sinLp2_p2;
    double zgpp_nu;
    double zgpp_Lr;
    double zgpp_q1;
    double zgpp_q2;
    double zgpp_p1;
    double zgpp_p2;
    double fUzp2_nu;
    double fUzp2_Lr;
    double fUzp2_q1;
    double fUzp2_q2;
    double fUzp2_p1;
    double fUzp2_p2;
    double Upp_nu;
    double Upp_Lr;
    double Upp_q1;
    double Upp_q2;
    double Upp_p1;
    double Upp_p2;
    double bpp_nu;
    double bpp_Lr;
    double bpp_q1;
    double bpp_q2;
    double bpp_p1;
    double bpp_p2;
    double cpp_nu;
    double cpp_Lr;
    double cpp_q1;
    double cpp_q2;
    double cpp_p1;
    double cpp_p2;
    double hpp_nu;
    double hpp_Lr;
    double hpp_q1;
    double hpp_q2;
    double hpp_p1;
    double hpp_p2;
    double alphap2_nu;
    double alphap2_Lr;
    double alphap2_q1;
    double alphap2_q2;
    double alphap2_p1;
    double alphap2_p2;
    double deltap2_nu;
    double deltap2_Lr;
    double deltap2_q1;
    double deltap2_q2;
    double deltap2_p1;
    double deltap2_p2;
    double Ipp_nu;
    double Ipp_Lr;
    double Ipp_q1;
    double Ipp_q2;
    double Ipp_p1;
    double Ipp_p2;
    double dp2_nu;
    double dp2_Lr;
    double dp2_q1;
    double dp2_q2;
    double dp2_p1;
    double dp2_p2;
    double whp2_nu;
    double whp2_Lr;
    double whp2_q1;
    double whp2_q2;
    double whp2_p1;
    double whp2_p2;
    double GAMMApp_nu;
    double GAMMApp_Lr;
    double GAMMApp_q1;
    double GAMMApp_q2;
    double GAMMApp_p1;
    double GAMMApp_p2;
    double xi1p2_nu;
    double xi1p2_Lr;
    double xi1p2_q1;
    double xi1p2_q2;
    double xi1p2_p1;
    double xi1p2_p2;
    double xi2p2_nu;
    double xi2p2_Lr;
    double xi2p2_q1;
    double xi2p2_q2;
    double xi2p2_p1;
    double xi2p2_p2;
    double qsp2_nu;
    double qsp2_Lr;
    double qsp2_q1;
    double qsp2_q2;
    double qsp2_p1;
    double qsp2_p2;
    double psp2_nu;
    double psp2_Lr;
    double psp2_q1;
    double psp2_q2;
    double psp2_p1;
    double psp2_p2;
    double Cpp_nu;
    double Cpp_Lr;
    double Cpp_q1;
    double Cpp_q2;
    double Cpp_p1;
    double Cpp_p2;
    double Dpp_nu;
    double Dpp_Lr;
    double Dpp_q1;
    double Dpp_q2;
    double Dpp_p1;
    double Dpp_p2;
    double fiDp2_nu;
    double fiDp2_Lr;
    double fiDp2_q1;
    double fiDp2_q2;
    double fiDp2_p1;
    double fiDp2_p2;
    double rpnp2_nu;
    double rpnp2_Lr;
    double rpnp2_q1;
    double rpnp2_q2;
    double rpnp2_p1;
    double rpnp2_p2;
    double rpp_nu;
    double rpp_Lr;
    double rpp_q1;
    double rpp_q2;
    double rpp_p1;
    double rpp_p2;
    double r3p3_nu;
    double r3p3_Lr;
    double r3p3_q1;
    double r3p3_q2;
    double r3p3_p1;
    double r3p3_p2;
    double hr3p2_nu;
    double hr3p2_Lr;
    double hr3p2_q1;
    double hr3p2_q2;
    double hr3p2_p1;
    double hr3p2_p2;
    double fihr3p2_nu;
    double fihr3p2_Lr;
    double fihr3p2_q1;
    double fihr3p2_q2;
    double fihr3p2_p1;
    double fihr3p2_p2;
    double firp3_nu;
    double firp3_Lr;
    double firp3_q1;
    double firp3_q2;
    double firp3_p1;
    double firp3_p2;
    double fir2p3_nu;
    double fir2p3_Lr;
    double fir2p3_q1;
    double fir2p3_q2;
    double fir2p3_p1;
    double fir2p3_p2;
    double fir3_p3_nu;
    double fir3_p3_Lr;
    double fir3_p3_q1;
    double fir3_p3_q2;
    double fir3_p3_p1;
    double fir3_p3_p2;
    double f2rpp2_nu;
    double f2rpp2_Lr;
    double f2rpp2_q1;
    double f2rpp2_q2;
    double f2rpp2_p1;
    double f2rpp2_p2;
    double f2cp_p_nu;
    double f2cp_p_Lr;
    double f2cp_p_q1;
    double f2cp_p_q2;
    double f2cp_p_p1;
    double f2cp_p_p2;
    double ficp2_nu;
    double ficp2_Lr;
    double ficp2_q1;
    double ficp2_q2;
    double ficp2_p1;
    double ficp2_p2;
    double fihp2_nu;
    double fihp2_Lr;
    double fihp2_q1;
    double fihp2_q2;
    double fihp2_p1;
    double fihp2_p2;
    double fialphap2_nu;
    double fialphap2_Lr;
    double fialphap2_q1;
    double fialphap2_q2;
    double fialphap2_p1;
    double fialphap2_p2;
    double fibp2_nu;
    double fibp2_Lr;
    double fibp2_q1;
    double fibp2_q2;
    double fibp2_p1;
    double fibp2_p2;
    double fibm1_p2_nu;
    double fibm1_p2_Lr;
    double fibm1_p2_q1;
    double fibm1_p2_q2;
    double fibm1_p2_p1;
    double fibm1_p2_p2;
    double hrp_nu;
    double hrp_Lr;
    double hrp_q1;
    double hrp_q2;
    double hrp_p1;
    double hrp_p2;
    double fic;
    double q1p3_nu;
    double q1p3_Lr;
    double q1p3_q1;
    double q1p3_q2;
    double q1p3_p1;
    double q1p3_p2;
    double q2p3_nu;
    double q2p3_Lr;
    double q2p3_q1;
    double q2p3_q2;
    double q2p3_p1;
    double q2p3_p2;
    double p1p3_nu;
    double p1p3_Lr;
    double p1p3_q1;
    double p1p3_q2;
    double p1p3_p1;
    double p1p3_p2;
    double p2p3_nu;
    double p2p3_Lr;
    double p2p3_q1;
    double p2p3_q2;
    double p2p3_p1;
    double p2p3_p2;
    double Lrp3_nu;
    double Lrp3_Lr;
    double Lrp3_q1;
    double Lrp3_q2;
    double Lrp3_p1;
    double Lrp3_p2;
    double p1ppp_0;
    double p2ppp_0;
    double Lrppp_0;
    double q1ppp_0;
    double q2ppp_0;
};

struct Order3Coefficients {
    double q1ppp_0;
    double q2ppp_0;
    double p1ppp_0;
    double p2ppp_0;
    double Lrppp_0;
    double q1p3_nu;
    double q1p3_Lr;
    double q1p3_q1;
    double q1p3_q2;
    double q1p3_p1;
    double q1p3_p2;
    double q2p3_nu;
    double q2p3_Lr;
    double q2p3_q1;
    double q2p3_q2;
    double q2p3_p1;
    double q2p3_p2;
    double p1p3_nu;
    double p1p3_Lr;
    double p1p3_q1;
    double p1p3_q2;
    double p1p3_p1;
    double p1p3_p2;
    double p2p3_nu;
    double p2p3_Lr;
    double p2p3_q1;
    double p2p3_q2;
    double p2p3_p1;
    double p2p3_p2;
    double Lrp3_nu;
    double Lrp3_Lr;
    double Lrp3_q1;
    double Lrp3_q2;
    double Lrp3_p1;
    double Lrp3_p2;
    double map_components_col2[6];
};

// Order 3 reuses Order1EvaluationScratch

void compute_coefficients_3(
    const double* y0,
    const PropagationConstants& constants,
    Order1Coefficients& out1,
    Order1Intermediates& inter1,
    Order2Coefficients& out2,
    Order2Intermediates& inter2,
    Order3Coefficients& out,
    Order3Intermediates& inter
);

void evaluate_order_3(
    const Order3Coefficients& coeffs,
    const double* dt_norm,
    std::size_t M,
    double* y_prop,
    Order1EvaluationScratch& scratch
);

} // namespace geqoe
} // namespace astrodyn_core
