/*!
Wrappers to Python objects.
*/

use pyo3::types::{PyModule, PyModuleMethods, PyTracebackMethods};
use pyo3::{pymodule, Bound, PyResult, Python};

use alg_tools::error::DynResult;
use anyhow::anyhow;

mod diff_mapping;
mod function;
mod numpy_array;
mod plot;
mod prox_mapping;

//mod measures;

#[allow(unused_imports)]
pub use diff_mapping::{
    Basic, Differentiable, DolfinxPyFunctionMarker, NumpyArrayMarker, PythonMapping,
};
//pub use pointsource_algs::measures::python::{DiscreteMeasure_1_f64, DiscreteMeasure_2_f64};
//

#[allow(unused_imports)]
pub use prox_mapping::HasProx;

#[allow(unused_imports)]
pub use numpy_array::{NumpyArray_f64, NumpyMatrix_f64, NumpyVector_f64};

#[allow(unused_imports)]
pub use plot::PythonPlotFactory;

/// Populates the Python module.
///
/// Needs to be called with [`pyo3::append_to_inittab`]:
/// ```
/// append_to_inittab!(pymod_pointsource_pde);
/// ```
/// before initialising the intepreter.
#[pymodule]
#[pyo3(name = "pointsource_pde")]
pub(crate) fn pymod_pointsource_pde(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_class::<super::experiments::Experiments>()?;
    m.add_class::<super::experiments::Problem>()?;
    m.add_class::<super::experiments::RegTermPy>()?;
    Ok(())
}

/// Converts a [`PyErr`] error in a [`Results`] error into a string with traceback.
pub fn process_error<'py, T>(fname: &str, py: Python<'py>, res: PyResult<T>) -> DynResult<T>
where
{
    match res {
        Ok(res) => Ok(res),
        Err(e) => Err(match e.traceback(py) {
            None => anyhow!("Failed Python traceback in {fname}; original error {e}"),
            Some(r) => match r.format() {
                Err(e) => anyhow!("Failed Python traceback formatting in {fname}: {e}"),
                Ok(o) => anyhow!("Python-side error in {fname}: {e}.\n{o}"),
            },
        }),
    }
}
