//! C FFI for cpdf — functionally equivalent to libcpdf.

use std::cell::RefCell;
use std::ffi::{CString, c_char, c_int};

thread_local! {
    static LAST_ERROR: RefCell<(i32, String)> = RefCell::new((0, String::new()));
    static LAST_ERROR_CSTRING: RefCell<CString> = RefCell::new(CString::new("").unwrap());
}

pub(crate) fn set_error(code: i32, msg: &str) {
    LAST_ERROR.with(|e| *e.borrow_mut() = (code, msg.to_owned()));
}

pub(crate) fn clear_error() {
    LAST_ERROR.with(|e| *e.borrow_mut() = (0, String::new()));
}

/// Returns the error code from the most recent cpdf operation.
/// Returns 0 on success, non-zero on error.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_lastError() -> c_int {
    LAST_ERROR.with(|e| e.borrow().0)
}

/// Returns a pointer to the error message from the most recent cpdf operation.
///
/// # Safety
///
/// The returned pointer is valid until the next call to any cpdf function
/// that modifies error state. Do not free this pointer. Copy the string
/// immediately if the value is needed beyond the next cpdf call.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_lastErrorString() -> *const c_char {
    LAST_ERROR.with(|e| {
        let msg = e.borrow().1.clone();
        let cs = CString::new(msg).unwrap_or_else(|_| CString::new("").unwrap());
        LAST_ERROR_CSTRING.with(|slot| {
            *slot.borrow_mut() = cs;
            slot.borrow().as_ptr()
        })
    })
}

/// Clears the current error state, resetting the error code to 0.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_clearError() {
    clear_error();
}

pub mod annotations;
pub mod attachments;
pub mod bookmarks;
pub mod compression;
pub mod document;
pub mod encryption;
pub mod fonts;
pub mod images;
pub mod metadata;
pub mod ocg;
pub mod page_labels;
pub mod pages;
pub mod stamps;
pub mod text;
pub use document::CPDF_DOCS;
