use std::process::exit;

use pyo3::prelude::*;
use pyo3::types::{PyAny, PyDict};
use pyxel::{Key, PyxelCallback};
#[cfg(not(target_os = "emscripten"))]
use sysinfo::{Pid, PidExt, System, SystemExt};

#[pyfunction]
#[pyo3(
    text_signature = "(width, height, *, title, fps, quit_key, display_scale, capture_scale, capture_sec)"
)]
fn init(
    py: Python,
    width: u32,
    height: u32,
    title: Option<&str>,
    fps: Option<u32>,
    quit_key: Option<Key>,
    display_scale: Option<u32>,
    capture_scale: Option<u32>,
    capture_sec: Option<u32>,
) -> PyResult<()> {
    let locals = PyDict::new(py);
    locals.set_item("os", py.import("os")?)?;
    locals.set_item("inspect", py.import("inspect")?)?;
    py.run(
        "os.chdir(os.path.dirname(inspect.stack()[1].filename) or '.')",
        None,
        Some(locals),
    )?;
    pyxel::init(
        width,
        height,
        title,
        fps,
        quit_key,
        display_scale,
        capture_scale,
        capture_sec,
    );
    Ok(())
}

#[pyfunction]
fn title(title: &str) {
    pyxel::title(title);
}

#[pyfunction]
fn icon(data: Vec<&str>, scale: u32) {
    pyxel::icon(&data, scale);
}

#[pyfunction]
fn fullscreen(full: bool) {
    pyxel::fullscreen(full);
}

#[pyfunction]
fn run(py: Python, update: &PyAny, draw: &PyAny) {
    struct PythonCallback<'a> {
        py: Python<'a>,
        update: &'a PyAny,
        draw: &'a PyAny,
    }

    impl<'a> PyxelCallback for PythonCallback<'a> {
        fn update(&mut self) {
            if let Err(err) = self.update.call0() {
                err.print(self.py);
                exit(1);
            }
        }

        fn draw(&mut self) {
            if let Err(err) = self.draw.call0() {
                err.print(self.py);
                exit(1);
            }
        }
    }

    pyxel::run(PythonCallback { py, update, draw });
}

#[pyfunction]
fn show() {
    pyxel::show();
}

#[pyfunction]
fn quit() {
    pyxel::quit();
}

#[cfg(not(target_os = "emscripten"))]
#[pyfunction]
fn process_exists(pid: u32) -> bool {
    let system = System::new_all();
    system.process(Pid::from_u32(pid)).is_some()
}

pub fn add_system_functions(m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(init, m)?)?;
    m.add_function(wrap_pyfunction!(title, m)?)?;
    m.add_function(wrap_pyfunction!(icon, m)?)?;
    m.add_function(wrap_pyfunction!(fullscreen, m)?)?;
    m.add_function(wrap_pyfunction!(run, m)?)?;
    m.add_function(wrap_pyfunction!(show, m)?)?;
    m.add_function(wrap_pyfunction!(quit, m)?)?;
    #[cfg(not(target_os = "emscripten"))]
    m.add_function(wrap_pyfunction!(process_exists, m)?)?;
    Ok(())
}
