use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};

fn main() -> Result<(), Box<dyn Error>> {
    // resolve the current working antlr into a fully qualified path
    let cwd = env::current_dir().unwrap();
    let grammars = vec!["Hamelin", "TrinoType"];
    let antlr_path = cwd.join("bin").join("antlr4-4.8-2-SNAPSHOT-complete.jar");

    for grammar in grammars.into_iter() {
        //ignoring error because we do not need to run anything when deploying to crates.io
        gen_for_grammar(grammar, &antlr_path)?;
    }

    println!("cargo:rerun-if-changed=build.rs");

    println!("cargo:rerun-if-changed={}", antlr_path.display());
    Ok(())
}

fn gen_for_grammar(grammar_file_name: &str, antlr_path: &PathBuf) -> Result<(), Box<dyn Error>> {
    let out_dir = env::var("OUT_DIR")?;
    let dest_path = Path::new(&out_dir);
    // Print the path to easily find it in the Cargo output
    // uncomment this if you're looking to find the stupid shit this thing generates
    // println!("cargo:warning=OUT_DIR is {}", out_dir);

    let input = env::current_dir()?.join("grammars");
    let file_name = grammar_file_name.to_owned() + ".g4";
    let grammar_source = input.join(&file_name);

    // Read the grammar file and apply the transformation
    let grammar_content = fs::read_to_string(&grammar_source)?;
    // ANTLR Rust has panic related challenges with hidden channels. Fuck off, then.
    let transformed_content = grammar_content.replace("-> channel(HIDDEN)", "-> skip");

    // Write the transformed grammar to a temp file in OUT_DIR
    let temp_grammar = dest_path.join(&file_name);
    fs::write(&temp_grammar, transformed_content)?;

    let out_files = vec![
        dest_path.join(grammar_file_name.to_lowercase() + "lexer.rs"),
        dest_path.join(grammar_file_name.to_lowercase() + "parser.rs"),
        dest_path.join(grammar_file_name.to_lowercase() + "visitor.rs"),
        dest_path.join(grammar_file_name.to_lowercase() + "listener.rs"),
    ];

    let mut c = Command::new("java");
    // What the fuck, Rust
    c.current_dir(dest_path)
        .arg("-jar")
        .arg(antlr_path)
        .arg("-Dlanguage=Rust")
        .arg("-o")
        .arg(dest_path)
        .arg(&file_name)
        .arg("-visitor");

    eprintln!("Running command: {:?}", c);

    c.spawn()?.wait_with_output()?;

    for file in out_files {
        eprintln!("reading {}", file.display());
        let content = fs::read_to_string(file.clone())?;
        eprintln!("opening for write {}", file.display());
        let mut out = File::create(file)?;
        for line in content.lines() {
            if !line.starts_with("#![allow") {
                writeln!(out, "{}", line)?;
            }
        }
    }

    println!("cargo:rerun-if-changed=grammars/{}", file_name);

    Ok(())
}
