use std::{
    collections::{btree_map::Entry, BTreeMap},
    fmt::{Debug, Display},
    path::{Path, PathBuf},
};
use crate::{error::munyo_error::PathItem, lang::processed::Processed, read_file};
use super::builder::{Builder, MetaBuilder};
#[derive(Debug, Clone, Default)]
pub struct DefaultMetaBuilder;
impl MetaBuilder for DefaultMetaBuilder {
    type Item = DefaultBuilder;
    fn build(&self, typename: String, argument: String) -> Result<Self::Item, String> {
        Ok(DefaultBuilder::new(typename, argument))
    }
}
#[derive(Debug)]
pub struct DefaultBuilder {
    pub(crate) typename: String,
    pub(crate) argument: String,
    pub(crate) params: BTreeMap<String, String>,
    pub(crate) children: Vec<MunyoItem>,
}
impl DefaultBuilder {
    pub fn new(typename: String, argument: String) -> Self {
        Self {
            typename,
            argument,
            params: BTreeMap::new(),
            children: vec![],
        }
    }
}
impl Builder for DefaultBuilder {
    type Item = MunyoItem;
    fn set_param(&mut self, param_name: String, argument: String) -> Result<(), String> {
        match self.params.entry(param_name) {
            Entry::Occupied(e) => {
                return Err(format!("'{}' is applied multiple times", e.key()));
            }
            Entry::Vacant(e) => {
                e.insert(argument);
            }
        }
        Ok(())
    }
    fn set_child(&mut self, child: Self::Item) -> Result<(), String> {
        self.children.push(child);
        Ok(())
    }
    fn finish(self) -> Result<Self::Item, String> {
        Ok(Self::Item {
            typename: self.typename,
            argument: self.argument,
            params: self.params,
            children: self.children,
        })
    }
}
#[derive(Clone, Default, PartialEq)]
pub struct MunyoItem {
	pub typename: String,
	pub argument: String,
	pub params: BTreeMap<String, String>,
	pub children: Vec<MunyoItem>,
}
impl Debug for MunyoItem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write_item(self, 0, f)
    }
}
impl Display for MunyoItem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write_item(self, 0, f)
    }
}
fn write_item(
    item: &MunyoItem,
    indent_level: usize,
    f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
    for _ in 0..indent_level {
        write!(f, "\t")?;
    }
    write!(f, "{}", item_format(&item.typename, &item.argument))?;
    for (key, val) in &item.params {
        write!(f, "|{}", item_format(key, val))?;
    }
    for child in &item.children {
        writeln!(f)?;
        write_item(child, indent_level + 1, f)?;
    }
    Ok(())
}
fn item_format(name: &str, val: &str) -> String {
    if val.is_empty() {
        name.to_string()
    } else {
        format!("{} {}", name, val)
    }
}
impl MunyoItem {
    pub fn from_str_with_path<P: AsRef<Path>>(
        s: &str,
        path: P,
    ) -> crate::Result<Processed<MunyoItem>> {
        Self::inner(s, Some(path.as_ref().to_path_buf()))
    }
    pub fn from_str(s: &str) -> crate::Result<Processed<MunyoItem>> {
        Self::inner(s, None)
    }
    pub fn from_file<P: AsRef<Path>>(path: P) -> crate::Result<Processed<MunyoItem>> {
        let buf = path.as_ref().to_path_buf();
        let s = read_file(&buf)?;
        Self::inner(&s, Some(buf))
    }
    fn inner(s: &str, path: Option<PathBuf>) -> crate::Result<Processed<MunyoItem>> {
        crate::from_str_with_metabuilder(s, &DefaultMetaBuilder)
            .map_err(|e| crate::Error::Parse(PathItem::new(path), e))
    }
}