//! Handling the JNI foreign-function interface.

use crate::{errors, panic_handling, OnionmasqMobile};
use jni::objects::{JClass, JString};
use jni::sys::jlong;
use jni::JNIEnv;
use onion_tunnel::accounting::BandwidthCounter;
use onion_tunnel::scaffolding::TunnelCommand;
use tracing::{debug, error, info};

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_runProxy(
    mut env: JNIEnv,
    _: JClass,
    fd: i32,
    cache_dir: JString,
    data_dir: JString,
    bridge_lines: JString,
) {
    panic_handling::capture_unwind!(env, {
        let mobile = OnionmasqMobile::get();
        if let Err(e) = mobile.run_proxy(&mut env, cache_dir, data_dir, fd, bridge_lines) {
            error!("runProxy() returned error: {e:?}");
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_closeProxy(
    mut env: JNIEnv,
    _: JClass,
) {
    panic_handling::capture_unwind!(env, {
        let mobile = OnionmasqMobile::get();
        if let Err(e) = mobile.stop_proxy() {
            error!("closeProxy() returned error: {e:?}");
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_refreshCircuits(
    mut env: JNIEnv,
    _: JClass,
) {
    panic_handling::capture_unwind!(env, {
        let mobile = OnionmasqMobile::get();
        if let Err(e) = mobile.send_command(TunnelCommand::RefreshCircuits) {
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_refreshCircuitsForApp(
    mut env: JNIEnv,
    _: JClass,
    aid: jlong,
) {
    panic_handling::capture_unwind!(env, {
        let mobile = OnionmasqMobile::get();
        if let Err(e) = mobile.send_command(TunnelCommand::RefreshCircuitsForApp {
            isolation_key: aid as u64,
        }) {
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_init(mut env: JNIEnv, _: JClass) {
    panic_handling::capture_unwind!(env, {
        if let Err(e) = OnionmasqMobile::new(&mut env) {
            error!("init() returned error: {e:?}");
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_setPcapPath(
    mut env: JNIEnv,
    _: JClass,
    path: JString,
) {
    panic_handling::capture_unwind!(env, {
        debug!("setPcapPath()");
        let path = env.get_string(&path).expect("path is invalid");
        let path = path.to_string_lossy();
        info!("enabling pcap logging to path: {}", path);
        let path = if path.is_empty() {
            None
        } else {
            Some(path.to_string())
        };
        OnionmasqMobile::get().set_pcap_path(path);
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_getBytesReceived(
    mut env: JNIEnv,
    _: JClass,
) -> jlong {
    panic_handling::capture_unwind!(env, {
        let rx = BandwidthCounter::global().bytes_rx();
        // Realistically nobody is going to pass 8192 PiB of data through the tunnel, are they?
        rx.try_into().expect("wow, large bandwidth counter")
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_getBytesReceivedForApp(
    mut env: JNIEnv,
    _: JClass,
    aid: jlong,
) -> jlong {
    panic_handling::capture_unwind!(env, {
        let rx = OnionmasqMobile::get().app_rx_counter(aid as u64);
        rx.try_into().expect("wow, large bandwidth counter")
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_getBytesSent(
    mut env: JNIEnv,
    _: JClass,
) -> jlong {
    panic_handling::capture_unwind!(env, {
        let tx = BandwidthCounter::global().bytes_tx();
        tx.try_into().expect("wow, large bandwidth counter")
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_getBytesSentForApp(
    mut env: JNIEnv,
    _: JClass,
    aid: jlong,
) -> jlong {
    panic_handling::capture_unwind!(env, {
        let tx = OnionmasqMobile::get().app_tx_counter(aid as u64);
        tx.try_into().expect("wow, large bandwidth counter")
    })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_resetCounters(
    mut env: JNIEnv,
    _: JClass,
) {
    panic_handling::capture_unwind!(env, { OnionmasqMobile::get().reset_counters() })
}

#[no_mangle]
pub extern "C" fn Java_org_torproject_onionmasq_OnionMasqJni_setCountryCode(
    mut env: JNIEnv,
    _: JClass,
    country_code: JString<'_>,
) {
    panic_handling::capture_unwind!(env, {
        if let Err(e) = OnionmasqMobile::get().set_country_code(country_code) {
            errors::throw_java_exception_for(&mut env, e).unwrap();
        }
    })
}
