use std::ffi::{CStr, CString};
use std::io;
use std::os::raw::c_char;
use std::ptr;
use winapi;
use winapi::shared::minwindef::{BOOL, DWORD, HINSTANCE, LPVOID};
use winapi::um::consoleapi::AllocConsole;


macro_rules! export_var {
    ($($name:ident: $v:ty = $val:expr),*) => {
        $(#[no_mangle] pub static mut $name: $v = $val;)*
    }
}

/// Export variables for interfacing with the ASM code using the macro of above.
/// (basically I wanted a no_mangle scoped)
export_var!{
    array_start: usize = std::usize::MIN,
    _end: usize = 0,
    new_value: f64 = 0.,
    my_arr: [f64; 120] = [0f64; 120]
}

extern "C" {
    static get_values: u8;
}

/// Replaces an array of bytes for a jmp f
/// in order to hook our own code.
 fn hook_fun(dest: usize, f: *const u8, len: usize) {
    let nops = vec![0x90; len];
    let mut _t: DWORD = 0;

    unsafe { 
        let result = winapi::um::memoryapi::VirtualProtect(
            dest as LPVOID,
            len,
            winapi::um::winnt::PAGE_EXECUTE_READWRITE,
            &mut _t
        );


    }

    println!("{:?}", std::io::Error::last_os_error());
    let mut ptr = dest as *mut u8;
    // nop the stuff
    unsafe { 
        for n in nops {
            *ptr = n;
        }
    }

    let _f: isize = (f as isize);
    let _dest: isize = dest as isize;
    let r_addr: DWORD = (_f - _dest) as DWORD - 5;
    let mut ptr = dest as *mut u8;
    unsafe {
        *ptr = 0xE9; // jmp opcode
        let ptr = (dest + 1) as *mut DWORD; // target address
        *ptr = r_addr;
    }
}

#[no_mangle]
pub unsafe extern "system" fn intercept_input(_: LPVOID) -> DWORD {
    use winapi::um;
    // AllocConsole();

    let _name = CString::new("CHARTV.dll").unwrap();
    let mba = um::libloaderapi::GetModuleHandleA(_name.as_ptr()) as usize;
    
    // specific offset which may not work for every taskmgr.exe
    let target_addr = mba + 0x312E;

    let mut _b = String::new();
    unsafe {
        // _end is used by the asm injection to jmp back from our
        // injection to their own code.
        _end = target_addr + 5;
        hook_fun(target_addr, &get_values as *const u8, 5);
    }

    let _t = CString::new("Enjoy the waves!").unwrap();
    winapi::um::winuser::MessageBoxA(std::ptr::null_mut(), _t.as_ptr(), _t.as_ptr(), 0);
    println!("Enjoy the waves!");
    // dirty math to create sinusoidal chart
    let mut t = 0f64;
    loop {
            for i in 0..my_arr.len() {
                let _i = (i as f64)/10f64;
                my_arr[i] = 50f64*(1f64 + (t + _i).sin())
            }
            t += 1e-5;
            if t > 2.*3.14 {
                t = 0f64;
            }
    }
    
    // winapi::um::wincon::FreeConsole();

    return 1;
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn DllMain(_: HINSTANCE, reason: DWORD, _: LPVOID) -> BOOL {
    unsafe {
        match reason {
            winapi::um::winnt::DLL_PROCESS_ATTACH => {
                winapi::um::processthreadsapi::CreateThread(
                    ptr::null_mut(),
                    0,
                    Some(intercept_input),
                    ptr::null_mut(),
                    0,
                    ptr::null_mut(),
                );
            }
            _ => (),
        };
    }

    return true as BOOL;
}