Documentation
mod info;
mod input;
mod output;

use clap::{App, Arg, SubCommand};
use rust_embed::RustEmbed;
use serde::Deserialize;
use std::fs::File;
use std::io::prelude::*;

#[derive(Deserialize, Debug)]
struct InputConfig {
    svd_filename: Option<String>,
    yaml_filename: Option<String>,
    peripheral: String,
}

#[derive(Deserialize, Debug)]
struct OutputConfig {
    rust: Option<output::RustConfig>,
    cpp: Option<output::CppConfig>,
    json: Option<output::JsonConfig>,

    #[serde(default)]
    print_stdout: bool,
}

#[derive(Deserialize, Debug)]
struct Operation {
    input: InputConfig,
    output: OutputConfig,
}

#[derive(RustEmbed)]
#[folder = "$CARGO_MANIFEST_DIR/src/templates/rust/"]
struct Templates;

fn generate_cargo() -> String {
    let mut tera = tera::Tera::default();
    tera.add_raw_template(
        "Cargo.toml",
        std::str::from_utf8(&Templates::get("Cargo.toml").unwrap()).unwrap(),
    )
    .unwrap();
    tera.render("Cargo.toml", &tera::Context::new()).unwrap()
}

fn render_template(path: &str, source: &str, target: &str) {
    let mut tera = tera::Tera::default();
    tera.add_raw_template(
        source,
        std::str::from_utf8(&Templates::get(source).unwrap()).unwrap(),
    )
    .unwrap();
    let content = tera.render(source, &tera::Context::new()).unwrap();

    let mut file = std::fs::File::create(&format!("{}/{}", path, target)).unwrap();
    //let cargo_toml = generate_cargo();
    file.write_all(content.as_bytes()).unwrap();
}

fn main() {
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();

    let matches = App::new("Register Map Accessor Generator")
        .arg(
            Arg::with_name("path")
                .short("p")
                .long("path")
                .takes_value(true)
                .required(true)
                .help("Path to generate the crate into"),
        )
        .get_matches();

    let path = matches.value_of("path").unwrap();

    std::fs::create_dir(&path).unwrap();
    std::fs::create_dir(&format!("{}/src", path)).unwrap();

    render_template(path, "Cargo.toml.tera", "Cargo.toml");
    render_template(path, "lib.rs.tera", "src/lib.rs");
    render_template(path, "build.rs.tera", "build.rs");
    render_template(path, "regmap.yaml.tera", "src/regmap.yaml");

    return;

    let matches = App::new("Register Map Accessor Generator")
        .arg(
            Arg::with_name("input")
                .short("i")
                .long("input")
                .takes_value(true)
                .help("Input configuration file to process"),
        )
        .arg(
            Arg::with_name("input-peripheral")
                .short("f")
                .long("input-file")
                .takes_value(true)
                .help("Input YAML peripheral file to process"),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .takes_value(true)
                .help("Output configuration file for register map output."),
        )
        .arg(
            Arg::with_name("operation")
                .short("c")
                .long("operation")
                .takes_value(true)
                .help("Operation config file."),
        )
        .get_matches();

    let operations = std::fs::read_to_string(matches.value_of("operation").unwrap()).unwrap();
    let operations: Vec<Operation> = serde_yaml::from_str(&operations).unwrap();

    for operation in operations.iter() {
        /*let periph = if let Some(filename) = matches.value_of("input") {
            let input = std::fs::read_to_string(matches.value_of("input").unwrap()).unwrap();
            let input: InputConfig = serde_yaml::from_str(&input).unwrap();
            if let Some(filename) = input.svd_filename {
                input::parse_svd(&filename, &input.peripheral, &input::svd::Config {})
            } else if let Some(filename) = input.yaml_filename {
                input::parse_yaml(&filename, &input.peripheral, &input::yaml::Config {})
            } else {
                panic!("Must have an input!");
            }
        } else {
            input::parse_yaml(
                &matches.value_of("input-peripheral").unwrap(),
                "flawn",
                &input::yaml::Config {},
            )
        };*/
        /*let periph = if let Some(filename) = &operation.input.svd_filename {
            input::parse_svd(&filename, &operation.input.peripheral, &input::svd::Config {})
        } else if let Some(filename) = &operation.input.yaml_filename {
            input::parse_yaml(&filename, &operation.input.peripheral, &input::yaml::Config {})
        } else {
            panic!("Must have an input!");
        };*/
        let periphs = input::parse_yaml(
            &operation.input.yaml_filename.as_ref().unwrap(),
            &input::yaml::Config {},
        );

        let output = &operation.output;
        /*let output = if operation.output. == "-" {
            OutputConfig {
                cpp: None,
                json: None,
                rust: None,
                print_stdout: true,
            }
        } else {
            let output = std::fs::read_to_string(matches.value_of("output").unwrap()).unwrap();
            let output: OutputConfig = serde_yaml::from_str(&output).unwrap();
            output
        };*/

        /*let periph = if let Some(filename) = input.svd_filename {
            input::parse_svd(&filename, &input.peripheral, &input::svd::Config {})
        } else if let Some(filename) = input.yaml_filename {
            input::parse_yaml(&filename, &input::yaml::Config {})
        } else if let Some(filename) = matches.value_of("input-peripheral") {
            input::parse_yaml(&filename, &input::yaml::Config {})
        } else {
            panic!("Must have an input!");
        };*/

        if let Some(config) = &output.rust {
            output::generate_rust(config, &periphs);
        }

        /*if let Some(config) = &output.cpp {
            output::generate_cpp(config, &periph);
        }

        if let Some(config) = &output.json {
            output::generate_json(config, &periph);
        }*/

        if output.print_stdout {
            //let s = serde_yaml::to_string(&periph).unwrap();
            println!("{:#?}", periphs);
        }
    }
}