use {
    std::collections::HashMap,
    std::sync::{Arc, RwLock},
    std::{boxed::Box, ops::Deref},
};

mod expr;
mod lexer;
mod parser;
mod procedure_compiler;
mod runtime;
mod symbols;

use crate::{
    expr::{LTExpr, Statement},
    procedure_compiler::ProcedureCompiler,
    symbols::Scope,
};

fn compile(
    scope: &Arc<RwLock<Scope>>,
    name: &str,
    source: impl Iterator<Item = char>,
) -> Vec<tisc::assembler::AssemblyWord> {
    ProcedureCompiler::new(scope)
        .compile(
            &parser::parse_expr(
                &scope.read().unwrap().typectx,
                &mut lexer::LTIRLexer::from(source.peekable())
                    .filter(|tok| match tok {
                        (_, Ok(lexer::LTIRToken::Comment(_))) => false,
                        _ => true
                    })
                    .peekable(),
            )
            .expect("syntax error"),
        )
        .into_asm(&name.into())
}


/* TODO:
 *   - Parser error reporting
 *   - Compiler error reporting
 *   - write to address resulting from expression
 *   - sized objects
 *   - Typecheck for LTExpr::Application
 *   - typecheck & inference for rest
 */

fn main() {
    // create virtual machine with 4096 words of memory
    let mut vm = tisc::VM::new(0x1000);

    let mut linker = tisc::Linker::new();
    let root_scope = crate::runtime::init_runtime(&mut linker);
    let main_scope = Scope::with_parent(&root_scope);
    let typectx = main_scope.read().unwrap().typectx.clone();

    let args: Vec<String> = std::env::args().collect();
    let path = &args[1];
    let iter_chars = iterate_text::file::characters::IterateFileCharacters::new(path);

    /* link assembly-program to symbols
     */
    linker.add_procedure("main", compile(&main_scope, "main", iter_chars));

    /* load & run compiled bytecode
     */
    let main_addr = linker
        .get_link_addr(&"main".into())
        .expect("'main' not linked");
    vm.load(linker.link_total().expect("could not link"));
    vm.execute(main_addr);

    eprintln!(
        "\n====\nVM execution finished\ndatastack = {:?}\n====",
        vm.data_stack
    );
}