/// The Module for the x86_64 CPU Information
/// Contains Registers, Dead-Code Instructions and Co
use rand;
use std::cmp::PartialEq;


// Register Struct
#[derive(Clone)]
pub struct Reg<'a>{
    pub register: Register,
    pub full: &'a str,
    pub extended: &'a str,
    pub word: &'a str,
    pub byte: &'a str
}


// All X64 Registers that are needed
#[derive(Debug, PartialEq, Clone)]
pub enum Register {
    RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15
}

/// Full Map of all registers
const REGISTERS: [Reg; 16] = [
    Reg{ register: Register::RAX, full: "RAX", extended: "EAX",  word: "AX",   byte: "AL" },
    Reg{ register: Register::RBX, full: "RBX", extended: "EBX",  word: "BX",   byte: "BL" },
    Reg{ register: Register::RCX, full: "RCX", extended: "ECX",  word: "CX",   byte: "CL" },
    Reg{ register: Register::RDX, full: "RDX", extended: "EDX",  word: "DX",   byte: "DL" },
    Reg{ register: Register::RBP, full: "RBP", extended: "EBP",  word: "BP",   byte: "BPL" },
    Reg{ register: Register::RSP, full: "RSP", extended: "ESP",  word: "SP",   byte: "SPL" },
    Reg{ register: Register::RSI, full: "RSI", extended: "ESI",  word: "SI",   byte: "SIL" },
    Reg{ register: Register::RDI, full: "RDI", extended: "EDI",  word: "DI",   byte: "DIL" },
    Reg{ register: Register::R8,  full: "R8",  extended: "R8D",  word: "R8W",  byte: "R8B" },
    Reg{ register: Register::R9,  full: "R9",  extended: "R9D", word: "R9W",  byte: "R9B" },
    Reg{ register: Register::R10, full: "R10", extended: "R10D", word: "R10W", byte: "R10B" },
    Reg{ register: Register::R11, full: "R11", extended: "R11D", word: "R11W", byte: "R11B" },
    Reg{ register: Register::R12, full: "R12", extended: "R12D", word: "R12W", byte: "R12B" },
    Reg{ register: Register::R13, full: "R13", extended: "R13D", word: "R13W", byte: "R13B" },
    Reg{ register: Register::R14, full: "R14", extended: "R14D", word: "R14W", byte: "R14B" },
    Reg{ register: Register::R15, full: "R15", extended: "R15D", word: "R15W", byte: "R15B" }
];


/// Trade a Register Enum to Reg() -> Used to get more info
pub fn get_register(register: Register) -> Reg<'static> {

    let out_register = REGISTERS[0].clone();
    for reg in REGISTERS.iter() {
        if register == reg.register {
            return reg.clone();
        }
    }

    return out_register;
}


/// Simply returns a random x64 register
/// If the generated register is in the blacklist,
/// return anotherone
pub fn get_random_reg(blacklist: &Vec<Register>) -> Reg<'static> {

    // generate registers until the register is not in the blacklist
    loop {
        // Generate a random number as index
        let rnd_index = rand::random::<usize>() % REGISTERS.len();

        let register: Register = REGISTERS[rnd_index].register.clone();

        if !blacklist.contains(&register) {
            return get_register(register);
        }
    }
}

const DEAD_CODE_INSTRUCTIONS: [&str; 6] = [
    "NOP",
    "CLD",
    "XOR {R}, 0",
    "MOV {R}, {R}",
    "XCHG {R}, {R}",
    "FNOP"
];


/// Generate an String of dead code
pub fn generate_dead_code() -> String {

    let blacklist = vec![Register::RSP, Register::RBP];
    // Output String
    let mut dead_code = String::new();
    let mut num_instructios = rand::random::<usize>() % 40;
    num_instructios += 3;
    for _ in 3..num_instructios {
        let rnd = rand::random::<usize>() % DEAD_CODE_INSTRUCTIONS.len();
        let tmp_dead_code = DEAD_CODE_INSTRUCTIONS[rnd];
        let random_reg = get_random_reg(&blacklist);
        let tmp_dead_code = tmp_dead_code.replace("{R}", &random_reg.full);

        dead_code.push_str(&tmp_dead_code);
        dead_code.push('\n');
    }
    return dead_code;
}


