use anyhow::Result;

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 instructions;
pub mod method;
pub mod method_handle;
pub mod py_utils;
pub mod scalar;
pub mod value;

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 instructions as ins;
pub use method::*;
pub use method_handle::*;
pub use scalar::*;
pub use value::*;

#[cfg(test)]
mod tests;

/// Androscalpel.
#[pymodule]
fn androscalpel(py: Python, m: &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::<StaticPut>()?;
    m.add_class::<StaticGet>()?;
    m.add_class::<InstancePut>()?;
    m.add_class::<InstanceGet>()?;
    m.add_class::<InvokeStatic>()?;
    m.add_class::<InvokeInstance>()?;
    m.add_class::<InvokeConstructor>()?;
    m.add_class::<InvokeDirect>()?;
    m.add_class::<InvokeInterface>()?;

    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.
fn androscalpel_ins(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<ins::CallSite>()?;
    m.add_class::<ins::Nop>()?;
    m.add_class::<ins::Move>()?;
    m.add_class::<ins::MoveWide>()?;
    m.add_class::<ins::MoveObject>()?;
    m.add_class::<ins::MoveResult>()?;
    m.add_class::<ins::MoveResultWide>()?;
    m.add_class::<ins::MoveResultObject>()?;
    m.add_class::<ins::MoveException>()?;
    m.add_class::<ins::ReturnVoid>()?;
    m.add_class::<ins::Return>()?;
    m.add_class::<ins::ReturnWide>()?;
    m.add_class::<ins::ReturnObject>()?;
    m.add_class::<ins::Const>()?;
    m.add_class::<ins::ConstWide>()?;
    m.add_class::<ins::ConstString>()?;
    m.add_class::<ins::ConstClass>()?;
    m.add_class::<ins::MonitorEnter>()?;
    m.add_class::<ins::MonitorExit>()?;
    m.add_class::<ins::CheckCast>()?;
    m.add_class::<ins::InstanceOf>()?;
    m.add_class::<ins::ArrayLength>()?;
    m.add_class::<ins::NewInstance>()?;
    m.add_class::<ins::NewArray>()?;
    m.add_class::<ins::FilledNewArray>()?;
    m.add_class::<ins::FillArrayData>()?;
    m.add_class::<ins::Throw>()?;
    m.add_class::<ins::Goto>()?;
    m.add_class::<ins::Switch>()?;
    m.add_class::<ins::CmpLFloat>()?;
    m.add_class::<ins::CmpGFloat>()?;
    m.add_class::<ins::CmpLDouble>()?;
    m.add_class::<ins::CmpGDouble>()?;
    m.add_class::<ins::CmpLong>()?;
    m.add_class::<ins::IfEq>()?;
    m.add_class::<ins::IfNe>()?;
    m.add_class::<ins::IfLt>()?;
    m.add_class::<ins::IfGe>()?;
    m.add_class::<ins::IfGt>()?;
    m.add_class::<ins::IfLe>()?;
    m.add_class::<ins::IfEqZ>()?;
    m.add_class::<ins::IfNeZ>()?;
    m.add_class::<ins::IfLtZ>()?;
    m.add_class::<ins::IfGeZ>()?;
    m.add_class::<ins::IfGtZ>()?;
    m.add_class::<ins::IfLeZ>()?;
    m.add_class::<ins::AGet>()?;
    m.add_class::<ins::AGetWide>()?;
    m.add_class::<ins::AGetObject>()?;
    m.add_class::<ins::AGetBoolean>()?;
    m.add_class::<ins::AGetByte>()?;
    m.add_class::<ins::AGetChar>()?;
    m.add_class::<ins::AGetShort>()?;
    m.add_class::<ins::APut>()?;
    m.add_class::<ins::APutWide>()?;
    m.add_class::<ins::APutObject>()?;
    m.add_class::<ins::APutBoolean>()?;
    m.add_class::<ins::APutByte>()?;
    m.add_class::<ins::APutChar>()?;
    m.add_class::<ins::APutShort>()?;
    m.add_class::<ins::IGet>()?;
    m.add_class::<ins::IGetWide>()?;
    m.add_class::<ins::IGetObject>()?;
    m.add_class::<ins::IGetBoolean>()?;
    m.add_class::<ins::IGetByte>()?;
    m.add_class::<ins::IGetChar>()?;
    m.add_class::<ins::IGetShort>()?;
    m.add_class::<ins::IPut>()?;
    m.add_class::<ins::IPutWide>()?;
    m.add_class::<ins::IPutObject>()?;
    m.add_class::<ins::IPutBoolean>()?;
    m.add_class::<ins::IPutByte>()?;
    m.add_class::<ins::IPutChar>()?;
    m.add_class::<ins::IPutShort>()?;
    m.add_class::<ins::SGet>()?;
    m.add_class::<ins::SGetWide>()?;
    m.add_class::<ins::SGetObject>()?;
    m.add_class::<ins::SGetBoolean>()?;
    m.add_class::<ins::SGetByte>()?;
    m.add_class::<ins::SGetChar>()?;
    m.add_class::<ins::SGetShort>()?;
    m.add_class::<ins::SPut>()?;
    m.add_class::<ins::SPutWide>()?;
    m.add_class::<ins::SPutObject>()?;
    m.add_class::<ins::SPutBoolean>()?;
    m.add_class::<ins::SPutByte>()?;
    m.add_class::<ins::SPutChar>()?;
    m.add_class::<ins::SPutShort>()?;
    m.add_class::<ins::InvokeVirtual>()?;
    m.add_class::<ins::InvokeSuper>()?;
    m.add_class::<ins::InvokeDirect>()?;
    m.add_class::<ins::InvokeStatic>()?;
    m.add_class::<ins::InvokeInterface>()?;
    m.add_class::<ins::NegInt>()?;
    m.add_class::<ins::NotInt>()?;
    m.add_class::<ins::NegLong>()?;
    m.add_class::<ins::NotLong>()?;
    m.add_class::<ins::NegFloat>()?;
    m.add_class::<ins::NegDouble>()?;
    m.add_class::<ins::IntToLong>()?;
    m.add_class::<ins::IntToFloat>()?;
    m.add_class::<ins::IntToDouble>()?;
    m.add_class::<ins::LongToInt>()?;
    m.add_class::<ins::LongToFloat>()?;
    m.add_class::<ins::LongToDouble>()?;
    m.add_class::<ins::FloatToInt>()?;
    m.add_class::<ins::FloatToLong>()?;
    m.add_class::<ins::FloatToDouble>()?;
    m.add_class::<ins::DoubleToInt>()?;
    m.add_class::<ins::DoubleToLong>()?;
    m.add_class::<ins::DoubleToFloat>()?;
    m.add_class::<ins::IntToByte>()?;
    m.add_class::<ins::IntToChar>()?;
    m.add_class::<ins::IntToShort>()?;
    m.add_class::<ins::AddInt>()?;
    m.add_class::<ins::SubInt>()?;
    m.add_class::<ins::MulInt>()?;
    m.add_class::<ins::DivInt>()?;
    m.add_class::<ins::RemInt>()?;
    m.add_class::<ins::AndInt>()?;
    m.add_class::<ins::OrInt>()?;
    m.add_class::<ins::XorInt>()?;
    m.add_class::<ins::ShlInt>()?;
    m.add_class::<ins::ShrInt>()?;
    m.add_class::<ins::UshrInt>()?;
    m.add_class::<ins::AddLong>()?;
    m.add_class::<ins::SubLong>()?;
    m.add_class::<ins::MulLong>()?;
    m.add_class::<ins::DivLong>()?;
    m.add_class::<ins::RemLong>()?;
    m.add_class::<ins::AndLong>()?;
    m.add_class::<ins::OrLong>()?;
    m.add_class::<ins::XorLong>()?;
    m.add_class::<ins::ShlLong>()?;
    m.add_class::<ins::ShrLong>()?;
    m.add_class::<ins::UshrLong>()?;
    m.add_class::<ins::AddFloat>()?;
    m.add_class::<ins::SubFloat>()?;
    m.add_class::<ins::MulFloat>()?;
    m.add_class::<ins::DivFloat>()?;
    m.add_class::<ins::RemFloat>()?;
    m.add_class::<ins::AddDouble>()?;
    m.add_class::<ins::SubDouble>()?;
    m.add_class::<ins::MulDouble>()?;
    m.add_class::<ins::DivDouble>()?;
    m.add_class::<ins::RemDouble>()?;
    m.add_class::<ins::AddInt2Addr>()?;
    m.add_class::<ins::SubInt2Addr>()?;
    m.add_class::<ins::MulInt2Addr>()?;
    m.add_class::<ins::DivInt2Addr>()?;
    m.add_class::<ins::RemInt2Addr>()?;
    m.add_class::<ins::AndInt2Addr>()?;
    m.add_class::<ins::OrInt2Addr>()?;
    m.add_class::<ins::XorInt2Addr>()?;
    m.add_class::<ins::ShlInt2Addr>()?;
    m.add_class::<ins::ShrInt2Addr>()?;
    m.add_class::<ins::UshrInt2Addr>()?;
    m.add_class::<ins::AddLong2Addr>()?;
    m.add_class::<ins::SubLong2Addr>()?;
    m.add_class::<ins::MulLong2Addr>()?;
    m.add_class::<ins::DivLong2Addr>()?;
    m.add_class::<ins::RemLong2Addr>()?;
    m.add_class::<ins::AndLong2Addr>()?;
    m.add_class::<ins::OrLong2Addr>()?;
    m.add_class::<ins::XorLong2Addr>()?;
    m.add_class::<ins::ShlLong2Addr>()?;
    m.add_class::<ins::ShrLong2Addr>()?;
    m.add_class::<ins::UshrLong2Addr>()?;
    m.add_class::<ins::AddFloat2Addr>()?;
    m.add_class::<ins::SubFloat2Addr>()?;
    m.add_class::<ins::MulFloat2Addr>()?;
    m.add_class::<ins::DivFloat2Addr>()?;
    m.add_class::<ins::RemFloat2Addr>()?;
    m.add_class::<ins::AddDouble2Addr>()?;
    m.add_class::<ins::SubDouble2Addr>()?;
    m.add_class::<ins::MulDouble2Addr>()?;
    m.add_class::<ins::DivDouble2Addr>()?;
    m.add_class::<ins::RemDouble2Addr>()?;
    m.add_class::<ins::AddIntLit>()?;
    m.add_class::<ins::RsubIntLit>()?;
    m.add_class::<ins::MulIntLit>()?;
    m.add_class::<ins::DivIntLit>()?;
    m.add_class::<ins::RemIntLit>()?;
    m.add_class::<ins::AndIntLit>()?;
    m.add_class::<ins::OrIntLit>()?;
    m.add_class::<ins::XorIntLit>()?;
    m.add_class::<ins::ShlIntLit>()?;
    m.add_class::<ins::ShrIntLit>()?;
    m.add_class::<ins::UshrIntLit>()?;
    m.add_class::<ins::InvokePolymorphic>()?;
    m.add_class::<ins::InvokeCustom>()?;
    m.add_class::<ins::ConstMethodHandle>()?;
    m.add_class::<ins::ConstMethodType>()?;
    m.add_class::<ins::Try>()?;
    m.add_class::<ins::Label>()?;
    Ok(())
}
