Documentation
use super::*;

/// Environment component accessor.
///
/// An environment is represented by an entity with the Environment component.
/// It lets you control the sun direction (and in the future other sky and environmental properties).
///
/// Currently, the last created environment is the one that will be used.

pub struct Environment {
    id: Entity,
}

impl std::fmt::Debug for Environment {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Environment")
            .field("entity", &self.id.name())
            .finish_non_exhaustive()
    }
}

impl Environment {
    /// Creates an environment entity.
    ///
    /// * `name` - Just a name, to keep track of the entity during development.
    pub fn create(name: &str) -> Self {
        let id = World::create_entity(name, ffi::EntityTemplate::Environment);
        Self { id }
    }

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the sun direction of the environment.
        ///
        /// Used to set/get/animate where the sun shines from.
        /// Note: World-space direction (extended to infinity).
        Environment,
        SunDirection,
        Vec3,
        sun_direction,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the sun size muliplier of the environment.
        ///
        /// Used to set/get/animate the sun color.
        /// The base of the sun size is based on real life values.
        Environment,
        SunSizeMultiplier,
        f32,
        sun_size_multiplier,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the gravity vector of the environment.
        ///
        /// Used to set/get/animate the gravity vector.
        ///
        /// Will be used for objects that have dynamic physics enabled.
        Environment,
        GravityVector,
        Vec3,
        gravity_vector,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the fog density of the environment.
        ///
        /// Used to set/get/animate the fog density.
        /// Fog density means the tickness of the fog where zero is no fog and infinity means infinitely thick fog.
        Environment,
        FogDensity,
        f32,
        fog_density,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the fog height of the environment.
        ///
        /// Used to set/get/animate the fog height.
        /// The world height at which the base of the exponential falloff starts
        Environment,
        FogHeight,
        f32,
        fog_height,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the fog height falloff of the environment.
        ///
        /// Used to set/get/animate the fog height falloff.
        /// The exponential rate of falloff. Zero means no falloff, and infinity means a sharp boundary
        Environment,
        FogHeightFalloff,
        f32,
        fog_height_falloff,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the minimum fog distance of the environment.
        ///
        /// Used to set/get/animate the minimum fog distance.
        /// Fog within this distance does not contribute.
        Environment,
        FogStart,
        f32,
        fog_start,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the fog color of the environment.
        ///
        /// Used to set/get/animate the fog color.
        /// The alpha component can be used to specify a maximum opacity for the final fog result.
        Environment,
        FogColor,
        Vec4,
        fog_color,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the fog color of the environment.
        ///
        /// Used to set/get/animate the fog color.
        /// The alpha component can be used to specify a maximum opacity for the final fog result.
        Environment,
        Exposure,
        f32,
        exposure,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the global illumination volume size.
        ///
        /// Used to set/get/animate the global illumination volume size.
        /// The square size of the global illumination region around the world origin.
        Environment,
        GiVolumeSize,
        f32,
        gi_volume_size,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the sun color multiplier.
        ///
        /// Used to set/get/animate the sun color multiplier.
        Environment,
        SunColorMultiplier,
        Vec3,
        sun_color_multiplier,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the ambient color.
        ///
        /// Used to set/get/animate the ambient color.
        /// The tint of the sky
        Environment,
        AmbientColor,
        Vec3,
        ambient_color,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the planet radius.
        ///
        /// Used to set/get/animate the planet radius.
        Environment,
        PlanetRadius,
        f32,
        planet_radius,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the atmosphere density.
        ///
        /// Used to set/get/animate the atmosphere density.
        Environment,
        AtmosphereDensity,
        f32,
        atmosphere_density,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the atmosphere height.
        ///
        /// Used to set/get/animate the atmosphere height in meters.
        /// The highest point of the atmosphere.
        Environment,
        AtmosphereHeight,
        f32,
        atmosphere_height,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the rayleigh height.
        ///
        /// Used to set/get/animate the rayleigh height in meters.
        /// The highest point of the gas molecule layer.
        /// Should always be less then the atmosphere height.
        Environment,
        RayleighHeight,
        f32,
        rayleigh_height,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the ozone height.
        ///
        /// Used to set/get/animate the ozone height in meters.
        /// The highest point of the ozone layer.
        /// Should always be less then the atmosphere height.
        Environment,
        OzoneHeight,
        f32,
        ozone_height,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the mie height.
        ///
        /// Used to set/get/animate the mie height.
        /// The highest point of the aerosol layer in meters.
        /// Should always be less then the atmosphere height.
        Environment,
        MieHeight,
        f32,
        mie_height,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the ozone width.
        ///
        /// Used to set/get/animate the ozone width.
        /// The width of the ozone layer in meters.
        Environment,
        OzoneWidth,
        f32,
        ozone_width,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the sky exposure.
        ///
        /// Used to set/get/animate the sky exposure.
        Environment,
        SkyExposure,
        f32,
        sky_exposure,
        ValueAccessorReadWriteAnimate
    );

    /// Sets a global collision matrix.
    ///
    /// Currently not actually bound to the environment.
    /// Can be set at any time to change collision behavior of entities with matching
    /// layers.
    pub fn set_collision_matrix(&self, matrix: &CollisionMatrix) {
        ffi::v3::set_collision_matrix_rows(0, &matrix.hit);
    }

    /// Makes this the active environment.
    ///
    /// Simplified wrapper for `EntityMessenger::set_active`.
    pub fn set_active(&self) {
        EntityMessenger::get()
            .global_queue()
            .set_active(self.id, ComponentType::Environment);
    }
}

impl_world_component!(Environment);

/// A collision matrix, specifying what can collide with what.
///
/// Due to its triangular shape, for fast access it's simply replicated
/// twice in the array, mirrored across the diagonal.
///
/// Create one, then use `Environment::set_collision_matrix` to apply it to all future created
/// physics objects. Does not change existing objects!
#[derive(Clone, PartialEq, Eq)]
pub struct CollisionMatrix {
    hit: [u64; 64],
}

impl CollisionMatrix {
    /// Creates a new collision matrix where all layers can collide with all other layers.
    pub fn new_full() -> Self {
        Self { hit: [!0u64; 64] }
    }
    /// Creates a collision matrix where no layers can collide against any other layer.
    pub fn new_empty() -> Self {
        Self { hit: [0u64; 64] }
    }

    /// Specifies whether layer `a` should collide with layer `b`.
    ///
    /// Note - this is equivalent to saying that layer `b` should collide
    /// with layer `a`, you don't need to specify that separately,
    /// it's always symmetric.
    pub fn set(&mut self, a: usize, b: usize, can_layers_collide: bool) {
        if can_layers_collide {
            self.hit[a] |= 1 << b;
            self.hit[b] |= 1 << a;
        } else {
            self.hit[a] &= !(1 << b);
            self.hit[b] &= !(1 << a);
        }
    }

    /// Checks whether this matrix lets layers a and b collide.
    pub fn get(&self, a: usize, b: usize) -> bool {
        ((self.hit[a] >> b) & 1) != 0
    }
}