use std::fmt;
use semver::Version;
use crate::{ident::Ident, Interface, Render, RenderOpts, World};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Package {
    name: PackageName,
    items: Vec<PackageItem>,
}
impl Package {
    pub fn new(name: PackageName) -> Self {
        Self {
            name,
            items: vec![],
        }
    }
    pub fn interface(&mut self, interface: Interface) {
        self.items.push(PackageItem::Interface(interface))
    }
    pub fn world(&mut self, world: World) {
        self.items.push(PackageItem::World(world))
    }
}
impl Render for Package {
    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
        write!(f, "{}package {};\n", opts.spaces(), self.name)?;
        write!(f, "\n")?;
        for item in &self.items {
            match item {
                PackageItem::Interface(interface) => {
                    if let Some(docs) = &interface.docs {
                        docs.render(f, opts)?;
                    }
                    write!(f, "{}interface {} {{\n", opts.spaces(), interface.name)?;
                    interface.items.render(f, &opts.indent())?;
                    write!(f, "{}}}\n", opts.spaces())?;
                }
                PackageItem::World(world) => {
                    world.render(f, opts)?;
                }
            }
        }
        Ok(())
    }
}
impl fmt::Display for Package {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.render(f, &RenderOpts::default())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PackageItem {
    Interface(Interface),
    World(World),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PackageName {
    namespace: String,
    name: Ident,
    version: Option<Version>,
}
impl PackageName {
    pub fn new(
        namespace: impl Into<String>,
        name: impl Into<Ident>,
        version: Option<Version>,
    ) -> Self {
        Self {
            namespace: namespace.into(),
            name: name.into(),
            version,
        }
    }
}
impl From<PackageName> for String {
    fn from(name: PackageName) -> String {
        name.to_string()
    }
}
impl fmt::Display for PackageName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.namespace, self.name)?;
        if let Some(version) = &self.version {
            write!(f, "@{version}")?;
        }
        Ok(())
    }
}