pub struct NeptuneStorm {
    pub maxwindspeedms: f64,
    pub centralpressurehpa: f64,
    pub radiuskm: f64,
    pub latitudedeg: f64,
}

impl NeptuneStorm {
    pub fn greatdarkspot() -> Self {
        Self {
            maxwindspeedms: 580.0,
            centralpressurehpa: 800.0,
            radiuskm: 13000.0,
            latitudedeg: -20.0,
        }
    }

    pub fn scooter() -> Self {
        Self {
            maxwindspeedms: 400.0,
            centralpressurehpa: 900.0,
            radiuskm: 3000.0,
            latitudedeg: -42.0,
        }
    }

    pub fn smalldarkspot() -> Self {
        Self {
            maxwindspeedms: 300.0,
            centralpressurehpa: 950.0,
            radiuskm: 5000.0,
            latitudedeg: -55.0,
        }
    }

    pub fn kineticenergydensity(&self) -> f64 {
        0.5 * *crate::ONEBARDENSITY * self.maxwindspeedms * self.maxwindspeedms
    }

    pub fn vorticityestimate(&self) -> f64 {
        if self.radiuskm.abs() < 1e-10 {
            return 0.0;
        }
        2.0 * self.maxwindspeedms / (self.radiuskm * 1000.0)
    }

    pub fn rossbydeformationradius(&self) -> f64 {
        let f = 2.0 * crate::OMEGANEPTUNE * (self.latitudedeg.to_radians()).sin();
        let nh = 200.0;
        if f.abs() < 1e-10 {
            return f64::INFINITY;
        }
        nh / f.abs()
    }

    pub fn windatradius(&self, rkm: f64) -> f64 {
        let rm = self.radiuskm;
        let vm = self.maxwindspeedms;
        vm * (rm / rkm) * (-(rkm - rm).powi(2) / (2.0 * rm * rm)).exp()
    }

    pub fn potentialintensityms(internalheatingwm2: f64, tropopausetempk: f64) -> f64 {
        let ckcd = 0.9;
        let enthalpydiff = internalheatingwm2 * 1000.0;
        let sstk = crate::ONEBARTEMPK;
        let efficiency = (sstk - tropopausetempk).abs() / sstk;
        (ckcd * efficiency * enthalpydiff).sqrt()
    }
}

pub fn fujitascale(windspeedms: f64) -> &'static str {
    let mph = windspeedms * 2.23694;
    if mph >= 261.0 {
        "EF5"
    } else if mph >= 201.0 {
        "EF4"
    } else if mph >= 136.0 {
        "EF3"
    } else if mph >= 111.0 {
        "EF2"
    } else if mph >= 86.0 {
        "EF1"
    } else if mph >= 65.0 {
        "EF0"
    } else {
        "Sub-EF"
    }
}

pub fn capelayer(parceltempk: f64, envtempk: f64, dz: f64) -> f64 {
    if envtempk.abs() < 1e-10 {
        return 0.0;
    }
    *crate::SURFACEGRAVITY * ((parceltempk - envtempk) / envtempk).max(0.0) * dz
}

pub fn capeintegrated(
    parceltempk: f64,
    envsurfacetempk: f64,
    envlapseratekperm: f64,
    dzm: f64,
    nlayers: u32,
) -> f64 {
    let moistlapse = 0.0008;
    let mut capetotal = 0.0;
    let mut abovelfc = false;
    for layer in 0..nlayers {
        let z = (layer as f64 + 1.0) * dzm;
        let tparcel = parceltempk - moistlapse * z;
        let tenv = envsurfacetempk - envlapseratekperm * z;
        if tparcel > tenv {
            abovelfc = true;
            capetotal += capelayer(tparcel, tenv, dzm);
        } else if abovelfc {
            break;
        }
    }
    capetotal
}
