use crate::mesh::{
    vertex_factory::StaticVertexFactory, MeshDrawMode, MeshError, VertexType, VertexValueType,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use vek::*;

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum GeometryValueType {
    Bool,
    String,
    Scalar,
    Vec2F,
    Vec3F,
    Vec4F,
    Mat2F,
    Mat3F,
    Mat4F,
    Integer,
    Vec2I,
    Vec3I,
    Vec4I,
    Mat2I,
    Mat3I,
    Mat4I,
}

impl GeometryValueType {
    pub fn as_vertex_value(&self) -> Option<VertexValueType> {
        match self {
            Self::Bool | Self::String => None,
            Self::Scalar => Some(VertexValueType::Scalar),
            Self::Vec2F => Some(VertexValueType::Vec2F),
            Self::Vec3F => Some(VertexValueType::Vec3F),
            Self::Vec4F => Some(VertexValueType::Vec4F),
            Self::Mat2F => Some(VertexValueType::Mat2F),
            Self::Mat3F => Some(VertexValueType::Mat3F),
            Self::Mat4F => Some(VertexValueType::Mat4F),
            Self::Integer => Some(VertexValueType::Integer),
            Self::Vec2I => Some(VertexValueType::Vec2I),
            Self::Vec3I => Some(VertexValueType::Vec3I),
            Self::Vec4I => Some(VertexValueType::Vec4I),
            Self::Mat2I => Some(VertexValueType::Mat2I),
            Self::Mat3I => Some(VertexValueType::Mat3I),
            Self::Mat4I => Some(VertexValueType::Mat4I),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GeometryValue {
    Bool(bool),
    String(String),
    Scalar(f32),
    Vec2F(Vec2<f32>),
    Vec3F(Vec3<f32>),
    Vec4F(Vec4<f32>),
    Mat2F(Mat2<f32>),
    Mat3F(Mat3<f32>),
    Mat4F(Mat4<f32>),
    Integer(i32),
    Vec2I(Vec2<i32>),
    Vec3I(Vec3<i32>),
    Vec4I(Vec4<i32>),
    Mat2I(Mat2<i32>),
    Mat3I(Mat3<i32>),
    Mat4I(Mat4<i32>),
}

impl GeometryValue {
    pub fn as_value_type(&self) -> GeometryValueType {
        match self {
            Self::Bool(_) => GeometryValueType::Bool,
            Self::String(_) => GeometryValueType::String,
            Self::Scalar(_) => GeometryValueType::Scalar,
            Self::Vec2F(_) => GeometryValueType::Vec2F,
            Self::Vec3F(_) => GeometryValueType::Vec3F,
            Self::Vec4F(_) => GeometryValueType::Vec4F,
            Self::Mat2F(_) => GeometryValueType::Mat2F,
            Self::Mat3F(_) => GeometryValueType::Mat3F,
            Self::Mat4F(_) => GeometryValueType::Mat4F,
            Self::Integer(_) => GeometryValueType::Integer,
            Self::Vec2I(_) => GeometryValueType::Vec2I,
            Self::Vec3I(_) => GeometryValueType::Vec3I,
            Self::Vec4I(_) => GeometryValueType::Vec4I,
            Self::Mat2I(_) => GeometryValueType::Mat2I,
            Self::Mat3I(_) => GeometryValueType::Mat3I,
            Self::Mat4I(_) => GeometryValueType::Mat4I,
        }
    }
}

impl ToString for GeometryValue {
    fn to_string(&self) -> String {
        match self {
            Self::Bool(value) => value.to_string(),
            Self::String(value) => value.to_string(),
            Self::Scalar(value) => value.to_string(),
            Self::Vec2F(value) => value.to_string(),
            Self::Vec3F(value) => value.to_string(),
            Self::Vec4F(value) => value.to_string(),
            Self::Mat2F(value) => value.to_string(),
            Self::Mat3F(value) => value.to_string(),
            Self::Mat4F(value) => value.to_string(),
            Self::Integer(value) => value.to_string(),
            Self::Vec2I(value) => value.to_string(),
            Self::Vec3I(value) => value.to_string(),
            Self::Vec4I(value) => value.to_string(),
            Self::Mat2I(value) => value.to_string(),
            Self::Mat3I(value) => value.to_string(),
            Self::Mat4I(value) => value.to_string(),
        }
    }
}

macro_rules! impl_value_from {
    (@copy $type:ty => $variant:ident) => {
        impl From<$type> for GeometryValue {
            fn from(value: $type) -> Self {
                Self::$variant(value)
            }
        }

        impl From<&$type> for GeometryValue {
            fn from(value: &$type) -> Self {
                Self::$variant(*value)
            }
        }
    };
    (@ownedref $type:ty => $variant:ident) => {
        impl From<&$type> for GeometryValue {
            fn from(value: &$type) -> Self {
                Self::$variant(value.to_owned())
            }
        }
    };
    (@owned $type:ty => $variant:ident) => {
        impl From<$type> for GeometryValue {
            fn from(value: $type) -> Self {
                Self::$variant(value)
            }
        }
    };
}

macro_rules! impl_value_into {
    (@copy $type:ty => $($variant:ident),+) => {
        impl TryInto<$type> for &GeometryValue {
            type Error = MeshError;

            fn try_into(self) -> Result<$type, Self::Error> {
                match self {
                    $(
                        GeometryValue::$variant(result) => Ok(<$type>::from(*result)),
                    )+
                    _ => Err(MeshError::UnsupportedGeometryValueConversionType(std::any::type_name::<$type>().to_owned())),
                }
            }
        }

        impl TryInto<$type> for GeometryValue {
            type Error = MeshError;

            fn try_into(self) -> Result<$type, Self::Error> {
                match self {
                    $(
                        GeometryValue::$variant(result) => Ok(<$type>::from(result)),
                    )+
                    _ => Err(MeshError::UnsupportedGeometryValueConversionType(std::any::type_name::<$type>().to_owned())),
                }
            }
        }
    };
    (@owned $type:ty => $variant:ident) => {
        impl TryInto<$type> for &GeometryValue {
            type Error = MeshError;

            fn try_into(self) -> Result<$type, Self::Error> {
                match self {
                    GeometryValue::$variant(result) => Ok(result.to_owned()),
                    _ => Err(MeshError::UnsupportedGeometryValueConversionType(std::any::type_name::<$type>().to_owned())),
                }
            }
        }
        impl TryInto<$type> for GeometryValue {
            type Error = MeshError;

            fn try_into(self) -> Result<$type, Self::Error> {
                match self {
                    GeometryValue::$variant(result) => Ok(result),
                    _ => Err(MeshError::UnsupportedGeometryValueConversionType(std::any::type_name::<$type>().to_owned())),
                }
            }
        }
    };
}

impl_value_from!(@copy bool => Bool);
impl_value_from!(@ownedref str => String);
impl_value_from!(@ownedref String => String);
impl_value_from!(@owned String => String);
impl_value_from!(@copy f32 => Scalar);
impl_value_from!(@copy Vec2<f32> => Vec2F);
impl_value_from!(@copy Vec3<f32> => Vec3F);
impl_value_from!(@copy Vec4<f32> => Vec4F);
impl_value_from!(@copy Mat2<f32> => Mat2F);
impl_value_from!(@copy Mat3<f32> => Mat3F);
impl_value_from!(@copy Mat4<f32> => Mat4F);
impl_value_from!(@copy i32 => Integer);
impl_value_from!(@copy Vec2<i32> => Vec2I);
impl_value_from!(@copy Vec3<i32> => Vec3I);
impl_value_from!(@copy Vec4<i32> => Vec4I);
impl_value_from!(@copy Mat2<i32> => Mat2I);
impl_value_from!(@copy Mat3<i32> => Mat3I);
impl_value_from!(@copy Mat4<i32> => Mat4I);

impl_value_into!(@copy bool => Bool);
impl_value_into!(@owned String => String);
impl_value_into!(@copy f32 => Scalar);
impl_value_into!(@copy Vec2<f32> => Vec2F, Vec3F, Vec4F);
impl_value_into!(@copy Vec3<f32> => Vec2F, Vec3F, Vec4F);
impl_value_into!(@copy Vec4<f32> => Vec2F, Vec3F, Vec4F);
impl_value_into!(@copy Mat2<f32> => Mat2F, Mat3F, Mat4F);
impl_value_into!(@copy Mat3<f32> => Mat2F, Mat3F, Mat4F);
impl_value_into!(@copy Mat4<f32> => Mat2F, Mat3F, Mat4F);
impl_value_into!(@copy i32 => Integer);
impl_value_into!(@copy Vec2<i32> => Vec2I, Vec3I, Vec4I);
impl_value_into!(@copy Vec3<i32> => Vec2I, Vec3I, Vec4I);
impl_value_into!(@copy Vec4<i32> => Vec2I, Vec3I, Vec4I);
impl_value_into!(@copy Mat2<i32> => Mat2I, Mat3I, Mat4I);
impl_value_into!(@copy Mat3<i32> => Mat2I, Mat3I, Mat4I);
impl_value_into!(@copy Mat4<i32> => Mat2I, Mat3I, Mat4I);

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GeometryValues {
    Bool(Vec<bool>),
    String(Vec<String>),
    Scalar(Vec<f32>),
    Vec2F(Vec<Vec2<f32>>),
    Vec3F(Vec<Vec3<f32>>),
    Vec4F(Vec<Vec4<f32>>),
    Mat2F(Vec<Mat2<f32>>),
    Mat3F(Vec<Mat3<f32>>),
    Mat4F(Vec<Mat4<f32>>),
    Integer(Vec<i32>),
    Vec2I(Vec<Vec2<i32>>),
    Vec3I(Vec<Vec3<i32>>),
    Vec4I(Vec<Vec4<i32>>),
    Mat2I(Vec<Mat2<i32>>),
    Mat3I(Vec<Mat3<i32>>),
    Mat4I(Vec<Mat4<i32>>),
}

impl GeometryValues {
    pub fn as_value_type(&self) -> GeometryValueType {
        match self {
            Self::Bool(_) => GeometryValueType::Bool,
            Self::String(_) => GeometryValueType::String,
            Self::Scalar(_) => GeometryValueType::Scalar,
            Self::Vec2F(_) => GeometryValueType::Vec2F,
            Self::Vec3F(_) => GeometryValueType::Vec3F,
            Self::Vec4F(_) => GeometryValueType::Vec4F,
            Self::Mat2F(_) => GeometryValueType::Mat2F,
            Self::Mat3F(_) => GeometryValueType::Mat3F,
            Self::Mat4F(_) => GeometryValueType::Mat4F,
            Self::Integer(_) => GeometryValueType::Integer,
            Self::Vec2I(_) => GeometryValueType::Vec2I,
            Self::Vec3I(_) => GeometryValueType::Vec3I,
            Self::Vec4I(_) => GeometryValueType::Vec4I,
            Self::Mat2I(_) => GeometryValueType::Mat2I,
            Self::Mat3I(_) => GeometryValueType::Mat3I,
            Self::Mat4I(_) => GeometryValueType::Mat4I,
        }
    }

    pub fn len(&self) -> usize {
        match self {
            Self::Bool(items) => items.len(),
            Self::String(items) => items.len(),
            Self::Scalar(items) => items.len(),
            Self::Vec2F(items) => items.len(),
            Self::Vec3F(items) => items.len(),
            Self::Vec4F(items) => items.len(),
            Self::Mat2F(items) => items.len(),
            Self::Mat3F(items) => items.len(),
            Self::Mat4F(items) => items.len(),
            Self::Integer(items) => items.len(),
            Self::Vec2I(items) => items.len(),
            Self::Vec3I(items) => items.len(),
            Self::Vec4I(items) => items.len(),
            Self::Mat2I(items) => items.len(),
            Self::Mat3I(items) => items.len(),
            Self::Mat4I(items) => items.len(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn get(&self, index: usize) -> Option<GeometryValue> {
        match self {
            Self::Bool(items) => items.get(index).map(|value| value.into()),
            Self::String(items) => items.get(index).map(|value| value.into()),
            Self::Scalar(items) => items.get(index).map(|value| value.into()),
            Self::Vec2F(items) => items.get(index).map(|value| value.into()),
            Self::Vec3F(items) => items.get(index).map(|value| value.into()),
            Self::Vec4F(items) => items.get(index).map(|value| value.into()),
            Self::Mat2F(items) => items.get(index).map(|value| value.into()),
            Self::Mat3F(items) => items.get(index).map(|value| value.into()),
            Self::Mat4F(items) => items.get(index).map(|value| value.into()),
            Self::Integer(items) => items.get(index).map(|value| value.into()),
            Self::Vec2I(items) => items.get(index).map(|value| value.into()),
            Self::Vec3I(items) => items.get(index).map(|value| value.into()),
            Self::Vec4I(items) => items.get(index).map(|value| value.into()),
            Self::Mat2I(items) => items.get(index).map(|value| value.into()),
            Self::Mat3I(items) => items.get(index).map(|value| value.into()),
            Self::Mat4I(items) => items.get(index).map(|value| value.into()),
        }
    }

    pub fn iter(&self) -> GeometryValuesIter {
        match self {
            Self::Bool(items) => GeometryValuesIter::Bool(items.iter()),
            Self::String(items) => GeometryValuesIter::String(items.iter()),
            Self::Scalar(items) => GeometryValuesIter::Scalar(items.iter()),
            Self::Vec2F(items) => GeometryValuesIter::Vec2F(items.iter()),
            Self::Vec3F(items) => GeometryValuesIter::Vec3F(items.iter()),
            Self::Vec4F(items) => GeometryValuesIter::Vec4F(items.iter()),
            Self::Mat2F(items) => GeometryValuesIter::Mat2F(items.iter()),
            Self::Mat3F(items) => GeometryValuesIter::Mat3F(items.iter()),
            Self::Mat4F(items) => GeometryValuesIter::Mat4F(items.iter()),
            Self::Integer(items) => GeometryValuesIter::Integer(items.iter()),
            Self::Vec2I(items) => GeometryValuesIter::Vec2I(items.iter()),
            Self::Vec3I(items) => GeometryValuesIter::Vec3I(items.iter()),
            Self::Vec4I(items) => GeometryValuesIter::Vec4I(items.iter()),
            Self::Mat2I(items) => GeometryValuesIter::Mat2I(items.iter()),
            Self::Mat3I(items) => GeometryValuesIter::Mat3I(items.iter()),
            Self::Mat4I(items) => GeometryValuesIter::Mat4I(items.iter()),
        }
    }
}

pub enum GeometryValuesIter<'a> {
    Bool(std::slice::Iter<'a, bool>),
    String(std::slice::Iter<'a, String>),
    Scalar(std::slice::Iter<'a, f32>),
    Vec2F(std::slice::Iter<'a, Vec2<f32>>),
    Vec3F(std::slice::Iter<'a, Vec3<f32>>),
    Vec4F(std::slice::Iter<'a, Vec4<f32>>),
    Mat2F(std::slice::Iter<'a, Mat2<f32>>),
    Mat3F(std::slice::Iter<'a, Mat3<f32>>),
    Mat4F(std::slice::Iter<'a, Mat4<f32>>),
    Integer(std::slice::Iter<'a, i32>),
    Vec2I(std::slice::Iter<'a, Vec2<i32>>),
    Vec3I(std::slice::Iter<'a, Vec3<i32>>),
    Vec4I(std::slice::Iter<'a, Vec4<i32>>),
    Mat2I(std::slice::Iter<'a, Mat2<i32>>),
    Mat3I(std::slice::Iter<'a, Mat3<i32>>),
    Mat4I(std::slice::Iter<'a, Mat4<i32>>),
}

impl<'a> Iterator for GeometryValuesIter<'a> {
    type Item = GeometryValue;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::Bool(iter) => iter.next().map(|value| value.into()),
            Self::String(iter) => iter.next().map(|value| value.into()),
            Self::Scalar(iter) => iter.next().map(|value| value.into()),
            Self::Vec2F(iter) => iter.next().map(|value| value.into()),
            Self::Vec3F(iter) => iter.next().map(|value| value.into()),
            Self::Vec4F(iter) => iter.next().map(|value| value.into()),
            Self::Mat2F(iter) => iter.next().map(|value| value.into()),
            Self::Mat3F(iter) => iter.next().map(|value| value.into()),
            Self::Mat4F(iter) => iter.next().map(|value| value.into()),
            Self::Integer(iter) => iter.next().map(|value| value.into()),
            Self::Vec2I(iter) => iter.next().map(|value| value.into()),
            Self::Vec3I(iter) => iter.next().map(|value| value.into()),
            Self::Vec4I(iter) => iter.next().map(|value| value.into()),
            Self::Mat2I(iter) => iter.next().map(|value| value.into()),
            Self::Mat3I(iter) => iter.next().map(|value| value.into()),
            Self::Mat4I(iter) => iter.next().map(|value| value.into()),
        }
    }
}

macro_rules! impl_values_from {
    ($type:ty => $variant:ident) => {
        impl FromIterator<$type> for GeometryValues {
            fn from_iter<I: IntoIterator<Item = $type>>(iter: I) -> Self {
                Self::$variant(iter.into_iter().collect())
            }
        }
    };
}

impl_values_from!(bool => Bool);
impl_values_from!(String => String);
impl_values_from!(f32 => Scalar);
impl_values_from!(Vec2<f32> => Vec2F);
impl_values_from!(Vec3<f32> => Vec3F);
impl_values_from!(Vec4<f32> => Vec4F);
impl_values_from!(Mat2<f32> => Mat2F);
impl_values_from!(Mat3<f32> => Mat3F);
impl_values_from!(Mat4<f32> => Mat4F);
impl_values_from!(i32 => Integer);
impl_values_from!(Vec2<i32> => Vec2I);
impl_values_from!(Vec3<i32> => Vec3I);
impl_values_from!(Vec4<i32> => Vec4I);
impl_values_from!(Mat2<i32> => Mat2I);
impl_values_from!(Mat3<i32> => Mat3I);
impl_values_from!(Mat4<i32> => Mat4I);

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryAttributes {
    items: BTreeMap<String, GeometryValue>,
}

impl GeometryAttributes {
    pub fn with(mut self, name: impl ToString, value: impl Into<GeometryValue>) -> Self {
        self.set(name, value);
        self
    }

    pub fn get(&self, name: &str) -> Option<GeometryValue> {
        self.items.get(name).cloned()
    }

    pub fn set(
        &mut self,
        name: impl ToString,
        value: impl Into<GeometryValue>,
    ) -> Option<GeometryValue> {
        self.items.insert(name.to_string(), value.into())
    }

    pub fn delete(&mut self, name: &str) -> Option<GeometryValue> {
        self.items.remove(name)
    }

    pub fn has(&self, name: &str) -> bool {
        self.items.contains_key(name)
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn iter(&self) -> impl Iterator<Item = (&str, &GeometryValue)> {
        self.items.iter().map(|(key, value)| (key.as_str(), value))
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut GeometryValue)> {
        self.items
            .iter_mut()
            .map(|(key, value)| (key.as_str(), value))
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryTriangle {
    pub indices: [usize; 3],
    pub attributes: GeometryAttributes,
}

impl GeometryTriangle {
    pub fn new(indices: [usize; 3]) -> Self {
        Self {
            indices,
            attributes: Default::default(),
        }
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<[usize; 3]> for GeometryTriangle {
    fn from(indices: [usize; 3]) -> Self {
        Self::new(indices)
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryTriangles {
    pub items: Vec<GeometryTriangle>,
    pub attributes: GeometryAttributes,
}

impl GeometryTriangles {
    pub fn with(mut self, triangle: impl Into<GeometryTriangle>) -> Self {
        self.items.push(triangle.into());
        self
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<()> for GeometryTriangles {
    fn from(_: ()) -> Self {
        Self::default()
    }
}

impl From<&[GeometryTriangle]> for GeometryTriangles {
    fn from(items: &[GeometryTriangle]) -> Self {
        items.iter().cloned().collect()
    }
}

impl From<Vec<GeometryTriangle>> for GeometryTriangles {
    fn from(items: Vec<GeometryTriangle>) -> Self {
        Self {
            items,
            attributes: Default::default(),
        }
    }
}

impl FromIterator<GeometryTriangle> for GeometryTriangles {
    fn from_iter<I: IntoIterator<Item = GeometryTriangle>>(iter: I) -> Self {
        Self {
            items: iter.into_iter().collect(),
            attributes: Default::default(),
        }
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryLine {
    pub indices: [usize; 2],
    pub attributes: GeometryAttributes,
}

impl GeometryLine {
    pub fn new(indices: [usize; 2]) -> Self {
        Self {
            indices,
            attributes: Default::default(),
        }
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<[usize; 2]> for GeometryLine {
    fn from(indices: [usize; 2]) -> Self {
        Self::new(indices)
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryLines {
    pub items: Vec<GeometryLine>,
    pub attributes: GeometryAttributes,
}

impl GeometryLines {
    pub fn with(mut self, line: impl Into<GeometryLine>) -> Self {
        self.items.push(line.into());
        self
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<()> for GeometryLines {
    fn from(_: ()) -> Self {
        Self::default()
    }
}

impl From<&[GeometryLine]> for GeometryLines {
    fn from(items: &[GeometryLine]) -> Self {
        items.iter().cloned().collect()
    }
}

impl From<Vec<GeometryLine>> for GeometryLines {
    fn from(items: Vec<GeometryLine>) -> Self {
        Self {
            items,
            attributes: Default::default(),
        }
    }
}

impl FromIterator<GeometryLine> for GeometryLines {
    fn from_iter<I: IntoIterator<Item = GeometryLine>>(iter: I) -> Self {
        Self {
            items: iter.into_iter().collect(),
            attributes: Default::default(),
        }
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryPoint {
    pub index: usize,
    pub attributes: GeometryAttributes,
}

impl GeometryPoint {
    pub fn new(index: usize) -> Self {
        Self {
            index,
            attributes: Default::default(),
        }
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<usize> for GeometryPoint {
    fn from(index: usize) -> Self {
        Self::new(index)
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryPoints {
    pub items: Vec<GeometryPoint>,
    pub attributes: GeometryAttributes,
}

impl GeometryPoints {
    pub fn with(mut self, point: impl Into<GeometryPoint>) -> Self {
        self.items.push(point.into());
        self
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }
}

impl From<()> for GeometryPoints {
    fn from(_: ()) -> Self {
        Self::default()
    }
}

impl From<&[GeometryPoint]> for GeometryPoints {
    fn from(items: &[GeometryPoint]) -> Self {
        items.iter().cloned().collect()
    }
}

impl From<Vec<GeometryPoint>> for GeometryPoints {
    fn from(items: Vec<GeometryPoint>) -> Self {
        Self {
            items,
            attributes: Default::default(),
        }
    }
}

impl FromIterator<GeometryPoint> for GeometryPoints {
    fn from_iter<I: IntoIterator<Item = GeometryPoint>>(iter: I) -> Self {
        Self {
            items: iter.into_iter().collect(),
            attributes: Default::default(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GeometryPrimitives {
    Triangles(GeometryTriangles),
    Lines(GeometryLines),
    Points(GeometryPoints),
}

impl GeometryPrimitives {
    pub fn triangles(triangles: impl Into<GeometryTriangles>) -> Self {
        Self::Triangles(triangles.into())
    }

    pub fn lines(lines: impl Into<GeometryLines>) -> Self {
        Self::Lines(lines.into())
    }

    pub fn points(points: impl Into<GeometryPoints>) -> Self {
        Self::Points(points.into())
    }

    pub fn as_triangles(&self) -> Result<&GeometryTriangles, MeshError> {
        match self {
            Self::Triangles(result) => Ok(result),
            _ => Err(MeshError::GeometryIsNotTriangles),
        }
    }

    pub fn as_lines(&self) -> Result<&GeometryLines, MeshError> {
        match self {
            Self::Lines(result) => Ok(result),
            _ => Err(MeshError::GeometryIsNotTriangles),
        }
    }

    pub fn as_points(&self) -> Result<&GeometryPoints, MeshError> {
        match self {
            Self::Points(result) => Ok(result),
            _ => Err(MeshError::GeometryIsNotTriangles),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryVerticesColumn {
    pub name: String,
    pub values: GeometryValues,
}

impl GeometryVerticesColumn {
    pub fn new(name: impl ToString, values: GeometryValues) -> Self {
        Self {
            name: name.to_string(),
            values,
        }
    }
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct GeometryVertices {
    columns: Vec<GeometryVerticesColumn>,
    count: usize,
    pub attributes: GeometryAttributes,
}

impl GeometryVertices {
    pub fn new(columns: &[(&str, GeometryValueType)]) -> Result<Self, MeshError> {
        let mut result = Self::default();
        for (name, value_type) in columns {
            result.ensure_column(name, *value_type)?;
        }
        Ok(result)
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }

    pub fn with(mut self, attributes: GeometryAttributes) -> Result<Self, MeshError> {
        self.push(attributes)?;
        Ok(self)
    }

    pub fn with_columns(
        mut self,
        columns: impl IntoIterator<Item = GeometryVerticesColumn>,
    ) -> Result<Self, MeshError> {
        self.push_columns(columns)?;
        Ok(self)
    }

    pub fn ensure_column(
        &mut self,
        name: &str,
        value_type: GeometryValueType,
    ) -> Result<(), MeshError> {
        if let Some(column) = self.columns.iter().find(|column| column.name == name) {
            if column.values.as_value_type() != value_type {
                return Err(MeshError::GeometryValueTypeMismatch(
                    value_type,
                    column.values.as_value_type(),
                ));
            }
        } else {
            self.columns.push(GeometryVerticesColumn {
                name: name.to_owned(),
                values: match value_type {
                    GeometryValueType::Bool => {
                        GeometryValues::Bool(vec![Default::default(); self.count])
                    }
                    GeometryValueType::String => {
                        GeometryValues::String(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Scalar => {
                        GeometryValues::Scalar(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec2F => {
                        GeometryValues::Vec2F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec3F => {
                        GeometryValues::Vec3F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec4F => {
                        GeometryValues::Vec4F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat2F => {
                        GeometryValues::Mat2F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat3F => {
                        GeometryValues::Mat3F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat4F => {
                        GeometryValues::Mat4F(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Integer => {
                        GeometryValues::Integer(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec2I => {
                        GeometryValues::Vec2I(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec3I => {
                        GeometryValues::Vec3I(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Vec4I => {
                        GeometryValues::Vec4I(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat2I => {
                        GeometryValues::Mat2I(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat3I => {
                        GeometryValues::Mat3I(vec![Default::default(); self.count])
                    }
                    GeometryValueType::Mat4I => {
                        GeometryValues::Mat4I(vec![Default::default(); self.count])
                    }
                },
            });
        }
        Ok(())
    }

    pub fn ensure_columns(&mut self, attributes: &GeometryAttributes) -> Result<(), MeshError> {
        for (key, value) in attributes.iter() {
            self.ensure_column(key, value.as_value_type())?;
        }
        Ok(())
    }

    pub fn reserve(&mut self, additional: usize) {
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => items.reserve(additional),
                GeometryValues::String(items) => items.reserve(additional),
                GeometryValues::Scalar(items) => items.reserve(additional),
                GeometryValues::Vec2F(items) => items.reserve(additional),
                GeometryValues::Vec3F(items) => items.reserve(additional),
                GeometryValues::Vec4F(items) => items.reserve(additional),
                GeometryValues::Mat2F(items) => items.reserve(additional),
                GeometryValues::Mat3F(items) => items.reserve(additional),
                GeometryValues::Mat4F(items) => items.reserve(additional),
                GeometryValues::Integer(items) => items.reserve(additional),
                GeometryValues::Vec2I(items) => items.reserve(additional),
                GeometryValues::Vec3I(items) => items.reserve(additional),
                GeometryValues::Vec4I(items) => items.reserve(additional),
                GeometryValues::Mat2I(items) => items.reserve(additional),
                GeometryValues::Mat3I(items) => items.reserve(additional),
                GeometryValues::Mat4I(items) => items.reserve(additional),
            }
        }
    }

    pub fn ensure_len(&mut self, count: usize) {
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => items.extend(
                    std::iter::repeat(bool::default()).take(count.saturating_sub(items.len())),
                ),
                GeometryValues::String(items) => items.extend(
                    std::iter::repeat(String::default()).take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Scalar(items) => items.extend(
                    std::iter::repeat(f32::default()).take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec2F(items) => items.extend(
                    std::iter::repeat(Vec2::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec3F(items) => items.extend(
                    std::iter::repeat(Vec3::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec4F(items) => items.extend(
                    std::iter::repeat(Vec4::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat2F(items) => items.extend(
                    std::iter::repeat(Mat2::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat3F(items) => items.extend(
                    std::iter::repeat(Mat3::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat4F(items) => items.extend(
                    std::iter::repeat(Mat4::<f32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Integer(items) => items.extend(
                    std::iter::repeat(i32::default()).take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec2I(items) => items.extend(
                    std::iter::repeat(Vec2::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec3I(items) => items.extend(
                    std::iter::repeat(Vec3::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Vec4I(items) => items.extend(
                    std::iter::repeat(Vec4::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat2I(items) => items.extend(
                    std::iter::repeat(Mat2::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat3I(items) => items.extend(
                    std::iter::repeat(Mat3::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
                GeometryValues::Mat4I(items) => items.extend(
                    std::iter::repeat(Mat4::<i32>::default())
                        .take(count.saturating_sub(items.len())),
                ),
            }
        }
        self.count = count;
    }

    pub fn clear(&mut self) {
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => items.clear(),
                GeometryValues::String(items) => items.clear(),
                GeometryValues::Scalar(items) => items.clear(),
                GeometryValues::Vec2F(items) => items.clear(),
                GeometryValues::Vec3F(items) => items.clear(),
                GeometryValues::Vec4F(items) => items.clear(),
                GeometryValues::Mat2F(items) => items.clear(),
                GeometryValues::Mat3F(items) => items.clear(),
                GeometryValues::Mat4F(items) => items.clear(),
                GeometryValues::Integer(items) => items.clear(),
                GeometryValues::Vec2I(items) => items.clear(),
                GeometryValues::Vec3I(items) => items.clear(),
                GeometryValues::Vec4I(items) => items.clear(),
                GeometryValues::Mat2I(items) => items.clear(),
                GeometryValues::Mat3I(items) => items.clear(),
                GeometryValues::Mat4I(items) => items.clear(),
            }
        }
        self.count = 0;
    }

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

    pub fn is_empty(&self) -> bool {
        self.count == 0
    }

    pub fn remove_column(&mut self, name: &str) {
        if let Some(index) = self.columns.iter().position(|c| c.name == name) {
            self.columns.remove(index);
        }
    }

    pub fn set_column(&mut self, column: GeometryVerticesColumn) {
        self.remove_column(&column.name);
        self.columns.push(column);
        let count = self
            .columns
            .iter()
            .fold(0, |accum, column| column.values.len().max(accum));
        self.ensure_len(count);
    }

    pub fn push_columns(
        &mut self,
        columns: impl IntoIterator<Item = GeometryVerticesColumn>,
    ) -> Result<(), MeshError> {
        for column in columns {
            self.ensure_column(&column.name, column.values.as_value_type())?;
            let target = self
                .columns
                .iter_mut()
                .find(|c| c.name == column.name)
                .unwrap();
            match (&mut target.values, &column.values) {
                (GeometryValues::Bool(target), GeometryValues::Bool(source)) => {
                    target.extend(source)
                }
                (GeometryValues::String(target), GeometryValues::String(source)) => {
                    target.extend(source.to_owned())
                }
                (GeometryValues::Scalar(target), GeometryValues::Scalar(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec2F(target), GeometryValues::Vec2F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec3F(target), GeometryValues::Vec3F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec4F(target), GeometryValues::Vec4F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat2F(target), GeometryValues::Mat2F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat3F(target), GeometryValues::Mat3F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat4F(target), GeometryValues::Mat4F(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Integer(target), GeometryValues::Integer(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec2I(target), GeometryValues::Vec2I(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec3I(target), GeometryValues::Vec3I(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Vec4I(target), GeometryValues::Vec4I(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat2I(target), GeometryValues::Mat2I(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat3I(target), GeometryValues::Mat3I(source)) => {
                    target.extend(source)
                }
                (GeometryValues::Mat4I(target), GeometryValues::Mat4I(source)) => {
                    target.extend(source)
                }
                _ => unreachable!(),
            }
        }
        let count = self
            .columns
            .iter()
            .fold(0, |accum, column| column.values.len().max(accum));
        self.ensure_len(count);
        Ok(())
    }

    pub fn push(&mut self, attributes: GeometryAttributes) -> Result<(), MeshError> {
        self.ensure_columns(&attributes)?;
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::String(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Scalar(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec2F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec3F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec4F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat2F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat3F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat4F(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Integer(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec2I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec3I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec4I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat2I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat3I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat4I(items) => items.push(
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
            }
        }
        self.count += 1;
        Ok(())
    }

    pub fn set(&mut self, index: usize, attributes: GeometryAttributes) -> Result<(), MeshError> {
        if index >= self.count {
            return Err(MeshError::OutOfBounds(index, self.count));
        }
        self.ensure_columns(&attributes)?;
        for (key, value) in attributes.iter() {
            if let Some(column) = self.columns.iter_mut().find(|column| column.name == key) {
                match &mut column.values {
                    GeometryValues::Bool(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::String(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Scalar(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec2F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec3F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec4F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat2F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat3F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat4F(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Integer(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec2I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec3I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Vec4I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat2I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat3I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                    GeometryValues::Mat4I(items) => {
                        items[index] = value.try_into().unwrap_or_default();
                    }
                }
            }
        }
        Ok(())
    }

    pub fn insert(
        &mut self,
        mut index: usize,
        attributes: GeometryAttributes,
    ) -> Result<(), MeshError> {
        index = index.min(self.count);
        self.ensure_columns(&attributes)?;
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::String(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Scalar(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec2F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec3F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec4F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat2F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat3F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat4F(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Integer(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec2I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec3I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Vec4I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat2I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat3I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
                GeometryValues::Mat4I(items) => items.insert(
                    index,
                    attributes
                        .get(&column.name)
                        .and_then(|value| value.try_into().ok())
                        .unwrap_or_default(),
                ),
            }
        }
        self.count += 1;
        Ok(())
    }

    pub fn get(&self, index: usize) -> Option<GeometryAttributes> {
        if index >= self.count {
            return None;
        }
        let mut result = GeometryAttributes::default();
        for column in &self.columns {
            match &column.values {
                GeometryValues::Bool(items) => result.set(&column.name, items[index])?,
                GeometryValues::String(items) => result.set(&column.name, items[index].as_str())?,
                GeometryValues::Scalar(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec2F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec3F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec4F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat2F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat3F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat4F(items) => result.set(&column.name, items[index])?,
                GeometryValues::Integer(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec2I(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec3I(items) => result.set(&column.name, items[index])?,
                GeometryValues::Vec4I(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat2I(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat3I(items) => result.set(&column.name, items[index])?,
                GeometryValues::Mat4I(items) => result.set(&column.name, items[index])?,
            };
        }
        Some(result)
    }

    pub fn get_column(&self, name: &str, index: usize) -> Option<GeometryValue> {
        let column = self.columns.iter().find(|column| column.name == name)?;
        Some(match &column.values {
            GeometryValues::Bool(items) => items[index].into(),
            GeometryValues::String(items) => items[index].to_owned().into(),
            GeometryValues::Scalar(items) => items[index].into(),
            GeometryValues::Vec2F(items) => items[index].into(),
            GeometryValues::Vec3F(items) => items[index].into(),
            GeometryValues::Vec4F(items) => items[index].into(),
            GeometryValues::Mat2F(items) => items[index].into(),
            GeometryValues::Mat3F(items) => items[index].into(),
            GeometryValues::Mat4F(items) => items[index].into(),
            GeometryValues::Integer(items) => items[index].into(),
            GeometryValues::Vec2I(items) => items[index].into(),
            GeometryValues::Vec3I(items) => items[index].into(),
            GeometryValues::Vec4I(items) => items[index].into(),
            GeometryValues::Mat2I(items) => items[index].into(),
            GeometryValues::Mat3I(items) => items[index].into(),
            GeometryValues::Mat4I(items) => items[index].into(),
        })
    }

    pub fn remove(&mut self, index: usize) -> Option<GeometryAttributes> {
        let result = self.get(index)?;
        for column in &mut self.columns {
            match &mut column.values {
                GeometryValues::Bool(items) => {
                    items.remove(index);
                }
                GeometryValues::String(items) => {
                    items.remove(index);
                }
                GeometryValues::Scalar(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec2F(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec3F(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec4F(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat2F(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat3F(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat4F(items) => {
                    items.remove(index);
                }
                GeometryValues::Integer(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec2I(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec3I(items) => {
                    items.remove(index);
                }
                GeometryValues::Vec4I(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat2I(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat3I(items) => {
                    items.remove(index);
                }
                GeometryValues::Mat4I(items) => {
                    items.remove(index);
                }
            }
        }
        self.count -= 1;
        Some(result)
    }

    pub fn columns_types(&self) -> impl Iterator<Item = (&str, GeometryValueType)> {
        self.columns
            .iter()
            .map(|column| (column.name.as_str(), column.values.as_value_type()))
    }

    pub fn column_type(&self, name: &str) -> Result<GeometryValueType, MeshError> {
        match self
            .columns_types()
            .find(|(n, _)| n == &name)
            .map(|(_, t)| t)
        {
            Some(result) => Ok(result),
            None => Err(MeshError::GeometryAttributeNotFound(name.to_owned())),
        }
    }

    pub fn columns(&self) -> impl Iterator<Item = (&str, &GeometryValues)> {
        self.columns
            .iter()
            .map(|column| (column.name.as_str(), &column.values))
    }

    pub fn column(&self, name: &str) -> Result<&GeometryValues, MeshError> {
        match self
            .columns
            .iter()
            .find(|column| column.name == name)
            .map(|column| &column.values)
        {
            Some(result) => Ok(result),
            None => Err(MeshError::GeometryAttributeNotFound(name.to_owned())),
        }
    }

    pub fn iter(&self) -> GeometryVerticesIter {
        GeometryVerticesIter {
            inner: self
                .columns
                .iter()
                .map(|column| (column.name.as_str(), column.values.iter()))
                .collect(),
        }
    }
}

pub struct GeometryVerticesIter<'a> {
    inner: Vec<(&'a str, GeometryValuesIter<'a>)>,
}

impl<'a> Iterator for GeometryVerticesIter<'a> {
    type Item = GeometryAttributes;

    fn next(&mut self) -> Option<Self::Item> {
        let mut result = GeometryAttributes::default();
        for (name, iter) in &mut self.inner {
            result.set(name, iter.next()?);
        }
        Some(result)
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Geometry {
    pub vertices: GeometryVertices,
    pub primitives: GeometryPrimitives,
    pub attributes: GeometryAttributes,
}

impl Geometry {
    pub fn triangles() -> Self {
        Self::new(
            GeometryVertices::default(),
            GeometryPrimitives::triangles(vec![]),
        )
    }

    pub fn lines() -> Self {
        Self::new(
            GeometryVertices::default(),
            GeometryPrimitives::lines(vec![]),
        )
    }

    pub fn points() -> Self {
        Self::new(
            GeometryVertices::default(),
            GeometryPrimitives::points(vec![]),
        )
    }

    pub fn new(vertices: GeometryVertices, primitives: GeometryPrimitives) -> Self {
        Self {
            vertices,
            primitives,
            attributes: Default::default(),
        }
    }

    pub fn with_attribute(mut self, name: impl ToString, value: GeometryValue) -> Self {
        self.attributes.set(name, value);
        self
    }

    pub fn factory<T: VertexType>(&self) -> Result<StaticVertexFactory, MeshError> {
        let vertex_layout = T::vertex_layout()?;
        let mut result = match &self.primitives {
            GeometryPrimitives::Triangles(triangles) => {
                let mut result = StaticVertexFactory::new(
                    vertex_layout.to_owned(),
                    triangles.items.len() * 3,
                    triangles.items.len(),
                    MeshDrawMode::Triangles,
                );
                result.triangles(
                    &triangles
                        .items
                        .iter()
                        .map(|item| {
                            let [a, b, c] = item.indices;
                            (a as u32, b as u32, c as u32)
                        })
                        .collect::<Vec<_>>(),
                    None,
                )?;
                result
            }
            GeometryPrimitives::Lines(lines) => {
                let mut result = StaticVertexFactory::new(
                    vertex_layout.to_owned(),
                    lines.items.len() * 2,
                    lines.items.len(),
                    MeshDrawMode::Lines,
                );
                result.lines(
                    &lines
                        .items
                        .iter()
                        .map(|item| {
                            let [a, b] = item.indices;
                            (a as u32, b as u32)
                        })
                        .collect::<Vec<_>>(),
                    None,
                )?;
                result
            }
            GeometryPrimitives::Points(points) => {
                let mut result = StaticVertexFactory::new(
                    vertex_layout.to_owned(),
                    points.items.len(),
                    points.items.len(),
                    MeshDrawMode::Points,
                );
                result.points(
                    &points
                        .items
                        .iter()
                        .map(|item| item.index as u32)
                        .collect::<Vec<_>>(),
                    None,
                )?;
                result
            }
        };
        for attribute in vertex_layout.attributes() {
            if let Ok(values) = self.vertices.column(&attribute.id) {
                match values {
                    GeometryValues::Bool(_) | GeometryValues::String(_) => {}
                    GeometryValues::Scalar(items) => {
                        result.vertices_scalar(&attribute.id, items, None)?;
                    }
                    GeometryValues::Vec2F(items) => match attribute.value_type {
                        VertexValueType::Vec2F => {
                            result.vertices_vec2f(&attribute.id, items, None)?;
                        }
                        VertexValueType::Vec3F => {
                            result.vertices_vec3f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec4F => {
                            result.vertices_vec4f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Vec3F(items) => match attribute.value_type {
                        VertexValueType::Vec2F => {
                            result.vertices_vec2f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec3F => {
                            result.vertices_vec3f(&attribute.id, items, None)?;
                        }
                        VertexValueType::Vec4F => {
                            result.vertices_vec4f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Vec4F(items) => match attribute.value_type {
                        VertexValueType::Vec2F => {
                            result.vertices_vec2f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec3F => {
                            result.vertices_vec3f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec4F => {
                            result.vertices_vec4f(&attribute.id, items, None)?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat2F(items) => match attribute.value_type {
                        VertexValueType::Mat2F => {
                            result.vertices_mat2f(&attribute.id, items, None)?;
                        }
                        VertexValueType::Mat3F => {
                            result.vertices_mat3f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat4F => {
                            result.vertices_mat4f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat3F(items) => match attribute.value_type {
                        VertexValueType::Mat2F => {
                            result.vertices_mat2f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat3F => {
                            result.vertices_mat3f(&attribute.id, items, None)?;
                        }
                        VertexValueType::Mat4F => {
                            result.vertices_mat4f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat4F(items) => match attribute.value_type {
                        VertexValueType::Mat2F => {
                            result.vertices_mat2f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat3F => {
                            result.vertices_mat3f(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat4F => {
                            result.vertices_mat4f(&attribute.id, items, None)?;
                        }
                        _ => {}
                    },
                    GeometryValues::Integer(items) => {
                        result.vertices_integer(&attribute.id, items, None)?;
                    }
                    GeometryValues::Vec2I(items) => match attribute.value_type {
                        VertexValueType::Vec2I => {
                            result.vertices_vec2i(&attribute.id, items, None)?;
                        }
                        VertexValueType::Vec3I => {
                            result.vertices_vec3i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec4I => {
                            result.vertices_vec4i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Vec3I(items) => match attribute.value_type {
                        VertexValueType::Vec2I => {
                            result.vertices_vec2i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec3I => {
                            result.vertices_vec3i(&attribute.id, items, None)?;
                        }
                        VertexValueType::Vec4I => {
                            result.vertices_vec4i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Vec4I(items) => match attribute.value_type {
                        VertexValueType::Vec2I => {
                            result.vertices_vec2i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec3I => {
                            result.vertices_vec3i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Vec4I => {
                            result.vertices_vec4i(&attribute.id, items, None)?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat2I(items) => match attribute.value_type {
                        VertexValueType::Mat2I => {
                            result.vertices_mat2i(&attribute.id, items, None)?;
                        }
                        VertexValueType::Mat3I => {
                            result.vertices_mat3i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat4I => {
                            result.vertices_mat4i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat3I(items) => match attribute.value_type {
                        VertexValueType::Mat2I => {
                            result.vertices_mat2i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat3I => {
                            result.vertices_mat3i(&attribute.id, items, None)?;
                        }
                        VertexValueType::Mat4I => {
                            result.vertices_mat4i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        _ => {}
                    },
                    GeometryValues::Mat4I(items) => match attribute.value_type {
                        VertexValueType::Mat2I => {
                            result.vertices_mat2i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat3I => {
                            result.vertices_mat3i(
                                &attribute.id,
                                &items
                                    .iter()
                                    .map(|value| (*value).into())
                                    .collect::<Vec<_>>(),
                                None,
                            )?;
                        }
                        VertexValueType::Mat4I => {
                            result.vertices_mat4i(&attribute.id, items, None)?;
                        }
                        _ => {}
                    },
                }
            }
        }
        Ok(result)
    }
}
