Documentation
use std::fmt;

use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::{IResult, Parser};

use super::attribute::parser_attribute_name;
use super::char_string::{parser_char_string, CharString};
use super::common_parsers::{multispacey, number_value};
use super::error::DbcParseError;

#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AttributeValue {
    Double(f64),
    String(CharString),
}

impl fmt::Display for AttributeValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AttributeValue::Double(v) => write!(f, "{v}"),
            AttributeValue::String(v) => write!(f, r#""{v}""#),
        }
    }
}

pub fn parser_attribute_value_double(input: &str) -> IResult<&str, AttributeValue, DbcParseError> {
    map(number_value, AttributeValue::Double).parse(input)
}

pub fn parser_attribute_value_string(input: &str) -> IResult<&str, AttributeValue, DbcParseError> {
    map(parser_char_string, AttributeValue::String).parse(input)
}

pub fn parser_attribute_value(input: &str) -> IResult<&str, AttributeValue, DbcParseError> {
    let res = alt((parser_attribute_value_double, parser_attribute_value_string)).parse(input);

    match res {
        Ok((remain, value)) => {
            log::info!("parse attribute value: {value:?}");
            Ok((remain, value))
        }
        Err(e) => {
            log::trace!("parse attribute value failed, e = {e:?}");
            Err(e)
        }
    }
}

#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AttributeDefinitionDefault {
    attribute_name: String,
    attribute_value: AttributeValue,
}

impl fmt::Display for AttributeDefinitionDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            r#"BA_DEF_DEF_ "{}" {};"#,
            self.attribute_name, self.attribute_value
        )
    }
}

pub fn parser_attribute_definition_default(
    input: &str,
) -> IResult<&str, AttributeDefault, DbcParseError> {
    let res = map(
        (
            multispacey(tag("BA_DEF_DEF_")),
            multispacey(parser_attribute_name),
            multispacey(parser_attribute_value),
            multispacey(tag(";")),
        ),
        |(_, attribute_name, attribute_value, _)| AttributeDefinitionDefault {
            attribute_name: attribute_name.to_string(),
            attribute_value,
        },
    )
    .parse(input);

    match res {
        Ok((remain, value)) => {
            log::info!("parse attribute default: {value:?}");
            Ok((remain, AttributeDefault::Attribute(value)))
        }
        Err(e) => {
            log::trace!("parse attribute default failed, e = {e:?}");
            Err(nom::Err::Error(
                DbcParseError::BadAttributeDefinitionDefault,
            ))
        }
    }
}

#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RelationAttributeDefinitionDefault {
    attribute_name: String,
    attribute_value: AttributeValue,
}

impl fmt::Display for RelationAttributeDefinitionDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            r#"BA_DEF_DEF_REL_ "{}" {};"#,
            self.attribute_name, self.attribute_value
        )
    }
}

pub fn parser_relation_attribute_definition_default(
    input: &str,
) -> IResult<&str, AttributeDefault, DbcParseError> {
    let res = map(
        (
            multispacey(tag("BA_DEF_DEF_REL_")),
            multispacey(parser_attribute_name),
            multispacey(parser_attribute_value),
            multispacey(tag(";")),
        ),
        |(_, attribute_name, attribute_value, _)| RelationAttributeDefinitionDefault {
            attribute_name: attribute_name.to_string(),
            attribute_value,
        },
    )
    .parse(input);

    match res {
        Ok((remain, value)) => {
            log::info!("parse relation attribute default: {value:?}");
            Ok((remain, AttributeDefault::RelationAttribute(value)))
        }
        Err(e) => {
            log::trace!("parse relation attribute default failed, e = {e:?}");
            Err(nom::Err::Error(
                DbcParseError::BadRelationAttributeDefinitionDefault,
            ))
        }
    }
}

#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AttributeDefault {
    Attribute(AttributeDefinitionDefault),
    RelationAttribute(RelationAttributeDefinitionDefault),
}

impl fmt::Display for AttributeDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AttributeDefault::Attribute(v) => write!(f, "{v}"),
            AttributeDefault::RelationAttribute(v) => write!(f, "{v}"),
        }
    }
}

pub fn parser_attribute_default(input: &str) -> IResult<&str, AttributeDefault, DbcParseError> {
    let res = alt((
        parser_attribute_definition_default,
        parser_relation_attribute_definition_default,
    ))
    .parse(input);

    match res {
        Ok((remain, value)) => {
            log::info!("parse attribute default: {value:?}");
            Ok((remain, value))
        }
        Err(e) => {
            log::trace!("parse attribute default failed, e = {e:?}");
            Err(e)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_attribute_default_string_01() {
        assert_eq!(
            AttributeDefault::Attribute(AttributeDefinitionDefault {
                attribute_name: "attribute_name".to_string(),
                attribute_value: AttributeValue::Double(0.0)
            })
            .to_string(),
            r#"BA_DEF_DEF_ "attribute_name" 0;"#
        );
    }

    #[test]
    fn test_attribute_default_string_02() {
        assert_eq!(
            AttributeDefault::Attribute(AttributeDefinitionDefault {
                attribute_name: "SGEnumAttribute".to_string(),
                attribute_value: AttributeValue::String(CharString("Val0".to_string()))
            })
            .to_string(),
            r#"BA_DEF_DEF_ "SGEnumAttribute" "Val0";"#
        );
    }

    #[test]
    fn test_attribute_default_string_03() {
        assert_eq!(
            AttributeDefault::Attribute(AttributeDefinitionDefault {
                attribute_name: "GlobalEnvVar_Val".to_string(),
                attribute_value: AttributeValue::Double(288.0)
            })
            .to_string(),
            r#"BA_DEF_DEF_ "GlobalEnvVar_Val" 288;"#
        );
    }

    #[test]
    fn test_attribute_default_string_04() {
        assert_eq!(
            AttributeDefault::Attribute(AttributeDefinitionDefault {
                attribute_name: "FloatAttribute".to_string(),
                attribute_value: AttributeValue::Double(25.25)
            })
            .to_string(),
            r#"BA_DEF_DEF_ "FloatAttribute" 25.25;"#
        );
    }

    #[test]
    fn test_attribute_default_string_05() {
        assert_eq!(
            AttributeDefault::RelationAttribute(RelationAttributeDefinitionDefault {
                attribute_name: "ControlUnitEnvVarAttr".to_string(),
                attribute_value: AttributeValue::String(CharString("MyVar".to_string()))
            })
            .to_string(),
            r#"BA_DEF_DEF_REL_ "ControlUnitEnvVarAttr" "MyVar";"#
        );
    }

    #[test]
    fn test_parser_attribute_definition_default_01() {
        assert_eq!(
            parser_attribute_definition_default(r#"BA_DEF_DEF_ "attribute_name" 0;"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "attribute_name".to_string(),
                    attribute_value: AttributeValue::Double(0.0)
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_01() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_ "attribute_name" 0;"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "attribute_name".to_string(),
                    attribute_value: AttributeValue::Double(0.0)
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_02() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_REL_ "ControlUnitEnvVarAttr" "MyVar";"#),
            Ok((
                "",
                AttributeDefault::RelationAttribute(RelationAttributeDefinitionDefault {
                    attribute_name: "ControlUnitEnvVarAttr".to_string(),
                    attribute_value: AttributeValue::String(CharString("MyVar".to_string()))
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_03() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_  "RWEnvVar_wData_Val" 5;"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "RWEnvVar_wData_Val".to_string(),
                    attribute_value: AttributeValue::Double(5.0)
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_04() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_  "GlobalEnvVar_Val" 288;"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "GlobalEnvVar_Val".to_string(),
                    attribute_value: AttributeValue::Double(288.0)
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_05() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_  "SGEnumAttribute" "Val0";"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "SGEnumAttribute".to_string(),
                    attribute_value: AttributeValue::String(CharString("Val0".to_string()))
                })
            ))
        );
    }

    #[test]
    fn test_parser_attribute_default_06() {
        assert_eq!(
            parser_attribute_default(r#"BA_DEF_DEF_  "FloatAttribute" 25.25;"#),
            Ok((
                "",
                AttributeDefault::Attribute(AttributeDefinitionDefault {
                    attribute_name: "FloatAttribute".to_string(),
                    attribute_value: AttributeValue::Double(25.25)
                })
            ))
        );
    }
}