use std::marker::PhantomData;

use yggdrasil_abi::arch::SavedFrame;
use yggdrasil_rt::{debug::DebugOperation, process::ProcessId};

use crate::{Error, Target};

pub struct State<T: Target> {
    pub pid: ProcessId,

    pub current_ip: usize,
    pub last_frame: SavedFrame,

    pub image_base: usize,
    pub ip_offset: usize,

    _pd: PhantomData<T>,
}

impl<T: Target> State<T> {
    pub fn new(image_base: usize, ip_offset: usize, pid: ProcessId) -> Self {
        Self {
            current_ip: 0,
            last_frame: SavedFrame::default(),

            pid,
            image_base,
            ip_offset,

            _pd: PhantomData,
        }
    }

    pub fn resume(&mut self, step: bool) -> Result<(), Error> {
        unsafe {
            yggdrasil_rt::sys::debug_control(self.pid, &mut DebugOperation::Continue(step))
                .map_err(Error::DebugError)
        }
    }

    pub fn set_breakpoint(&mut self, address: u64) -> Result<(), Error> {
        unsafe {
            yggdrasil_rt::sys::debug_control(self.pid, &mut DebugOperation::SetBreakpoint(address.try_into().unwrap())).map_err(Error::DebugError)
        }
    }

    pub fn read_memory(&mut self, address: u64, buffer: &mut [u8]) -> Result<(), Error> {
        let mut op = DebugOperation::ReadMemory { address: address.try_into().unwrap(), buffer };
        unsafe {
            yggdrasil_rt::sys::debug_control(
                self.pid,
                &mut op
            ).map_err(Error::DebugError)
        }
    }

    pub fn update_ip(&mut self, ip: usize) {
        self.current_ip = ip;
    }

    pub fn update(&mut self, frame: &SavedFrame, _refresh: bool) -> Result<(), Error> {
        let ip = T::real_ip(frame) - self.ip_offset;
        self.update_ip(ip);
        self.last_frame = frame.clone();

        Ok(())
    }
}
