mod masm;
mod mast;
use midenc_hir::{
    self as hir,
    pass::{RewritePass, RewriteSet},
};
use midenc_session::{diagnostics::Report, Session};
pub use self::{masm::MasmArtifact, mast::MastArtifact};
use crate::{intrinsics, ConvertHirToMasm, Program};
pub type CompilerResult<T> = Result<T, Report>;
pub struct MasmCompiler<'a> {
    session: &'a Session,
    analyses: hir::pass::AnalysisManager,
}
impl<'a> MasmCompiler<'a> {
    pub fn new(session: &'a Session) -> Self {
        Self {
            session,
            analyses: hir::pass::AnalysisManager::new(),
        }
    }
    pub fn compile(&mut self, mut input: Box<hir::Program>) -> CompilerResult<MasmArtifact> {
        use midenc_hir::pass::ConversionPass;
        let mut rewrites = default_rewrites([], self.session);
        let modules = input.modules_mut().take();
        for mut module in modules.into_iter() {
            rewrites.apply(&mut module, &mut self.analyses, self.session)?;
            input.modules_mut().insert(module);
        }
        let mut convert_to_masm = ConvertHirToMasm::<hir::Program>::default();
        let mut artifact = convert_to_masm.convert(input, &mut self.analyses, self.session)?;
        artifact.insert(Box::new(
            intrinsics::load("intrinsics::mem", &self.session.source_manager)
                .expect("undefined intrinsics module"),
        ));
        artifact.insert(Box::new(
            intrinsics::load("intrinsics::i32", &self.session.source_manager)
                .expect("undefined intrinsics module"),
        ));
        artifact.insert(Box::new(
            intrinsics::load("intrinsics::i64", &self.session.source_manager)
                .expect("undefined intrinsics module"),
        ));
        Ok(artifact)
    }
    pub fn compile_module(&mut self, input: Box<hir::Module>) -> CompilerResult<Box<Program>> {
        assert!(input.entrypoint().is_some(), "cannot compile a program without an entrypoint");
        let program =
            hir::ProgramBuilder::new(&self.session.diagnostics).with_module(input)?.link()?;
        match self.compile(program)? {
            MasmArtifact::Executable(program) => Ok(program),
            _ => unreachable!("expected compiler to produce an executable, got a library"),
        }
    }
    pub fn compile_modules<I: IntoIterator<Item = Box<hir::Module>>>(
        &mut self,
        input: I,
    ) -> CompilerResult<Box<Program>> {
        let mut builder = hir::ProgramBuilder::new(&self.session.diagnostics);
        for module in input.into_iter() {
            builder.add_module(module)?;
        }
        let program = builder.link()?;
        assert!(program.has_entrypoint(), "cannot compile a program without an entrypoint");
        match self.compile(program)? {
            MasmArtifact::Executable(program) => Ok(program),
            _ => unreachable!("expected compiler to produce an executable, got a library"),
        }
    }
}
pub fn default_rewrites<P>(registered: P, session: &Session) -> RewriteSet<hir::Module>
where
    P: IntoIterator<Item = Box<dyn RewritePass<Entity = hir::Module>>>,
    <P as IntoIterator>::IntoIter: ExactSizeIterator,
{
    use midenc_hir::pass::ModuleRewritePassAdapter;
    let registered = registered.into_iter();
    let mut rewrites = RewriteSet::default();
    if registered.len() == 0 {
        if session.should_codegen() {
            let fn_rewrites = default_function_rewrites(session);
            for rewrite in fn_rewrites {
                rewrites.push(ModuleRewritePassAdapter::new(rewrite));
            }
        }
    } else {
        rewrites.extend(registered);
    }
    rewrites
}
pub fn default_function_rewrites(session: &Session) -> RewriteSet<hir::Function> {
    use midenc_hir_transform as transforms;
    let mut rewrites = RewriteSet::default();
    if session.should_codegen() {
        rewrites.push(transforms::SplitCriticalEdges);
        rewrites.push(transforms::Treeify);
        rewrites.push(transforms::InlineBlocks);
        rewrites.push(transforms::ApplySpills);
    }
    rewrites
}