//! System lifecycle FFI functions.
//!
//! Provides opaque pointer wrappers for `entropyk_solver::System`.

use std::os::raw::c_uint;

use crate::components::EntropykComponent;
use crate::error::EntropykErrorCode;

/// Opaque handle to a thermodynamic system.
///
/// Create with `entropyk_system_create()`.
/// MUST call `entropyk_system_free()` when done.
#[repr(C)]
pub struct EntropykSystem {
    _private: [u8; 0],
}

impl EntropykSystem {
    fn from_inner(inner: entropyk_solver::System) -> *mut Self {
        Box::into_raw(Box::new(inner)) as *mut Self
    }
}

/// Create a new thermodynamic system.
///
/// # Returns
///
/// Pointer to the new system, or null on allocation failure.
///
/// # Ownership
///
/// Caller owns the returned pointer. MUST call `entropyk_system_free()` when done.
#[no_mangle]
pub extern "C" fn entropyk_system_create() -> *mut EntropykSystem {
    let system = entropyk_solver::System::new();
    EntropykSystem::from_inner(system)
}

/// Free a system created by `entropyk_system_create()`.
///
/// # Safety
///
/// - `system` must be a valid pointer returned by `entropyk_system_create()`, or null
/// - After this call, `system` is invalid and must not be used
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_free(system: *mut EntropykSystem) {
    if !system.is_null() {
        drop(Box::from_raw(system as *mut entropyk_solver::System));
    }
}

/// Add a component to the system.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
/// - `component`: The component to add (must not be null)
///
/// # Returns
///
/// Node index on success, or `UINT32_MAX` (0xFFFFFFFF) on error.
/// Use this index for `entropyk_system_add_edge()`.
///
/// # Safety
///
/// - `system` must be a valid pointer
/// - `component` must be a valid pointer
/// - After this call, `component` is consumed and must not be used again
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_add_component(
    system: *mut EntropykSystem,
    component: *mut EntropykComponent,
) -> c_uint {
    const ERROR_INDEX: c_uint = u32::MAX;

    if system.is_null() || component.is_null() {
        return ERROR_INDEX;
    }

    let sys = &mut *(system as *mut entropyk_solver::System);
    let comp_ptr = component as *mut Box<dyn entropyk_components::Component>;
    let comp = Box::from_raw(comp_ptr);

    let node_index = sys.add_component(*comp);
    node_index.index() as c_uint
}

/// Add a flow edge from source to target node.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
/// - `from`: Source node index (returned by `entropyk_system_add_component`)
/// - `to`: Target node index (returned by `entropyk_system_add_component`)
///
/// # Returns
///
/// - `ENTROPYK_OK` on success
/// - `ENTROPYK_NULL_POINTER` if system is null
/// - `ENTROPYK_TOPOLOGY_ERROR` if edge cannot be added
///
/// # Safety
///
/// `system` must be a valid pointer.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_add_edge(
    system: *mut EntropykSystem,
    from: c_uint,
    to: c_uint,
) -> EntropykErrorCode {
    if system.is_null() {
        return EntropykErrorCode::EntropykNullPointer;
    }

    let sys = &mut *(system as *mut entropyk_solver::System);
    let src = petgraph::graph::NodeIndex::new(from as usize);
    let tgt = petgraph::graph::NodeIndex::new(to as usize);

    match sys.add_edge(src, tgt) {
        Ok(_) => EntropykErrorCode::EntropykOk,
        Err(e) => EntropykErrorCode::from(e),
    }
}

/// Finalize the system for solving.
///
/// Must be called before any solve function.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
///
/// # Returns
///
/// - `ENTROPYK_OK` on success
/// - `ENTROPYK_NULL_POINTER` if system is null
/// - `ENTROPYK_TOPOLOGY_ERROR` if topology is invalid
///
/// # Safety
///
/// `system` must be a valid pointer.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_finalize(
    system: *mut EntropykSystem,
) -> EntropykErrorCode {
    if system.is_null() {
        return EntropykErrorCode::EntropykNullPointer;
    }

    let sys = &mut *(system as *mut entropyk_solver::System);

    match sys.finalize() {
        Ok(_) => EntropykErrorCode::EntropykOk,
        Err(e) => EntropykErrorCode::from(e),
    }
}

/// Get the number of components (nodes) in the system.
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_node_count(system: *const EntropykSystem) -> c_uint {
    if system.is_null() {
        return 0;
    }
    let sys = &*(system as *const entropyk_solver::System);
    sys.node_count() as c_uint
}

/// Get the number of edges in the system.
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_edge_count(system: *const EntropykSystem) -> c_uint {
    if system.is_null() {
        return 0;
    }
    let sys = &*(system as *const entropyk_solver::System);
    sys.edge_count() as c_uint
}

/// Get the length of the state vector (after finalization).
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_state_vector_len(system: *const EntropykSystem) -> c_uint {
    if system.is_null() {
        return 0;
    }
    let sys = &*(system as *const entropyk_solver::System);
    sys.state_vector_len() as c_uint
}
