use crate::common::Vector3;

use super::ni_collision_object::NiCollisionObject;
use anyhow;
use binread::{
    io::{Read, Seek},
    BinRead, BinReaderExt,
};

#[derive(Debug, PartialEq, BinRead)]
pub struct NiCollisionData {
    pub base: NiCollisionObject,
    pub propagation_mode: PropagationMode,
    pub collision_mode: CollisionMode,
    pub use_abv: u8,
    #[br(if(use_abv == 1))]
    pub bounding_volume: Option<BoundingVolume>,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct BoundingVolume {
    pub bounding_volume_data: BoundingVolumeData,
}

#[derive(Debug, PartialEq, BinRead)]
pub enum BoundingVolumeData {
    #[br(magic = 0u32)]
    Sphere(NiBound),
    #[br(magic = 1u32)]
    Box(BoxBV),
    #[br(magic = 2u32)]
    Capsule(CapsuleBV),
    #[br(magic = 4u32)]
    Union(UnionBV),
    #[br(magic = 5u32)]
    HalfSpace(HalfSpaceBV),
    #[br(magic = 0xFFFFFFFFu32)]
    Default,
    Unknown,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct NiBound {
    pub center: Vector3,
    pub radius: f32,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct BoxBV {
    pub center: Vector3,
    #[br(count = 3)]
    pub axis: Vec<Vector3>,
    pub extent: Vector3,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct CapsuleBV {
    pub center: Vector3,
    pub origin: Vector3,
    pub extent: f32,
    pub radius: f32,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct NiPlane {
    pub normal: Vector3,
    pub constant: f32,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct HalfSpaceBV {
    pub plane: NiPlane,
    pub center: Vector3,
}

#[derive(Debug, PartialEq, BinRead)]
pub struct UnionBV {
    pub num_bv: u32,
    #[br(count=num_bv)]
    pub bounding_volumes: Vec<BoundingVolume>,
}

#[derive(Debug, PartialEq, BinRead)]
pub enum PropagationMode {
    #[br(magic = 0u32)]
    OnSuccess,
    #[br(magic = 1u32)]
    OnFailure,
    #[br(magic = 2u32)]
    Always,
    #[br(magic = 3u32)]
    Never,
}

#[derive(Debug, PartialEq, BinRead)]
pub enum CollisionMode {
    #[br(magic = 0u32)]
    UseOBB,
    #[br(magic = 1u32)]
    UseTri,
    #[br(magic = 2u32)]
    UseABV,
    #[br(magic = 3u32)]
    NoTest,
    #[br(magic = 4u32)]
    UseNiBound,
}

impl NiCollisionData {
    pub fn parse<R: Read + Seek>(reader: &mut R) -> anyhow::Result<Self> {
        Ok(reader.read_le()?)
    }
}
