use crate::python::instruction::PythonInstruction;
use crate::python::modeled_block::PythonModeledBlock;
use crate::python::modeled_instruction::PythonModeledInstruction;
use jingle_sleigh::JingleSleighError::InstructionDecode;
use jingle_sleigh::SleighArchInfo;
use jingle_sleigh::context::image::gimli::load_with_gimli;
use jingle_sleigh::context::loaded::LoadedSleighContext;
use pyo3::exceptions::PyRuntimeError;
use pyo3::{PyResult, pyclass, pymethods};
use std::rc::Rc;

#[pyclass(unsendable, name = "SleighContext")]
pub struct PythonLoadedSleighContext {
    context: Rc<LoadedSleighContext<'static>>,
}

impl PythonLoadedSleighContext {
    pub fn arch_info(&self) -> &SleighArchInfo {
        self.context.arch_info()
    }
}
#[pymethods]
impl PythonLoadedSleighContext {
    #[new]
    pub fn new(binary_path: &str, ghidra: &str) -> PyResult<Self> {
        let context = Rc::new(load_with_gimli(binary_path, ghidra)?);
        Ok(PythonLoadedSleighContext { context })
    }

    pub fn instruction_at(&self, offset: u64) -> Option<PythonInstruction> {
        PythonInstruction::read_from_ctx(&self.context, offset)
    }

    pub fn model_instruction_at(&self, offset: u64) -> PyResult<PythonModeledInstruction> {
        let instr = self
            .context
            .instruction_at(offset)
            .ok_or(InstructionDecode)?;
        PythonModeledInstruction::new(instr, self.context.arch_info())
    }

    pub fn model_block_at(&self, offset: u64, max_instrs: usize) -> PyResult<PythonModeledBlock> {
        PythonModeledBlock::new(
            self.context.arch_info(),
            self.context.read(offset, max_instrs),
        )
    }

    #[setter]
    pub fn set_base_address(&mut self, offset: u64) -> PyResult<()> {
        Rc::get_mut(&mut self.context)
            .ok_or(PyRuntimeError::new_err("sdf"))?
            .set_base_address(offset);
        Ok(())
    }

    #[getter]
    pub fn get_base_address(&mut self) -> u64 {
        self.context.get_base_address()
    }
}
