Documentation
use std::str::FromStr;

use super::{GenericState, GenericStep, MemoryOp, State, Step};
use bitflags::bitflags;

#[derive(Clone, Debug)]
pub struct X64Step {
    state: X64State,
    code: Vec<u8>,
    address: u64,
    strace: Option<String>,
    memory_ops: Vec<MemoryOp>,
}

impl Step<16> for X64Step {
    type STATE = X64State;

    fn code(&self) -> &[u8] {
        &self.code[..]
    }

    fn state(&self) -> &X64State {
        &self.state
    }

    fn address(&self) -> u64 {
        self.address
    }

    fn strace(&self) -> Option<&String> {
        self.strace.as_ref()
    }

    fn memory_ops(&self) -> &[super::MemoryOp] {
        &self.memory_ops
    }
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq)]
    pub struct X64Flags: u32 {
        const CARRY = 1 << 0;
        const PARITY = 1 << 2;
        const AUXILIARY_CARRY = 1 << 4;
        const ZERO = 1 << 6;
        const SIGN = 1 << 7;
        const TRAP = 1 << 8;
        const INTERRUPT_EN = 1 << 9;
        const DIRECTION = 1 << 10;
        const OVERFLOW = 1 << 11;
        const IOPL_0 = 1 << 12;
        const IOPL_1 = 1 << 13;
        const NESTED_TASK = 1 << 14;
        const RESUME = 1 << 16;
        const VIRTUAL_8086 = 1 << 17;
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct X64State {
    regs: [u64; 16],
    pc: u64,
    flags: X64Flags,
}

impl State<16> for X64State {
    type FLAGS = X64Flags;

    fn pc(&self) -> u64 {
        self.pc
    }

    fn regs(&self) -> &[u64; 16] {
        &self.regs
    }

    fn flags(&self) -> &X64Flags {
        &self.flags
    }

    fn reg_name(i: usize) -> &'static str {
        [
            "rax", "rbx", "rcx", "rdx", "rbp", "rsp", "rsi", "rdi", "r8", "r9", "r10", "r11",
            "r12", "r13", "r14", "r15",
        ][i]
    }
}

impl FromStr for X64State {
    type Err = anyhow::Error;

    fn from_str(input: &str) -> anyhow::Result<Self> {
        let generic: GenericState<u64, 16> = GenericState::from_str(input)?;

        Ok(Self {
            regs: generic.regs,
            pc: generic.pc,
            flags: X64Flags::from_bits_retain(generic.flags as u32),
        })
    }
}

impl TryFrom<&[String]> for X64Step {
    type Error = anyhow::Error;

    fn try_from(input: &[String]) -> anyhow::Result<Self> {
        let generic: GenericStep<X64State> = GenericStep::try_from(input)?;

        Ok(Self {
            state: generic.state,
            code: generic.code,
            address: generic.address,
            strace: generic.strace,
            memory_ops: generic.memory_ops,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::X64State;
    use crate::state::X64Flags;
    use std::str::FromStr;

    #[test]
    fn aarch64_state_from_string() {
        let input = "r0=0|r1=0|r2=0|r3=0|r4=0|r5=0|r6=0|r7=0|r8=0|r9=0|r10=0|r11=0|r12=0|r13=0|r14=0|r15=0|pc=0|flags=0";

        let result = X64State::from_str(input);

        assert!(result.is_ok());

        assert_eq!(
            result.unwrap(),
            X64State {
                regs: [0; 16],
                pc: 0,
                flags: X64Flags::empty(),
            }
        );
    }
}