use crate::Dynarmic;
use std::ffi::{c_char, c_void};

pub trait SFHook {}

pub struct DyHook<'a, T: Clone + Send + Sync, F> {
    pub callback: F,
    pub dy: Dynarmic<'a, T>,
}

impl<'a, T: Clone + Send + Sync, F> SFHook for DyHook<'a, T, F> {}

extern "C" {
    pub fn dynarmic_version() -> u32;

    pub fn dynarmic_colorful_egg() -> *const c_char;

    pub fn dynarmic_init_memory() -> *mut c_void;

    pub fn dynarmic_init_monitor(processor_count: u32) -> *mut c_void;

    pub fn dynarmic_init_page_table() -> *mut *mut c_void;

    pub fn dynarmic_new(
        process_id: u32,
        memory: *mut c_void,
        monitor: *mut c_void,
        page_table: *mut *mut c_void,
        jit_size: u64,
        unsafe_optimizations: bool,
    ) -> *mut c_void;

    pub fn dynarmic_new_a32(
        process_id: u32,
        memory: *mut c_void,
        monitor: *mut c_void,
        page_table: *mut *mut c_void,
        jit_size: u64,
        unsafe_optimizations: bool,
        coprocessors: *const c_void,
    ) -> *mut c_void;

    pub fn dynarmic_get_cache_size(dynarmic: *mut c_void) -> u64;

    pub fn dynarmic_destroy(dynarmic: *mut c_void);

    pub fn dynarmic_set_svc_callback(
        dynarmic: *mut c_void,
        cb: extern "C" fn(swi: u32, user_data: *const c_void),
        user_data: *const c_void,
    );

    pub fn dynarmic_set_unmapped_mem_callback(
        dynarmic: *mut c_void,
        cb: extern "C" fn(addr: u64, size: usize, value: u64, user_data: *const c_void) -> bool,
        user_data: *const c_void,
    );

    pub fn dynarmic_munmap(dynarmic: *mut c_void, address: u64, size: u64) -> i32;

    pub fn dynarmic_mmap(dynarmic: *mut c_void, address: u64, size: u64, perms: i32) -> i32;

    pub fn dynarmic_mem_map_ptr(
        dynarmic: *mut c_void,
        address: u64,
        size: u64,
        perms: i32,
        ptr: *mut c_void,
    ) -> i32;

    pub fn dynarmic_mem_protect(dynarmic: *mut c_void, address: u64, size: u64, perms: i32) -> i32;

    pub fn dynarmic_mem_write(
        dynarmic: *mut c_void,
        address: u64,
        data: *const c_char,
        size: usize,
    ) -> i32;

    pub fn dynarmic_mem_read(
        dynarmic: *mut c_void,
        address: u64,
        bytes: *mut c_char,
        size: usize,
    ) -> i32;

    pub fn reg_read_pc(dynarmic: *mut c_void) -> u64;

    pub fn reg_write_pc(dynarmic: *mut c_void, value: u64) -> i32;

    pub fn reg_write_sp(dynarmic: *mut c_void, value: u64) -> i32;

    pub fn reg_read_sp(dynarmic: *mut c_void) -> u64;

    pub fn reg_read_nzcv(dynarmic: *mut c_void) -> u64;

    pub fn reg_write_nzcv(dynarmic: *mut c_void, value: u64) -> i32;

    pub fn reg_write_tpidr_el0(dynarmic: *mut c_void, value: u64) -> i32;

    pub fn reg_read_tpidr_el0(dynarmic: *mut c_void) -> u64;

    pub fn reg_write_tpidrr0_el0(dynarmic: *mut c_void, value: u64) -> i32;

    pub fn reg_read_tpidrr0_el0(dynarmic: *mut c_void) -> u64;

    #[allow(dead_code)]
    pub fn reg_write_vector(dynarmic: *mut c_void, index: u64, array: *mut u64) -> i32;

    #[allow(dead_code)]
    pub fn reg_read_vector(dynarmic: *mut c_void, index: u64, array: *mut u64) -> i32;

    pub fn reg_write(dynarmic: *mut c_void, index: u64, value: u64) -> i32;

    pub fn reg_read(dynarmic: *mut c_void, index: u64) -> u64;

    pub fn reg_write_r(dynarmic: *mut c_void, index: u32, value: u32) -> i32;
    pub fn reg_read_r(dynarmic: *mut c_void, index: u32) -> u32;
    pub fn reg_write_cpsr(dynarmic: *mut c_void, value: u32) -> i32;
    pub fn reg_read_cpsr(dynarmic: *mut c_void) -> u32;
    pub fn reg_write_c13_c0_3(dynarmic: *mut c_void, value: u32) -> i32;
    pub fn reg_read_c13_c0_3(dynarmic: *mut c_void) -> u32;

    pub fn dynarmic_emu_start(dynarmic: *mut c_void, pc: u64) -> i32;

    pub fn dynarmic_emu_stop(dynarmic: *mut c_void) -> i32;

    pub fn dynarmic_context_alloc() -> *mut c_void;

    pub fn dynarmic_context_free(context: *mut c_void);

    pub fn dynarmic_context_restore(dynarmic: *mut c_void, context: *mut c_void) -> i32;

    pub fn dynarmic_context_save(dynarmic: *mut c_void, context: *mut c_void) -> i32;

    pub fn dynarmic_context32_alloc() -> *mut c_void;
    pub fn dynarmic_context32_free(context: *mut c_void);
    pub fn dynarmic_context32_restore(dynarmic: *mut c_void, context: *mut c_void) -> i32;
    pub fn dynarmic_context32_save(dynarmic: *mut c_void, context: *mut c_void) -> i32;
}
