use anyhow::Result;
use clap::{Parser, Subcommand};
use std::fs;

use henceforth::*;

#[derive(Debug, Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
    #[clap(subcommand)]
    commands: Commands,
}

#[derive(Debug, Subcommand)]
enum Commands {
    #[clap(about = "Lexes source code and outputs tokens")]
    Lex {
        #[clap(value_parser)]
        src: String,
    },
    #[clap(about = "Parses source code and outputs AST")]
    Parse {
        #[clap(value_parser)]
        src: String,
    },
    #[clap(about = "Compiles assembly from source code")]
    Compile {
        #[clap(value_parser)]
        src: String,
        #[clap(value_parser)]
        out: Option<String>,
        #[clap(short, long, action)]
        optimize: Option<bool>,
        #[clap(long, action)]
        dump: bool,
    },
}

fn main() -> Result<()> {
    let args = Cli::parse();
    match args.commands {
        Commands::Lex { src } => {
            let source = fs::read_to_string(src)?;
            let tokens = lexer::lex(&source)?;
            println!("{:#?}", tokens);

            Ok(())
        }
        Commands::Parse { src } => {
            let source = fs::read_to_string(src)?;
            let tokens = lexer::lex(&source)?;
            let body = parser::parse(tokens)?;
            println!("{:#?}", body);

            Ok(())
        }
        Commands::Compile {
            src,
            out,
            optimize,
            dump,
        } => {
            let source = fs::read_to_string(&src)?;
            let tokens = lexer::lex(&source)?;
            let ast = parser::parse(tokens)?;
            let assembly = format!("{}\n", compiler::compile(ast, optimize.unwrap_or(true))?);

            if dump {
                print!("{}", assembly);
            }
            if let Some(x) = out {
                fs::write(x, &assembly)?;
            }

            Ok(())
        }
    }
}
