//! Type-safe indices and indexed containers.

use std::default::Default;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};

pub trait EntityRef: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + Hash {
    fn new(value: usize) -> Self;
    fn index(self) -> usize;
    fn invalid() -> Self;
    fn is_valid(self) -> bool {
        self != Self::invalid()
    }
    fn is_invalid(self) -> bool {
        self == Self::invalid()
    }
    fn maybe_index(self) -> Option<usize>;
}

#[macro_export]
macro_rules! declare_entity {
    ($name:tt, $prefix:tt) => {
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        pub struct $name(u32);

        impl $crate::entity::EntityRef for $name {
            fn new(value: usize) -> Self {
                use std::convert::TryFrom;
                let value = u32::try_from(value).unwrap();
                debug_assert!(value != u32::MAX);
                Self(value)
            }
            fn index(self) -> usize {
                debug_assert!(self.is_valid());
                self.0 as usize
            }
            fn maybe_index(self) -> Option<usize> {
                if self.is_valid() {
                    Some(self.0 as usize)
                } else {
                    None
                }
            }
            fn invalid() -> Self {
                Self(u32::MAX)
            }
        }

        impl std::convert::From<u32> for $name {
            fn from(val: u32) -> Self {
                <Self as $crate::entity::EntityRef>::new(val as usize)
            }
        }

        impl std::default::Default for $name {
            fn default() -> Self {
                <Self as $crate::entity::EntityRef>::invalid()
            }
        }

        impl std::fmt::Debug for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{}{}", $prefix, self.0)
            }
        }
        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{}{}", $prefix, self.0)
            }
        }
    };
}

#[derive(Clone, Debug)]
pub struct EntityVec<Idx: EntityRef, T: Clone + Debug>(Vec<T>, PhantomData<Idx>);

impl<Idx: EntityRef, T: Clone + Debug> std::default::Default for EntityVec<Idx, T> {
    fn default() -> Self {
        Self(vec![], PhantomData)
    }
}

impl<Idx: EntityRef, T: Clone + Debug> From<Vec<T>> for EntityVec<Idx, T> {
    fn from(vec: Vec<T>) -> Self {
        Self(vec, PhantomData)
    }
}

impl<Idx: EntityRef, T: Clone + Debug> EntityVec<Idx, T> {
    pub fn push(&mut self, t: T) -> Idx {
        let idx = Idx::new(self.0.len());
        self.0.push(t);
        idx
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> impl DoubleEndedIterator<Item = Idx> {
        (0..self.0.len()).map(|index| Idx::new(index))
    }

    pub fn values(&self) -> impl DoubleEndedIterator<Item = &T> {
        self.0.iter()
    }

    pub fn values_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
        self.0.iter_mut()
    }

    pub fn entries(&self) -> impl DoubleEndedIterator<Item = (Idx, &T)> {
        self.0
            .iter()
            .enumerate()
            .map(|(index, t)| (Idx::new(index), t))
    }

    pub fn entries_mut(&mut self) -> impl Iterator<Item = (Idx, &mut T)> {
        self.0
            .iter_mut()
            .enumerate()
            .map(|(index, t)| (Idx::new(index), t))
    }

    pub fn get(&self, idx: Idx) -> Option<&T> {
        self.0.get(idx.index())
    }

    pub fn get_mut(&mut self, idx: Idx) -> Option<&mut T> {
        self.0.get_mut(idx.index())
    }

    pub fn into_vec(self) -> Vec<T> {
        self.0
    }
}

impl<Idx: EntityRef, T: Clone + Debug> Index<Idx> for EntityVec<Idx, T> {
    type Output = T;
    fn index(&self, idx: Idx) -> &T {
        &self.0[idx.index()]
    }
}

impl<Idx: EntityRef, T: Clone + Debug> IndexMut<Idx> for EntityVec<Idx, T> {
    fn index_mut(&mut self, idx: Idx) -> &mut T {
        &mut self.0[idx.index()]
    }
}

#[derive(Clone, Debug, Default)]
pub struct PerEntity<Idx: EntityRef, T: Clone + Debug + Default>(Vec<T>, PhantomData<Idx>, T);

impl<Idx: EntityRef, T: Clone + Debug + Default> Index<Idx> for PerEntity<Idx, T> {
    type Output = T;
    fn index(&self, idx: Idx) -> &T {
        debug_assert!(idx.is_valid());
        self.0.get(idx.index()).unwrap_or(&self.2)
    }
}

impl<Idx: EntityRef, T: Clone + Debug + Default> IndexMut<Idx> for PerEntity<Idx, T> {
    fn index_mut(&mut self, idx: Idx) -> &mut T {
        debug_assert!(idx.is_valid());
        if idx.index() >= self.0.len() {
            self.0.resize(idx.index() + 1, T::default());
        }
        &mut self.0[idx.index()]
    }
}

impl<Idx: EntityRef, T: Clone + Debug + Default + PartialEq> PartialEq for PerEntity<Idx, T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}
impl<Idx: EntityRef, T: Clone + Debug + Default + PartialEq + Eq> Eq for PerEntity<Idx, T> {}