//! Raw FFI bindings to the CP2K C API
//!
//! This module contains unsafe extern functions that directly map to the
//! functions provided by libcp2k.h. These should not be used directly
//! outside of this crate.

use std::os::raw::{c_char, c_double, c_int, c_long};

#[allow(non_camel_case_types)]
pub type force_env_t = c_int;

// We'll define the callback function type
#[allow(dead_code)]
pub type ExtMethodCallbackFPtr = extern "C" fn(
    cp2k_transport_parameters,
    cp2k_csr_interop_type,
    cp2k_csr_interop_type,
    *mut cp2k_csr_interop_type,
    *mut cp2k_csr_interop_type,
);

#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub struct cp2k_transport_parameters {
    pub n_occ: c_int,
    pub n_atoms: c_int,
    pub energy_diff: c_double,
    pub evoltfactor: c_double,
    pub e_charge: c_double,
    pub boltzmann: c_double,
    pub h_bar: c_double,
    pub iscf: c_int,
    pub method: c_int,
    pub qt_formalism: c_int,
    pub injection_method: c_int,
    pub rlaxis_integration_method: c_int,
    pub linear_solver: c_int,
    pub matrixinv_method: c_int,
    pub transport_neutral: c_int,
    pub num_pole: c_int,
    pub ordering: c_int,
    pub row_ordering: c_int,
    pub verbosity: c_int,
    pub pexsi_np_symb_fact: c_int,
    pub n_kpoint: c_int,
    pub num_interval: c_int,
    pub num_contacts: c_int,
    pub stride_contacts: c_int,
    pub tasks_per_energy_point: c_int,
    pub tasks_per_pole: c_int,
    pub gpus_per_point: c_int,
    pub n_points_beyn: c_int,
    pub ncrc_beyn: c_int,
    pub tasks_per_integration_point: c_int,
    pub n_points_inv: c_int,
    pub cutout: [c_int; 2],
    pub colzero_threshold: c_double,
    pub eps_limit: c_double,
    pub eps_limit_cc: c_double,
    pub eps_decay: c_double,
    pub eps_singularity_curvatures: c_double,
    pub eps_mu: c_double,
    pub eps_eigval_degen: c_double,
    pub eps_fermi: c_double,
    pub energy_interval: c_double,
    pub min_interval: c_double,
    pub temperature: c_double,
    pub dens_mixing: c_double,
    pub n_rand_beyn: c_double,
    pub n_rand_cc_beyn: c_double,
    pub svd_cutoff: c_double,
    pub contacts_data: *mut c_int,
    pub nsgf: *mut c_int,
    pub zeff: *mut c_double,
    pub obc_equilibrium: bool,
    pub extra_scf: bool,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub struct cp2k_csr_interop_type {
    pub nrows_total: c_int,
    pub ncols_total: c_int,
    pub nze_total: c_int,
    pub nze_local: c_int,
    pub nrows_local: c_int,
    pub data_type: c_int,
    pub first_row: c_int,
    pub rowptr_local: *mut c_int,
    pub colind_local: *mut c_int,
    pub nzerow_local: *mut c_int,
    pub nzvals_local: *mut c_double,
}

unsafe extern "C" {
    pub fn cp2k_get_version(version_str: *mut c_char, str_length: c_int);
    #[allow(dead_code)]
    pub fn cp2k_init();
    pub fn cp2k_init_without_mpi();
    #[allow(dead_code)]
    pub fn cp2k_finalize();
    pub fn cp2k_finalize_without_mpi();
    // MPI-aware initialization (checks if MPI already initialized)
    pub fn cp2k_init_with_mpi_check() -> c_int;
    pub fn cp2k_finalize_with_mpi_check() -> c_int;
    #[allow(dead_code)]
    pub fn cp2k_create_force_env(
        new_force_env: *mut force_env_t,
        input_file_path: *const c_char,
        output_file_path: *const c_char,
    );
    #[allow(dead_code)]
    pub fn cp2k_create_force_env_comm(
        new_force_env: *mut force_env_t,
        input_file_path: *const c_char,
        output_file_path: *const c_char,
        mpi_comm: c_int,
    );
    pub fn cp2k_destroy_force_env(force_env: force_env_t);
    pub fn cp2k_set_positions(force_env: force_env_t, new_pos: *const c_double, n_el: c_int);
    pub fn cp2k_set_velocities(force_env: force_env_t, new_vel: *const c_double, n_el: c_int);
    pub fn cp2k_set_cell(force_env: force_env_t, new_cell: *const c_double);
    pub fn cp2k_get_result(
        force_env: force_env_t,
        description: *const c_char,
        result: *mut c_double,
        n_el: c_int,
    );
    pub fn cp2k_get_natom(force_env: force_env_t, natom: *mut c_int);
    pub fn cp2k_get_nparticle(force_env: force_env_t, nparticle: *mut c_int);
    pub fn cp2k_get_positions(force_env: force_env_t, pos: *mut c_double, n_el: c_int);
    pub fn cp2k_get_forces(force_env: force_env_t, force: *mut c_double, n_el: c_int);
    pub fn cp2k_get_potential_energy(force_env: force_env_t, e_pot: *mut c_double);
    pub fn cp2k_get_cell(force_env: force_env_t, cell: *mut c_double);
    pub fn cp2k_get_qmmm_cell(force_env: force_env_t, cell: *mut c_double);
    pub fn cp2k_calc_energy_force(force_env: force_env_t);
    pub fn cp2k_calc_energy(force_env: force_env_t);
    pub fn cp2k_run_input(input_file_path: *const c_char, output_file_path: *const c_char);
    #[allow(dead_code)]
    pub fn cp2k_run_input_comm(
        input_file_path: *const c_char,
        output_file_path: *const c_char,
        mpi_comm: c_int,
    );
    #[allow(dead_code)]
    pub fn cp2k_transport_set_callback(force_env: force_env_t, func: Option<ExtMethodCallbackFPtr>);
    pub fn cp2k_active_space_get_mo_count(force_env: force_env_t) -> c_int;
    pub fn cp2k_active_space_get_fock_sub(
        force_env: force_env_t,
        buf: *mut c_double,
        buf_len: c_long,
    ) -> c_long;
    pub fn cp2k_active_space_get_eri_nze_count(force_env: force_env_t) -> c_long;
    pub fn cp2k_active_space_get_eri(
        force_env: force_env_t,
        buf_coords: *mut c_int,
        buf_coords_len: c_long,
        buf_values: *mut c_double,
        buf_values_len: c_long,
    ) -> c_int;
}
