use std::ffi::c_char;
use std::ffi::CStr;
use std::ffi::CString;
mod overhead_allocation;
pub use self::overhead_allocation::*;
mod products;
pub use self::products::create_products;
pub use self::products::csv::SourceType;
mod shared_models;
pub use self::shared_models::*;
pub mod cli;
pub mod graph;
mod io;
pub mod link;

#[no_mangle]
pub unsafe extern "C" fn move_money_from_text_free(s: *mut c_char) {
    unsafe {
        if s.is_null() {
            return;
        }
        let _ = CString::from_raw(s);
    };
}

#[no_mangle]
pub extern "C" fn allocate_overheads_from_text(
    lines: *const c_char,
    accounts: *const c_char,
    allocation_statistics: *const c_char,
    areas: *const c_char,
    cost_centres: *const c_char,
    account_type: *const c_char,
    use_numeric_accounts: bool,
) -> *mut c_char {
    let lines = unwrap_c_char(lines);
    let accounts = unwrap_c_char(accounts);
    let allocation_statistics = unwrap_c_char(allocation_statistics);
    let areas = unwrap_c_char(areas);
    let cost_centres = unwrap_c_char(cost_centres);
    let account_type = unwrap_c_char(account_type);
    let mut output_writer = csv::Writer::from_writer(vec![]);
    reciprocal_allocation(
        &mut csv::Reader::from_reader(lines.to_bytes()),
        &mut csv::Reader::from_reader(accounts.to_bytes()),
        &mut csv::Reader::from_reader(allocation_statistics.to_bytes()),
        &mut csv::Reader::from_reader(areas.to_bytes()),
        &mut csv::Reader::from_reader(cost_centres.to_bytes()),
        &mut output_writer,
        use_numeric_accounts,
        false,
        true,
        account_type.to_str().unwrap().to_owned(),
        // This needs to be false when we return just a string, as the
        // amount of data produced could easily go out of memory (9000ccs + 1000
        // accounts can reach 2gb)
        false,
        0.1,
    )
    .expect("Failed to allocate overheads");
    let inner = output_writer.into_inner().unwrap();
    CString::new(String::from_utf8(inner).unwrap())
        .unwrap()
        .into_raw()
}

#[no_mangle]
pub extern "C" fn allocate_overheads_from_text_to_file(
    lines: *const c_char,
    accounts: *const c_char,
    allocation_statistics: *const c_char,
    areas: *const c_char,
    cost_centres: *const c_char,
    account_type: *const c_char,
    output_path: *const c_char,
    use_numeric_accounts: bool,
    show_from: bool,
) {
    let lines = unwrap_c_char(lines);
    let accounts = unwrap_c_char(accounts);
    let allocation_statistics = unwrap_c_char(allocation_statistics);
    let areas = unwrap_c_char(areas);
    let cost_centres = unwrap_c_char(cost_centres);
    let account_type = unwrap_c_char(account_type);
    let output_path = unwrap_c_char(output_path);
    reciprocal_allocation(
        &mut csv::Reader::from_reader(lines.to_bytes()),
        &mut csv::Reader::from_reader(accounts.to_bytes()),
        &mut csv::Reader::from_reader(allocation_statistics.to_bytes()),
        &mut csv::Reader::from_reader(areas.to_bytes()),
        &mut csv::Reader::from_reader(cost_centres.to_bytes()),
        &mut csv::Writer::from_path(output_path.to_str().unwrap()).unwrap(),
        use_numeric_accounts,
        false,
        true,
        account_type.to_str().unwrap().to_owned(),
        show_from,
        0.1,
    )
    .expect("Failed to allocate overheads");
}

fn unwrap_c_char<'a>(s: *const c_char) -> &'a CStr {
    unsafe {
        assert!(!s.is_null());
        CStr::from_ptr(s)
    }
}

#[no_mangle]
pub unsafe extern "C" fn allocate_overheads_from_text_free(s: *mut c_char) {
    unsafe {
        if s.is_null() {
            return;
        }
        let _ = CString::from_raw(s);
    };
}
