extern crate alloc;
extern crate core;

pub mod android_library;
pub mod android_loader;
pub mod hook_manager;
mod relocation_types;

pub use sysv64::sysv64;

#[cfg(all(target_family = "windows", target_arch = "x86_64"))]
pub use sysv64::sysved64_type as sysv64_type;

#[cfg(not(all(target_family = "windows", target_arch = "x86_64")))]
pub use sysv64::sysvno64_type as sysv64_type;

#[cfg(test)]
mod tests {
    use libc::{
        chmod, close, free, fstat, ftruncate, gettimeofday, lstat, malloc, mkdir, open, read,
        strncpy, umask, write,
    };
    use rand::Rng;
    use std::alloc::Layout;
    use std::collections::HashMap;
    use std::ffi::CString;
    use std::os::raw::c_char;

    use crate::android_library::AndroidLibrary;
    use crate::{sysv64, sysv64_type};

    #[sysv64]
    fn arc4random() -> u32 {
        rand::thread_rng().gen()
    }

    #[test]
    fn load_android_libraries() {
        let mut hooks = HashMap::new();
        hooks.insert("arc4random".to_owned(), arc4random as usize);
        hooks.insert("chmod".to_owned(), chmod as usize);
        hooks.insert("close".to_owned(), close as usize);
        hooks.insert("free".to_owned(), free as usize);
        hooks.insert("fstat".to_owned(), fstat as usize);
        hooks.insert("ftruncate".to_owned(), ftruncate as usize);
        hooks.insert("gettimeofday".to_owned(), gettimeofday as usize);
        hooks.insert("lstat".to_owned(), lstat as usize);
        hooks.insert("malloc".to_owned(), malloc as usize);
        hooks.insert("mkdir".to_owned(), mkdir as usize);
        hooks.insert("open".to_owned(), open as usize);
        hooks.insert("read".to_owned(), read as usize);
        hooks.insert("strncpy".to_owned(), strncpy as usize);
        hooks.insert("umask".to_owned(), umask as usize);
        hooks.insert("write".to_owned(), write as usize);
        crate::hook_manager::add_hooks(hooks);

        let store_services_core = AndroidLibrary::load(
            "/workspaces/storeservices-wrapper/android/lib/arm64-v8a/libstoreservicescore.so",
        )
        .expect("Cannot load StoreServicesCore");

        println!("Library loaded. Let's start.");
        let load_library_with_path: sysv64_type!(fn(*const c_char) -> i32) =
            unsafe { std::mem::transmute(store_services_core.get_symbol("kq56gsgHG6").unwrap()) }; // Sph98paBcz abort
        let fairplay_load_library_with_path: sysv64_type!(fn(*const c_char) -> i32) =
            unsafe { std::mem::transmute(store_services_core.get_symbol("N8jdR29h").unwrap()) }; // Sph98paBcz abort

        let library_path =
            CString::new("/workspaces/storeservices-wrapper/android/lib/arm64-v8a/").unwrap();
        let ret = load_library_with_path(library_path.as_ptr() as *const c_char);
        println!("provisioning path, ADI returned {}", ret);

        let retf = fairplay_load_library_with_path(library_path.as_ptr() as *const c_char);
        println!("provisioning path, FP returned {}", retf);

        let set_android_identifier: sysv64_type!(fn(*const c_char, u32) -> i32) =
            unsafe { std::mem::transmute(store_services_core.get_symbol("Sph98paBcz").unwrap()) }; // Sph98paBcz abort

        let set_fp_identifier: sysv64_type!(fn(*const c_char, u32) -> i32) =
            unsafe { std::mem::transmute(store_services_core.get_symbol("bsawCXd").unwrap()) }; // Sph98paBcz abort

        let fairplay_get_guid_fields: sysv64_type!(fn(out_guid: *mut u8, flag: u32) -> i32) =
            unsafe { std::mem::transmute(store_services_core.get_symbol("QHioSBsQR").unwrap()) }; // Sph98paBcz abort

        let identifier = "f213456789abcde0";
        let str = CString::new(identifier).unwrap();
        let len = identifier.len() as u32;
        let ret = set_android_identifier(str.as_ptr() as *const c_char, len);
        println!("Fin ? ADI returned {}", ret);

        let ret2 = set_fp_identifier(str.as_ptr() as *const c_char, len);
        println!("Fin ? ADI2 returned {}", ret2);

        // Print the GUID
        let layout: Layout = Layout::from_size_align(1, 1).unwrap();
        let guid_ptr: *mut u8 = unsafe { alloc::alloc::alloc(layout) };
        unsafe { *guid_ptr = 0 };

        let ret3 = fairplay_get_guid_fields(guid_ptr, 2);

        let mut guid = Vec::new();

        let mut i = 0;
        while unsafe { *guid_ptr.offset(i) } != 0 {
            guid.push(unsafe { *guid_ptr.offset(i) });
            i += 1;
        }

        println!("Fin ? ADI3 returned {}", ret3);
    }
}
