use std::sync::Arc;

use mers_lib::{
    data::{Data, MersDataWInfo, Type},
    errors::CheckError,
    prelude_compile::{parse, Config, Source},
    program::parsed::CompInfo,
};

fn main() {
    show("1.sum(2)".to_owned());
    show("1.sum(2).println".to_owned());
    show("1.sum(2.5)".to_owned());
    show("if true { 1 } else { 0.5 }".to_owned());
}

/// Tries to parse, compile, check and run `src`,
/// then prints an error or the returned value and output type to stderr.
/// Note: The output type is not the type of the value but the one determined by `.check()` before the code even runs.
fn show(src: String) {
    eprintln!(
        "-{}",
        " -".repeat(src.lines().map(|l| l.len()).max().unwrap_or(0) / 2)
    );
    eprintln!("{src}");
    match parse_compile_check_run(src) {
        Err(e) => eprintln!("{e:?}"),
        Ok((t, v, i)) => eprintln!(
            "Returned `{}` :: `{}`",
            v.get().with_info(&i),
            t.with_info(&i)
        ),
    }
}

fn parse_compile_check_run(
    src: String,
) -> Result<(Type, Data, mers_lib::program::run::Info), CheckError> {
    // prepare the string for parsing
    let mut source = Source::new_from_string(src);
    // this is used for error messages
    let srca = Arc::new(source.clone());
    // parse the code
    let parsed = parse(&mut source, &srca)?;
    // get infos
    let (mut i1, mut i2, mut i3) = Config::new().bundle_std().infos();
    // compile
    let compiled = parsed.compile(&mut i1, CompInfo::default())?;
    // check (this step is optional, but if it is skipped when it would have returned an error, `run` will likely panic)
    let output_type = compiled.check(&mut i3, None)?;
    // run
    let output_value = compiled.run(&mut i2)?;
    // check that the predicted output type was correct
    assert!(output_value.get().as_type().is_included_in(&output_type));
    // return the produced value
    Ok((output_type, output_value, i2))
}
