use anyhow::Result;

#[cfg(feature = "python")]
use pyo3::prelude::*;

pub mod annotation;
pub mod apk;
pub mod class;
pub mod code;
pub mod dex_id;
pub mod dex_string;
pub mod dex_writer;
pub mod field;
mod hashmap_vectorize;
pub mod hiddenapi;
pub mod instructions;
pub mod method;
pub mod method_handle;
#[cfg(feature = "python")]
pub mod py_utils;
pub mod scalar;
pub mod value;
pub mod visitor;

#[cfg(feature = "code-analysis")]
pub mod code_analysis;

pub use annotation::*;
pub use apk::*;
pub use class::*;
pub use code::*;
pub use dex_id::*;
pub use dex_string::*;
pub use dex_writer::*;
pub use field::*;
pub use hiddenapi::*;
pub use instructions as ins;
pub use instructions::*;
pub use method::*;
pub use method_handle::*;
pub use scalar::*;
pub use value::*;
pub use visitor::*;

#[cfg(feature = "code-analysis")]
pub use code_analysis::*;

#[cfg(test)]
mod tests;

/// Androscalpel.
#[cfg(feature = "python")]
#[pymodule]
fn androscalpel(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
    pyo3_log::init();
    m.add_class::<DexNull>()?;
    m.add_class::<DexBoolean>()?;
    m.add_class::<DexByte>()?;
    m.add_class::<DexShort>()?;
    m.add_class::<DexChar>()?;
    m.add_class::<DexInt>()?;
    m.add_class::<DexLong>()?;
    m.add_class::<DexFloat>()?;
    m.add_class::<DexDouble>()?;
    m.add_class::<DexString>()?;

    m.add_class::<IdMethodType>()?;
    m.add_class::<IdType>()?;
    m.add_class::<IdField>()?;
    m.add_class::<IdMethod>()?;
    m.add_class::<IdEnum>()?;

    m.add_class::<HiddenApiData>()?;
    m.add_class::<HiddenApiPermission>()?;
    m.add_class::<HiddenApiDomain>()?;

    m.add_class::<DexAnnotationItem>()?;
    m.add_class::<DexAnnotation>()?;
    m.add_class::<DexArray>()?;
    m.add_class::<FieldVisibility>()?;
    m.add_class::<MethodVisibility>()?;
    m.add_class::<Method>()?;
    m.add_class::<Field>()?;
    m.add_class::<Class>()?;
    m.add_class::<Code>()?;
    m.add_class::<Apk>()?;

    let ins_module = PyModule::new(py, "ins")?;
    androscalpel_ins(py, &ins_module)?;
    m.add_submodule(&ins_module)?;
    let utils_module = PyModule::new(py, "utils")?;
    py_utils::export_module(py, &utils_module)?;
    m.add_submodule(&utils_module)?;

    Ok(())
}

/// Dalvik opcode for Androscalpel.
#[cfg(feature = "python")]
fn androscalpel_ins(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_class::<ins::CallSite>()?;
    m.add_class::<ins::Instruction>()?;
    Ok(())
}
