//! C FFI for cpdf text extraction and removal.

use super::document::{CPDF_DOCS, CPDF_RANGES};
use super::{clear_error, set_error};
use crate::cpdf::text;
use crate::ffi::Handle;
use std::ffi::{CString, c_char, c_int};

macro_rules! get_doc_ref {
    ($handle:expr) => {
        match CPDF_DOCS.get($handle) {
            Some(arc) => arc,
            None => {
                set_error(1, &format!("invalid document handle: {}", $handle));
                return std::ptr::null_mut();
            }
        }
    };
}

macro_rules! get_range {
    ($handle:expr) => {
        match CPDF_RANGES.get($handle) {
            Some(arc) => arc.lock().unwrap().clone(),
            None => {
                set_error(1, &format!("invalid range handle: {}", $handle));
                return std::ptr::null_mut();
            }
        }
    };
    ($handle:expr, $ret:expr) => {
        match CPDF_RANGES.get($handle) {
            Some(arc) => arc.lock().unwrap().clone(),
            None => {
                set_error(1, &format!("invalid range handle: {}", $handle));
                return $ret;
            }
        }
    };
}

/// Extract text from pages in range. Free with `cpdf_freeString`.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_getText(doc: Handle, range_h: Handle) -> *mut c_char {
    clear_error();
    let d = get_doc_ref!(doc);
    let guard = d.lock().unwrap();
    let r = get_range!(range_h);
    match text::extract_text(&guard, &r) {
        Ok(s) => match CString::new(s) {
            Ok(cs) => cs.into_raw(),
            Err(_) => {
                set_error(1, "text contains null byte");
                std::ptr::null_mut()
            }
        },
        Err(e) => {
            set_error(1, &e.to_string());
            std::ptr::null_mut()
        }
    }
}

/// Extract text from a single page. Free with `cpdf_freeString`.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_getPageText(doc: Handle, page_num: c_int) -> *mut c_char {
    clear_error();
    let d = get_doc_ref!(doc);
    let guard = d.lock().unwrap();
    match text::extract_page_text(&guard, page_num.max(1) as usize) {
        Ok(s) => match CString::new(s) {
            Ok(cs) => cs.into_raw(),
            Err(_) => {
                set_error(1, "text contains null byte");
                std::ptr::null_mut()
            }
        },
        Err(e) => {
            set_error(1, &e.to_string());
            std::ptr::null_mut()
        }
    }
}

/// Remove text from pages in range. Returns new document handle.
#[unsafe(no_mangle)]
pub extern "C" fn cpdf_removeText(doc: Handle, range_h: Handle) -> Handle {
    clear_error();
    let d = match CPDF_DOCS.get(doc) {
        Some(arc) => arc.lock().unwrap().clone(),
        None => {
            set_error(1, &format!("invalid document handle: {}", doc));
            return 0;
        }
    };
    let r = get_range!(range_h, 0);
    match text::remove_text(d, &r) {
        Ok(doc) => CPDF_DOCS.insert(doc),
        Err(e) => {
            set_error(1, &e.to_string());
            0
        }
    }
}
