use byteorder::{ByteOrder, LittleEndian};
use ckb_vm::{
    decoder::build_decoder, CoreMachine, DefaultCoreMachine, DefaultMachine, Error as CkbError, Memory, SupportMachine,
    RISCV_GENERAL_REGISTER_NUMBER,
};
use ckb_gdb_remote_protocol::{
    Breakpoint, Error, Handler, MemoryRegion, ProcessType, StopReason, ThreadId, VCont, VContFeature, Watchpoint,
};
use log::debug;
use std::borrow::Cow;
use std::cell::RefCell;
fn show_warning(e: &CkbError) {
    println!(
        "Fatal error in ckb-vm occurred: {:?}. Press Ctrl+C to quit or use gdb to attach it.",
        e
    );
    println!("Note: it doesn't mean any coding error in ckb-vm.");
    println!("This session can't be re-used. It is paused for post-mortem.")
}
fn format_register_value(v: u64) -> Vec<u8> {
    let mut buf = [0u8; 8];
    LittleEndian::write_u64(&mut buf, v);
    buf.to_vec()
}
pub struct WatchPointStatus {
    pub watchpoint: Watchpoint,
    pub data: Vec<u8>,
    pub has_data: bool,
}
impl WatchPointStatus {
    fn new(wp: Watchpoint) -> Self {
        let mut data = Vec::<u8>::new();
        data.resize(wp.n_bytes as usize, 0);
        WatchPointStatus {
            watchpoint: wp,
            data,
            has_data: false,
        }
    }
    fn has_change<H: Handler>(&mut self, handler: &H) -> Result<bool, Error> {
        let has_data = self.has_data;
        let mem = MemoryRegion {
            address: self.watchpoint.addr,
            length: self.watchpoint.n_bytes,
        };
        let new_content = handler.read_memory(mem)?;
        let result = if new_content == self.data {
            Ok(false)
        } else {
            self.data = new_content;
            Ok(true)
        };
        self.has_data = true;
        if has_data {
            result
        } else {
            Ok(false)
        }
    }
}
pub struct GdbHandler<M: Memory<REG = u64>> {
    machine: RefCell<DefaultMachine<DefaultCoreMachine<u64, M>>>,
    breakpoints: RefCell<Vec<Breakpoint>>,
    watchpoints: RefCell<Vec<WatchPointStatus>>,
}
impl<M: Memory<REG = u64>> GdbHandler<M> {
    fn at_breakpoint(&self) -> bool {
        let pc = *self.machine.borrow().pc();
        self.breakpoints.borrow().iter().any(|b| b.addr == pc)
    }
    fn at_watchpoint(&self) -> Result<bool, Error> {
        let mut result = false;
        for wp in self.watchpoints.borrow_mut().iter_mut() {
            if wp.has_change(self)? {
                result = true;
            }
        }
        Ok(result)
    }
    pub fn new(machine: DefaultMachine<DefaultCoreMachine<u64, M>>) -> Self {
        GdbHandler {
            machine: RefCell::new(machine),
            breakpoints: RefCell::new(vec![]),
            watchpoints: RefCell::new(vec![]),
        }
    }
}
impl<M: Memory<REG = u64>> Handler for GdbHandler<M> {
    fn attached(&self, _pid: Option<u64>) -> Result<ProcessType, Error> {
        Ok(ProcessType::Created)
    }
    fn halt_reason(&self) -> Result<StopReason, Error> {
        Ok(StopReason::Signal(2))
    }
    fn read_general_registers(&self) -> Result<Vec<u8>, Error> {
        let registers: Vec<Vec<u8>> =
            self.machine.borrow().registers().iter().map(|v| format_register_value(*v)).collect();
        Ok(registers.concat())
    }
    fn read_register(&self, register: u64) -> Result<Vec<u8>, Error> {
        let register = register as usize;
        if register < RISCV_GENERAL_REGISTER_NUMBER {
            Ok(format_register_value(self.machine.borrow().registers()[register]))
        } else if register == RISCV_GENERAL_REGISTER_NUMBER {
            Ok(format_register_value(*self.machine.borrow().pc()))
        } else {
            Err(Error::Error(1))
        }
    }
    fn write_register(&self, register: u64, contents: &[u8]) -> Result<(), Error> {
        let mut buffer = [0u8; 8];
        if contents.len() > 8 {
            error!("Register value too large!");
            return Err(Error::Error(2));
        }
        buffer[0..contents.len()].copy_from_slice(contents);
        let value = LittleEndian::read_u64(&buffer[..]);
        let register = register as usize;
        if register < RISCV_GENERAL_REGISTER_NUMBER {
            self.machine.borrow_mut().set_register(register, value);
            Ok(())
        } else if register == RISCV_GENERAL_REGISTER_NUMBER {
            self.machine.borrow_mut().update_pc(value);
            self.machine.borrow_mut().commit_pc();
            Ok(())
        } else {
            Err(Error::Error(2))
        }
    }
    fn read_memory(&self, region: MemoryRegion) -> Result<Vec<u8>, Error> {
        let mut values = vec![];
        for address in region.address..(region.address + region.length) {
            let value = self.machine.borrow_mut().memory_mut().load8(&address).map_err(|e| {
                error!("Error reading memory address {:x}: {:?}", address, e);
                Error::Error(3)
            })?;
            values.push(value as u8);
        }
        Ok(values)
    }
    fn write_memory(&self, address: u64, bytes: &[u8]) -> Result<(), Error> {
        self.machine.borrow_mut().memory_mut().store_bytes(address, bytes).map_err(|e| {
            error!("Error writing memory address {:x}: {:?}", address, e);
            Error::Error(4)
        })?;
        Ok(())
    }
    fn query_supported_vcont(&self) -> Result<Cow<'static, [VContFeature]>, Error> {
        Ok(Cow::from(
            &[
                VContFeature::Continue,
                VContFeature::ContinueWithSignal,
                VContFeature::Step,
                VContFeature::StepWithSignal,
                VContFeature::Stop,
                VContFeature::RangeStep,
            ][..],
        ))
    }
    fn vcont(&self, request: Vec<(VCont, Option<ThreadId>)>) -> Result<StopReason, Error> {
        let mut decoder = build_decoder::<u64>(self.machine.borrow().isa(), self.machine.borrow().version());
        let (vcont, _thread_id) = &request[0];
        match vcont {
            VCont::Continue => {
                if self.machine.borrow_mut().reset_signal() {
                    decoder.reset_instructions_cache()
                }
                let res = self.machine.borrow_mut().step(&mut decoder);
                if res.is_err() {
                    show_warning(&res.err().unwrap());
                    return Ok(StopReason::Signal(5));
                }
                while (!self.at_breakpoint()) && self.machine.borrow().running() {
                    if self.at_watchpoint()? {
                        break;
                    }
                    if self.machine.borrow_mut().reset_signal() {
                        decoder.reset_instructions_cache()
                    }
                    let res = self.machine.borrow_mut().step(&mut decoder);
                    if res.is_err() {
                        show_warning(&res.err().unwrap());
                        return Ok(StopReason::Signal(5));
                    }
                }
            }
            VCont::Step => {
                if self.machine.borrow().running() {
                    if self.machine.borrow_mut().reset_signal() {
                        decoder.reset_instructions_cache()
                    }
                    let res = self.machine.borrow_mut().step(&mut decoder);
                    if res.is_err() {
                        show_warning(&res.err().unwrap());
                        return Ok(StopReason::Signal(5));
                    }
                }
            }
            VCont::RangeStep(range) => {
                if self.machine.borrow_mut().reset_signal() {
                    decoder.reset_instructions_cache()
                }
                let res = self.machine.borrow_mut().step(&mut decoder);
                if res.is_err() {
                    show_warning(&res.err().unwrap());
                    return Ok(StopReason::Signal(5));
                }
                while self.machine.borrow().pc() >= &range.start
                    && self.machine.borrow().pc() < &range.end
                    && (!self.at_breakpoint())
                    && self.machine.borrow().running()
                {
                    if self.at_watchpoint()? {
                        break;
                    }
                    if self.machine.borrow_mut().reset_signal() {
                        decoder.reset_instructions_cache()
                    }
                    let res = self.machine.borrow_mut().step(&mut decoder);
                    if res.is_err() {
                        show_warning(&res.err().unwrap());
                        return Ok(StopReason::Signal(5));
                    }
                }
            }
            v => {
                debug!("Unspported vcont type: {:?}", v);
                return Err(Error::Error(5));
            }
        }
        if self.machine.borrow().running() {
            Ok(StopReason::Signal(5))
        } else {
            Ok(StopReason::Exited(0, self.machine.borrow().exit_code() as u8))
        }
    }
    fn insert_software_breakpoint(&self, breakpoint: Breakpoint) -> Result<(), Error> {
        self.breakpoints.borrow_mut().push(breakpoint);
        Ok(())
    }
    fn remove_software_breakpoint(&self, breakpoint: Breakpoint) -> Result<(), Error> {
        self.breakpoints.borrow_mut().retain(|b| b != &breakpoint);
        Ok(())
    }
    fn insert_write_watchpoint(&self, watchpoint: Watchpoint) -> Result<(), Error> {
        let wp = WatchPointStatus::new(watchpoint);
        self.watchpoints.borrow_mut().push(wp);
        debug!(
            "insert watch point at {:x} with length {}",
            watchpoint.addr, watchpoint.n_bytes
        );
        Ok(())
    }
    fn remove_write_watchpoint(&self, wp: Watchpoint) -> Result<(), Error> {
        self.watchpoints.borrow_mut().retain(|b| b.watchpoint.addr != wp.addr || b.watchpoint.n_bytes != wp.n_bytes);
        debug!("remove watch point at {:x} with length {}", wp.addr, wp.n_bytes);
        Ok(())
    }
}