use dotnetdll::prelude::*;

#[macro_use]
mod common;

#[test]
fn parse() -> Result<(), Box<dyn std::error::Error>> {
    let file = std::fs::read(common::env::LIBRARIES.join("System.Private.CoreLib.dll"))?;
    let r = Resolution::parse(&file, ReadOptions::default())?;

    if let Some(e) = &r.entry_point {
        print!("assembly entry point: ");
        match e {
            EntryPoint::Method(m) => println!("{}", UserMethod::Definition(*m).show(&r)),
            EntryPoint::File(f) => println!("external file {}", r[*f].name),
        }
    }

    for e in &r.exported_types {
        if let TypeImplementation::TypeForwarder(a) = e.implementation {
            println!("re-exports {} from {}", e.type_name(), r[a].name);
        }
    }

    for t in &r.type_definitions {
        println!("{} {{", t.show(&r));

        for f in &t.fields {
            println!("\t{};", f.show(&r));
        }
        for p in &t.properties {
            println!("\t{};", p.show(&r));
        }

        for m in &t.methods {
            print!("\t{}", m.show(&r));

            if let Some(b) = &m.body {
                println!(" {{");

                if b.header.initialize_locals {
                    println!("\t\tinit locals");
                }
                println!("\t\tmaximum stack size {}", b.header.maximum_stack_size);
                let locals = &b.header.local_variables;
                if !locals.is_empty() {
                    println!("\t\tlocal variables:");

                    let max_size = ((locals.len() - 1) as f32).log10().ceil() as usize;

                    for (idx, v) in locals.iter().enumerate() {
                        println!("\t\t\t{:1$}: {2}", idx, max_size, v.show(&r));
                    }
                }

                let max_size = ((b.instructions.len() - 1) as f32).log10().ceil() as usize;

                println!("\t\t---");

                for (idx, instr) in b.instructions.iter().enumerate() {
                    println!("\t\t{:1$}: {2}", idx, max_size, instr.show(&r));
                }

                println!("\t}}");
            } else {
                println!(";");
            }
        }

        println!("}}\n");
    }

    Ok(())
}
