use crate::{timeman};

use super::*;

pub type BodyId = usize;

pub const BODY_TICK_DURATION: Second = timeman::DAY;

pub struct OrbitalBody
{
    id: BodyId,
    system: SystemId,

    name: String,
    mass: Kilograms,
    radius: Kilometers,
    sgp: f64,

    orbit: Option<StaticOrbit>,
}

impl StaticOrbiter for OrbitalBody {
    fn orbit(&self) -> Option<&StaticOrbit> {
        self.orbit.as_ref()
    }

    fn sgp(&self) -> f64 {
        self.sgp
    }
}

impl OrbitalBody
{
    pub const TICK_DURATION: Second = BODY_TICK_DURATION;

    pub fn new_from_record(
        id: BodyId,
        system: SystemId,
        record: CSVOrbitalBody)
    -> Self {
        Self {
            id,
            system,
            name: record.name,
            mass: record.mass,
            radius: record.radius,
            sgp: record.sgp,
            orbit: match record.orbits {
                Some(parent) => Some(StaticOrbit::new(
                    parent,
                    system,
                    record.eccentricity,
                    record.inclination,
                    record.long_asc_node,
                    record.long_periapsis,
                    record.mean_long,
                    record.semi_major_axis
                )),
                None => None
            }
        }
    }

    pub fn id(&self) -> BodyId { self.id }
    pub fn system(&self) -> SystemId { self.system }
    pub fn name(&self) -> &String { &self.name }
    pub fn radius(&self) -> f32 { self.radius as f32 }
    pub fn mass(&self) -> Kilograms { self.mass }
    pub fn sgp(&self) -> f64 { self.sgp }

    pub fn origin_position(
        &self,
        star_system: &SolarSystem,
        time: Second)
    -> cgmath::Vector3<Kilometers>
    {
        if let Some(orbit) = &self.orbit {
            star_system.body(orbit.parent()).absolute_position(star_system, time)
        }else{
            cgmath::vec3(0.0, 0.0, 0.0)
        }
    }

    pub fn offset_position(
        &self, 
        time: Second) 
    -> cgmath::Vector3<f64> 
    { 
        let time = time - (time % BODY_TICK_DURATION);
        self.calculate_orbit_at(time)
    }

    pub fn absolute_position(
        &self,
        star_system: &SolarSystem,
        time: Second)
    -> cgmath::Vector3<Kilometers>
    {
        self.origin_position(star_system, time) + self.offset_position(time)
    }

    pub fn get_orbit(&self) -> &Option<StaticOrbit> { &self.orbit }

    pub fn orbital_period(
        &self
        ) -> Second
    {
        match &self.orbit {
            Some(v) => v.period(self),
            None => return 0
        }
    }

    pub fn calculate_orbit_at(
        &self,
        time: Second)
    -> cgmath::Vector3<Kilometers>
    {
        match &self.orbit {
            Some(orbit) => orbit.calculate_position_at(self, time),
            None => cgmath::vec3(0.0, 0.0, 0.0)
        }
    }
}

