use super::types::Rule;
use std::fmt::{Display, Formatter};

impl Display for Rule {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Rule::Empty => write!(f, "()"),
            Rule::Any => write!(f, "."),
            Rule::Literal(text) => write!(
                f,
                "'{}'",
                text.replace('\\', "\\\\")
                    .replace('\n', "\\n")
                    .replace('\'', "\\'")
            ),
            Rule::NonTerminal(name) => write!(f, "{}", name),
            Rule::Range(start, end) => write!(f, "[{}-{}]", start, end),
            Rule::Class(symbols) => write!(
                f,
                "[{}]",
                symbols
                    .iter()
                    .map(|s| s
                        .replace('\\', "\\\\")
                        .replace('[', "\\[")
                        .replace(']', "\\]"))
                    .collect::<Vec<_>>()
                    .join("")
            ),
            Rule::Group(expr) => write!(f, "({})", expr),
            Rule::ZeroOrMore(expr) => write!(f, "{}*", expr),
            Rule::OneOrMore(expr) => write!(f, "{}+", expr),
            Rule::Optional(expr) => write!(f, "{}?", expr),
            Rule::And(expr) => write!(f, "&{}", expr),
            Rule::Not(expr) => write!(f, "!{}", expr),
            Rule::Choice(choices) => {
                let joined = choices
                    .iter()
                    .map(|c| c.to_string())
                    .collect::<Vec<_>>()
                    .join(" / ");
                write!(
                    f,
                    "{}",
                    if choices.len() > 1 {
                        format!("({})", joined)
                    } else {
                        joined
                    }
                )
            }
            Rule::Sequence(sequence) => {
                let joined = sequence
                    .iter()
                    .map(|c| c.to_string())
                    .collect::<Vec<_>>()
                    .join(" ");
                write!(
                    f,
                    "{}",
                    if sequence.len() > 1 {
                        format!("({})", joined)
                    } else {
                        joined
                    }
                )
            }
        }
    }
}
