use lv_codegen::{CodeGen, Rusty};
use proc_macro2::TokenStream;
use quote::quote;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command;

fn main() {
    println!("cargo::rustc-check-cfg=cfg(LV_USE_GRID)");
    println!("cargo::rustc-check-cfg=cfg(LV_USE_LOG)");
    println!("cargo::rustc-check-cfg=cfg(LV_USE_STDLIB_MALLOC, values(\"BUILTIN\", \"CLIB\"))");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    let rs = out_path.join("generated.rs");

    let source = lightvgl_sys::_bindgen_raw_src();

    let codegen = CodeGen::from(source).unwrap();
    let widgets_impl: Vec<TokenStream> = codegen
        .get_widgets()
        .iter()
        .flat_map(|w| w.code(&()))
        .collect();

    let code = quote! {
        use ::core::ptr::NonNull;
        use ::core::any::Any;
        #[allow(unused_imports)]
        use ::alloc::ffi::CString;
        use constptr::ConstPtr;
        use lightvgl_sys::*;

        #(#widgets_impl)*
    };

    let mut file = File::create(&rs).unwrap();
    writeln!(
        file,
        "/* automatically generated by lv_codegen */\n{}",
        code
    )
    .unwrap();

    rustfmt_path()
        .and_then(|rustfmt_path| {
            Command::new(&rustfmt_path)
                .arg(&rs)
                .arg("--config")
                .arg("max_width=200")
                .spawn()
                .ok()
        })
        .and_then(|mut cmd| cmd.wait().ok());

    let widgets_path = out_path.join("widgets.rs");
    let widgets_impl: Vec<TokenStream> = codegen
        .get_widgets()
        .iter()
        .flat_map(|w| w.gen_impl())
        .collect();
    let widgets_code = quote! {
        #(#widgets_impl)*
    };
    let mut widgets_file = File::create(&widgets_path).unwrap();
    writeln!(
        widgets_file,
        "/* automatically generated by lv_codegen */\n{}",
        widgets_code
    )
    .unwrap();

    rustfmt_path()
        .and_then(|rustfmt_path| {
            Command::new(&rustfmt_path)
                .arg(&widgets_path)
                .arg("--config")
                .arg("max_width=200")
                .spawn()
                .ok()
        })
        .and_then(|mut cmd| cmd.wait().ok());

    // this check is needed to enable lv_grid_fr wrapper
    if lightvgl_sys::LV_USE_GRID != 0 {
        println!("cargo::rustc-cfg=LV_USE_GRID");
    }

    // this check is needed to enable logging functions
    if lightvgl_sys::LV_USE_LOG != 0 {
        println!("cargo::rustc-cfg=LV_USE_LOG");
    }

    // this check is needed to warn of segmentation fault
    match lightvgl_sys::LV_USE_STDLIB_MALLOC {
        lightvgl_sys::LV_STDLIB_BUILTIN => {
            println!("cargo::rustc-cfg=LV_USE_STDLIB_MALLOC=\"BUILTIN\"");
        }
        lightvgl_sys::LV_STDLIB_CLIB => {
            println!("cargo::rustc-cfg=LV_USE_STDLIB_MALLOC=\"CLIB\"");
        }
        _ => {}
    }
}

fn rustfmt_path() -> Option<PathBuf> {
    if let Ok(rustfmt) = env::var("RUSTFMT") {
        return Some(PathBuf::from(rustfmt));
    } else if let Ok(rustfmt) = which::which("rustfmt") {
        return Some(rustfmt);
    }
    None
}
