use std::io;

use crate::ast::{self as ast, HasName};

pub(super) const API_DECORATOR: &str = "MOLECULE_API_DECORATOR";

macro_rules! w {
    ($writer:ident, $( $args:tt )*) => {
        writeln!($writer, "{}", format!($( $args )*).trim_end())?;
    }
}

pub(super) trait IdentPrefix: HasName {
    fn reader_prefix(&self) -> String {
        format!("MolReader_{}", self.name())
    }

    fn builder_prefix(&self) -> String {
        format!("MolBuilder_{}", self.name())
    }

    fn default_constant(&self) -> String {
        format!("MolDefault_{}", self.name())
    }

    fn api_decorator(&self) -> &str {
        API_DECORATOR
    }

    fn define_reader_macro<W: io::Write>(
        &self,
        writer: &mut W,
        macro_sig_tail: &str,
        macro_content: &str,
    ) -> io::Result<()> {
        self.define_macro(true, writer, macro_sig_tail, macro_content)
    }

    fn define_builder_macro<W: io::Write>(
        &self,
        writer: &mut W,
        macro_sig_tail: &str,
        macro_content: &str,
    ) -> io::Result<()> {
        self.define_macro(false, writer, macro_sig_tail, macro_content)
    }

    fn define_reader_function<W: io::Write>(
        &self,
        writer: &mut W,
        func_sig_tail: &str,
        func_args: &str,
        func_ret: &str,
    ) -> io::Result<()> {
        self.define_function(true, writer, func_sig_tail, func_args, func_ret)
    }

    fn define_builder_function<W: io::Write>(
        &self,
        writer: &mut W,
        func_sig_tail: &str,
        func_args: &str,
        func_ret: &str,
    ) -> io::Result<()> {
        self.define_function(false, writer, func_sig_tail, func_args, func_ret)
    }

    fn define_macro<W: io::Write>(
        &self,
        is_reader: bool,
        writer: &mut W,
        macro_sig_tail: &str,
        macro_content: &str,
    ) -> io::Result<()> {
        let prefix = if is_reader {
            self.reader_prefix()
        } else {
            self.builder_prefix()
        };
        let macro_sig = format!("{}{}", prefix, macro_sig_tail);
        writeln!(
            writer,
            "{:39} {:47} {}",
            "#define", macro_sig, macro_content
        )
    }

    fn define_function<W: io::Write>(
        &self,
        is_reader: bool,
        writer: &mut W,
        func_sig_tail: &str,
        func_args: &str,
        func_ret: &str,
    ) -> io::Result<()> {
        let prefix = if is_reader {
            self.reader_prefix()
        } else {
            self.builder_prefix()
        };
        let func_name = format!("{}{}", prefix, func_sig_tail);
        writeln!(
            writer,
            "{:23} {:15} {:47} {};",
            self.api_decorator(),
            func_ret,
            func_name,
            func_args
        )
    }
}

impl IdentPrefix for ast::Option_ {}

impl IdentPrefix for ast::Union {}

impl IdentPrefix for ast::Array {}

impl IdentPrefix for ast::Struct {}

impl IdentPrefix for ast::FixVec {}

impl IdentPrefix for ast::DynVec {}

impl IdentPrefix for ast::Table {}

impl IdentPrefix for ast::TopDecl {}
