use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec::Vec;
use nom::IResult;
use nom::branch::alt;
use nom::bytes::complete::{take_till1, take_until};
use nom::character::complete::{char, i64, line_ending, multispace0, not_line_ending};
use nom::combinator::map;
use nom::multi::{many0, separated_list0};
use nom::sequence::{delimited, preceded, tuple};
use serde::de::value::StrDeserializer;
use serde::{de, forward_to_deserialize_any};

// a little metadata parser
#[derive(Debug)]
pub enum MetadataValue {
    String(String),
    Number(i64),
    Array(Vec<MetadataValue>),
}
impl MetadataValue {
    pub fn parse(input: &str) -> IResult<&str, Self> {
        delimited(
            whitespace,
            alt((
                map(i64, MetadataValue::Number),
                map(
                    delimited(
                        char('['),
                        separated_list0(char(','), MetadataValue::parse),
                        char(']'),
                    ),
                    MetadataValue::Array,
                ),
                map(
                    delimited(char('"'), take_until("\""), char('"')),
                    |x: &str| MetadataValue::String(x.to_owned()),
                ),
            )),
            whitespace,
        )(input)
    }
}
#[derive(Debug)]
pub struct MetadataElements<'a>(Vec<MetadataElement<'a>>);
impl<'a> MetadataElements<'a> {
    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
        map(many0(MetadataElement::parse), MetadataElements)(input)
    }
}

#[derive(Debug)]
pub enum MetadataElement<'a> {
    Group {
        name: &'a str,
        contents: MetadataElements<'a>,
    },
    Value {
        name: &'a str,
        value: MetadataValue,
    },
}
fn parse_ident(input: &str) -> IResult<&str, &str> {
    take_till1(|x| " \r\n\t{}=[]".contains(x))(input)
}
fn comment(input: &str) -> IResult<&str, &str> {
    preceded(
        multispace0,
        delimited(char('#'), not_line_ending, line_ending),
    )(input)
}
fn whitespace(input: &str) -> IResult<&str, ()> {
    let (input, _) = many0(comment)(input)?;
    let (input, _) = multispace0(input)?;
    Ok((input, ()))
}
impl<'a> MetadataElement<'a> {
    pub fn name(&self) -> &'a str {
        match self {
            MetadataElement::Group { name, .. } | MetadataElement::Value { name, .. } => name,
        }
    }
    pub fn parse(input: &'a str) -> IResult<&'a str, Self> {
        delimited(
            whitespace,
            alt((
                map(
                    tuple((
                        parse_ident,
                        whitespace,
                        char('='),
                        whitespace,
                        MetadataValue::parse,
                    )),
                    |(name, (), _, (), v)| MetadataElement::Value { name, value: v },
                ),
                map(
                    tuple((
                        parse_ident,
                        whitespace,
                        char('{'),
                        MetadataElements::parse,
                        char('}'),
                    )),
                    |(name, (), _, contents, _)| MetadataElement::Group { name, contents },
                ),
            )),
            whitespace,
        )(input)
    }
}

impl<'de> de::Deserializer<'de> for &MetadataElements<'de> {
    forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

    type Error = serde::de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        visitor.visit_map(MetadataElementsAccess(self.0.iter().peekable()))
    }
}
pub struct MetadataElementsAccess<'a, 'de>(
    core::iter::Peekable<alloc::slice::Iter<'a, MetadataElement<'de>>>,
);

impl<'de> de::MapAccess<'de> for MetadataElementsAccess<'_, 'de> {
    type Error = serde::de::value::Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
    where
        K: de::DeserializeSeed<'de>,
    {
        match self.0.peek() {
            None => Ok(None),
            Some(x) => seed.deserialize(StrDeserializer::new(x.name())).map(Some),
        }
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
    where
        V: de::DeserializeSeed<'de>,
    {
        match self.0.next() {
            None => panic!("deserializing value before key"),
            Some(MetadataElement::Group { contents, .. }) => seed.deserialize(contents),
            Some(MetadataElement::Value { value, .. }) => seed.deserialize(value),
        }
    }
}
impl<'de> de::Deserializer<'de> for &MetadataValue {
    forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

    type Error = serde::de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        match self {
            MetadataValue::String(s) => visitor.visit_str(s),
            &MetadataValue::Number(n) => visitor.visit_i64(n),
            MetadataValue::Array(a) => visitor.visit_seq(MetadataValuesAccess(a.iter())),
        }
    }
}
pub struct MetadataValuesAccess<'a>(alloc::slice::Iter<'a, MetadataValue>);
impl<'de> de::SeqAccess<'de> for MetadataValuesAccess<'_> {
    type Error = serde::de::value::Error;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
    where
        T: de::DeserializeSeed<'de>,
    {
        match self.0.next() {
            None => Ok(None),
            Some(x) => seed.deserialize(x).map(Some),
        }
    }
}
