//! Code for parsing and serializing device or global property values.
//! 
//! The parsing code is specific because a property value is only represented as an
//! array of strings in Tango.

use std::fmt::{Debug, Display};
use std::convert::Infallible;
use std::borrow::Cow;


/// A trait for converting some type into property'd values.
pub trait IntoProp<'d> {
    /// Push this property value into the given arrays of string values, this should not
    /// alter the property value itself.
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E);
}

/// A trait for parsing a type from some property'd values.
pub trait FromProp<'d>: Sized {
    /// Type of error for the parsing.
    type Err: Display + Debug;
    /// Parse this property'd type from a series of string values.
    fn from_prop(values: &'d [Cow<'d, str>]) -> std::result::Result<Self, Self::Err>;
}

// =========================== //
//   Default implementations   //
// =========================== //

impl<'d, T> IntoProp<'d> for Vec<T>
where
    T: IntoProp<'d>,
{
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        for val in self {
            val.into_prop(values);
        }
    }
}

impl<'d, T, const N: usize> IntoProp<'d> for [T; N]
where
    T: IntoProp<'d>,
{
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        for val in self {
            val.into_prop(values);
        }
    }
}

impl<'d, T> IntoProp<'d> for &'_ [T]
where
    for<'a> &'a T: IntoProp<'d>,
{
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        for val in self {
            val.into_prop(values);
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum ParsePropError<E> {
    #[error("no value")]
    NoValue,
    #[error("too much values")]
    TooMuchValues,
    #[error("parse: {0}")]
    Parse(#[from] E),
}

macro_rules! impl_prop_for_parse {
    ( $ty:ty ) => {
        
        impl<'d> IntoProp<'d> for $ty {
            #[inline]
            fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
                values.extend(Some(Cow::<str>::Owned(format!("{self}"))));
            }
        }
        
        impl<'d> IntoProp<'d> for &'_ $ty {
            #[inline]
            fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
                (*self).into_prop(values);
            }
        }
        
        impl<'d> FromProp<'d> for $ty {
            type Err = ParsePropError<<$ty as std::str::FromStr>::Err>;
            fn from_prop(values: &[Cow<'d, str>]) -> std::result::Result<Self, Self::Err> {
                match values.len() {
                    0 => Err(ParsePropError::NoValue),
                    1 => values[0].parse::<$ty>().map_err(ParsePropError::Parse),
                    2.. => Err(ParsePropError::NoValue),
                    
                }
            }
        }
        
        impl<'d> FromProp<'d> for Vec<$ty> {
            type Err = ParsePropError<<$ty as std::str::FromStr>::Err>;
            fn from_prop(values: &[Cow<'d, str>]) -> std::result::Result<Self, Self::Err> {
                let mut ret = Vec::with_capacity(values.len());
                for val in values {
                    ret.push(val.parse::<$ty>().map_err(ParsePropError::Parse)?);
                }
                Ok(ret)
            }
        }

    };
}

impl_prop_for_parse!(bool);
impl_prop_for_parse!(u8);
impl_prop_for_parse!(i8);
impl_prop_for_parse!(u16);
impl_prop_for_parse!(i16);
impl_prop_for_parse!(u32);
impl_prop_for_parse!(i32);
impl_prop_for_parse!(u64);
impl_prop_for_parse!(i64);
impl_prop_for_parse!(f32);
impl_prop_for_parse!(f64);

// =========================== //
//   String implementations    //
// =========================== //

impl<'d> IntoProp<'d> for Cow<'d, str> {
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        values.extend(Some(self));
    }
}

impl<'d> IntoProp<'d> for &'d str {
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        Cow::<str>::Borrowed(self).into_prop(values);
    }
}

impl<'d> IntoProp<'d> for String {
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        Cow::<str>::Owned(self).into_prop(values);
    }
}

impl<'d> IntoProp<'d> for &'d String {
    fn into_prop<E: Extend<Cow<'d, str>>>(self, values: &mut E) {
        Cow::<str>::Borrowed(self).into_prop(values);
    }
}

impl<'d> FromProp<'d> for String {
    type Err = Infallible;
    fn from_prop(values: &'d [Cow<'d, str>]) -> std::result::Result<Self, Self::Err> {
        let mut buf = String::new();
        for val in values {
            if !buf.is_empty() {
                buf.push('\n');
            }
            buf.push_str(&*val);
        }
        Ok(buf)
    }
}

impl<'d> FromProp<'d> for Vec<String> {
    type Err = Infallible;
    fn from_prop(values: &'d [Cow<'d, str>]) -> std::result::Result<Self, Self::Err> {
        Ok(values.iter().map(|s| s.to_string()).collect::<Vec<_>>())
    }
}

impl<'d: 'd_, 'd_> FromProp<'d> for &'d_ [Cow<'d_, str>] {
    type Err = Infallible;
    fn from_prop(values: &'d [Cow<'d, str>]) -> std::result::Result<Self, Self::Err> {
        Ok(values)
    }
}
