// src/Environment.cpp

#include "Environment.h"

#include <cmath> // For pow()

namespace {

// Constants
constexpr double R_EARTH = 6371000.0;         // Earth radius (meters)
constexpr double G0 = 9.80665;                // Gravity at sea level (m/s2)
constexpr double R_AIR = 287.05;               // Specific gas constant for air (J/kg/K)

// US Standard Atmosphere 1976 layers up to 86 km
struct AtmosphereLayer {
    double baseAltitude;     // meters
    double baseTemperature;  // Kelvin
    double basePressure;     // Pascals
    double lapseRate;        // K/m
};

// Base layer properties precomputed
static const AtmosphereLayer AtmosphereLayers[] = {
    {0.0,     288.15, 101325.0,    -0.0065},
    {11000.0, 216.65, 22632.06,     0.0},
    {20000.0, 216.65, 5474.889,     0.001},
    {32000.0, 228.65, 868.019,      0.0028},
    {47000.0, 270.65, 110.906,      0.0},
    {51000.0, 270.65, 66.93887,    -0.0028},
    {71000.0, 214.65, 3.956420,    -0.002},
};

static constexpr int NumAtmosphereLayers = sizeof(AtmosphereLayers) / sizeof(AtmosphereLayer);

//const AtmosphereLayer* Environment::findAtmosphereLayer(double altitude) const {
const AtmosphereLayer* findAtmosphereLayer(double altitude) {
    for (int i = NumAtmosphereLayers - 1; i >= 0; --i) {
        if (altitude >= AtmosphereLayers[i].baseAltitude) {
            return &AtmosphereLayers[i];
        }
    }
    // Should never happen, but fallback
    return &AtmosphereLayers[0];
}

} // namespace

// Constructor
Environment::Environment(double surfacePressure,
                         double surfaceTemperature,
                         double launchAltitude)
    : surfacePressure_(surfacePressure),
      surfaceTemperature_(surfaceTemperature),
      launchAltitude_(launchAltitude)
{}

// Public Methods

double Environment::getAirDensity(double altitude) const {
    double temp = getStandardTemperature(altitude);
    double pressure = getStandardPressure(altitude);

    return pressure / (R_AIR * temp);
}

double Environment::getGravity(double altitude) const {
    return G0 * std::pow(R_EARTH / (R_EARTH + altitude), 2);
}

double Environment::getSurfacePressure() const {
    return surfacePressure_;
}

double Environment::getSurfaceTemperature() const {
    return surfaceTemperature_;
}

double Environment::getLaunchAltitude() const {
    return launchAltitude_;
}

// Private Methods

double Environment::getStandardTemperature(double altitude) const {
    const AtmosphereLayer* layer = findAtmosphereLayer(altitude);

    double deltaH = altitude - layer->baseAltitude;
    return layer->baseTemperature + layer->lapseRate * deltaH;
}

double Environment::getStandardPressure(double altitude) const {
    const AtmosphereLayer* layer = findAtmosphereLayer(altitude);

    double deltaH = altitude - layer->baseAltitude;
    double T = getStandardTemperature(altitude);
    double T0 = layer->baseTemperature;
    double P0 = layer->basePressure;
    double a = layer->lapseRate;

    if (a == 0.0) {
        // Isothermal layer
        return P0 * std::exp(-G0 * deltaH / (R_AIR * T0));
    } else {
        // Gradient layer
        return P0 * std::pow(T0 / T, (G0 / (a * R_AIR)));
    }
}

