use itertools::Itertools;
use std::fmt;

use crate::{arg, assembler};

#[derive(Debug, Clone)]
pub enum Node {
    Comment(String),
    Label(String),
    Call { name: String, arg: Option<arg::Arg> },
    MacroCall { name: String, args: Vec<arg::Arg> },
}

impl fmt::Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Node::Comment(x) => write!(f, "; {}", x),
            Node::Label(x) => write!(f, "{}:", x),
            Node::Call { name, arg } => {
                if let Some(a) = arg {
                    write!(f, "{} {}", name, a)
                } else {
                    write!(f, "{}", name)
                }
            }
            Node::MacroCall { name, args } => {
                if args.is_empty() {
                    write!(f, ".{}", name)
                } else {
                    write!(f, ".{} {}", name, args.iter().join(", "))
                }
            }
        }
    }
}

impl assembler::ToCode for Node {}

pub type Body = Vec<Node>;

#[derive(Debug, Clone)]
pub struct AST {
    pub body: Vec<Node>,
}

impl fmt::Display for AST {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", itertools::free::join(&self.body, "\n"))
    }
}

impl assembler::ToCode for AST {}
