use std::sync::mpsc::{self, Receiver, Sender};

use common::prelude::*;

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Running {
    Running,
    Paused,
    Halted,
}

pub trait IODevice: Send + Sync {
    fn read_byte(&mut self, addr: u32) -> u8;
    fn write_byte(&mut self, addr: u32, value: u8);
    fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8>;
    fn write_range(&mut self, addr: u32, value: Vec<u8>);
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Command {
    // set emulator state.
    Start,
    Stop,
    Step(usize),
    Reset(usize),
    Interrupt(Interrupt),
    Write(Address, Vec<u8>),
    WriteBlock(Address, Box<[u8; 256]>),

    // request emulator state.
    SerialRequest,
    MemRequest(Address, u32),
    DisplayRequest,
    StackRequest,
    RegisterRequest,
    RunningRequest,
    HistoryRequest,
    InstructionCountRequest,
}

#[derive(Debug)]
pub enum ProcessorError {
    InvalidInstruction(u32),
    InvalidRegister(u8),
    BadMemoryAccess(u32),
}

impl std::error::Error for ProcessorError {}

impl std::fmt::Display for ProcessorError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidInstruction(instruction) => {
                write!(f, "Invalid instruction: {instruction}")
            }
            Self::InvalidRegister(register) => {
                write!(f, "Invalid register: {register}")
            }
            Self::BadMemoryAccess(address) => {
                write!(f, "Bad memory access: {address}")
            }
        }
    }
}

pub struct State {
    pub state_receiver: Receiver<StateUpdate>,
    pub cmd_sender: Sender<Command>,

    // Processor state
    pub reg_file: RegFile,
    pub running: Running,
    pub instructions: usize,

    // Memory access views
    pub stack_view: Vec<u8>,
    pub memory_view: Vec<u8>,
    pub display_view: Vec<u8>,

    pub error_log: Vec<String>,

    pub instruction_history: Vec<(u32, u32)>,
    pub serial_buff: Vec<u8>,
}

impl State {
    #[must_use]
    pub fn new(sender: Sender<Command>, receiver: Receiver<StateUpdate>) -> Self {
        Self {
            state_receiver: receiver,
            cmd_sender: sender,
            reg_file: RegFile::default(),
            running: Running::Paused,
            instructions: 0,
            stack_view: vec![],
            memory_view: vec![],
            display_view: vec![],
            error_log: vec![],
            instruction_history: vec![],
            serial_buff: vec![],
        }
    }

    pub fn send(&mut self, cmd: Command) {
        if let Err(e) = self.cmd_sender.send(cmd) {
            self.error_log.push(e.to_string());
        }
    }

    pub fn update(&mut self) -> Result<(), mpsc::TryRecvError> {
        while let Ok(update) = self.state_receiver.try_recv() {
            match update {
                StateUpdate::Registers(reg_file) => self.reg_file = reg_file,
                StateUpdate::Running(running) => self.running = running,
                StateUpdate::Instructions(instructions) => {
                    self.instructions = instructions;
                }
                StateUpdate::StackView(stack_view) => self.stack_view = stack_view,
                StateUpdate::MemoryView(memory_view) => self.memory_view = memory_view,
                StateUpdate::DisplayView(display_view) => {
                    self.display_view = display_view;
                }
                StateUpdate::Error(err_state) => self.error_log.push(err_state),
                StateUpdate::InstructionHistory(history) => {
                    self.instruction_history.extend(history);
                }
                StateUpdate::Serial(buffer) => {
                    self.serial_buff.extend(buffer);
                }
            }

            if self.error_log.len() > 256 {
                self.error_log.drain(0..self.error_log.len() - 256);
            }

            if self.instruction_history.len() > 1024 {
                self.instruction_history
                    .drain(0..self.instruction_history.len() - 1024);
            }
        }

        if let Err(e) = self.state_receiver.try_recv() {
            match e {
                mpsc::TryRecvError::Empty => {}
                mpsc::TryRecvError::Disconnected => {
                    return Err(e);
                }
            }
        }

        Ok(())
    }
}

pub enum StateUpdate {
    Registers(RegFile),
    Serial(Vec<u8>),
    Running(Running),
    Instructions(usize),
    StackView(Vec<u8>),
    MemoryView(Vec<u8>),
    DisplayView(Vec<u8>),
    Error(String),
    InstructionHistory(Vec<(u32, u32)>),
}

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegFile {
    // General Purpose Registers
    rg0: u32,
    rg1: u32,
    rg2: u32,
    rg3: u32,
    rg4: u32,
    rg5: u32,
    rg6: u32,
    rg7: u32,
    rg8: u32,
    rg9: u32,
    rga: u32,
    rgb: u32,
    rgc: u32,
    rgd: u32,
    rge: u32,
    rgf: u32,

    // Special Purpose Registers
    acc: u32,
    spr: u32,
    bpr: u32,
    ret: u32,
    idr: u32,
    mmr: u32,

    // System Registers
    mar: u32,
    mdr: u32,
    sts: u32,
    cir: u32,
    pcx: u32,
}

impl RegFile {
    #[must_use]
    pub fn all(&self) -> Vec<(&str, u32)> {
        vec![
            ("Rg0", self.rg0),
            ("Rg1", self.rg1),
            ("Rg2", self.rg2),
            ("Rg3", self.rg3),
            ("Rg4", self.rg4),
            ("Rg5", self.rg5),
            ("Rg6", self.rg6),
            ("Rg7", self.rg7),
            ("Rg8", self.rg8),
            ("Rg9", self.rg9),
            ("Rga", self.rga),
            ("Rgb", self.rgb),
            ("Rgc", self.rgc),
            ("Rgd", self.rgd),
            ("Rge", self.rge),
            ("Rgf", self.rgf),
            ("Acc", self.acc),
            ("Spr", self.spr),
            ("Bpr", self.bpr),
            ("Ret", self.ret),
            ("Idr", self.idr),
            ("Mmr", self.mmr),
            ("Mar", self.mar),
            ("Mdr", self.mdr),
            ("Sts", self.sts),
            ("Cir", self.cir),
            ("Pcx", self.pcx),
        ]
    }

    pub const fn reset(&mut self) {
        self.rg0 = 0;
        self.rg1 = 0;
        self.rg2 = 0;
        self.rg3 = 0;
        self.rg4 = 0;
        self.rg5 = 0;
        self.rg6 = 0;
        self.rg7 = 0;
        self.rg8 = 0;
        self.rg9 = 0;
        self.rga = 0;
        self.rgb = 0;
        self.rgc = 0;
        self.rgd = 0;
        self.rge = 0;
        self.rgf = 0;
        self.acc = 0;
        self.spr = 0;
        self.bpr = 0;
        self.ret = 0;
        self.idr = 0;
        self.mmr = 0;
        self.mar = 0;
        self.mdr = 0;
        self.sts = 0;
        self.cir = 0;
        self.pcx = 0;
    }

    pub const fn reg(&mut self, reg: Register) -> Result<&mut u32, ProcessorError> {
        Ok(match reg {
            Register::Rg0 => &mut self.rg0,
            Register::Rg1 => &mut self.rg1,
            Register::Rg2 => &mut self.rg2,
            Register::Rg3 => &mut self.rg3,
            Register::Rg4 => &mut self.rg4,
            Register::Rg5 => &mut self.rg5,
            Register::Rg6 => &mut self.rg6,
            Register::Rg7 => &mut self.rg7,
            Register::Rg8 => &mut self.rg8,
            Register::Rg9 => &mut self.rg9,
            Register::Rga => &mut self.rga,
            Register::Rgb => &mut self.rgb,
            Register::Rgc => &mut self.rgc,
            Register::Rgd => &mut self.rgd,
            Register::Rge => &mut self.rge,
            Register::Rgf => &mut self.rgf,
            Register::Acc => &mut self.acc,
            Register::Spr => &mut self.spr,
            Register::Bpr => &mut self.bpr,
            Register::Ret => &mut self.ret,
            Register::Idr => &mut self.idr,
            Register::Mmr => &mut self.mmr,
            Register::Mar => &mut self.mar,
            Register::Mdr => &mut self.mdr,
            Register::Sts => &mut self.sts,
            Register::Cir => &mut self.cir,
            Register::Pcx => &mut self.pcx,
            _ => return Err(ProcessorError::InvalidRegister(Register::Null as u8)),
        })
    }

    pub const fn get(&self, reg: Register) -> Result<u32, ProcessorError> {
        Ok(match reg {
            Register::Rg0 => self.rg0,
            Register::Rg1 => self.rg1,
            Register::Rg2 => self.rg2,
            Register::Rg3 => self.rg3,
            Register::Rg4 => self.rg4,
            Register::Rg5 => self.rg5,
            Register::Rg6 => self.rg6,
            Register::Rg7 => self.rg7,
            Register::Rg8 => self.rg8,
            Register::Rg9 => self.rg9,
            Register::Rga => self.rga,
            Register::Rgb => self.rgb,
            Register::Rgc => self.rgc,
            Register::Rgd => self.rgd,
            Register::Rge => self.rge,
            Register::Rgf => self.rgf,
            Register::Acc => self.acc,
            Register::Spr => self.spr,
            Register::Bpr => self.bpr,
            Register::Ret => self.ret,
            Register::Idr => self.idr,
            Register::Mmr => self.mmr,
            Register::Mar => self.mar,
            Register::Mdr => self.mdr,
            Register::Sts => self.sts,
            Register::Cir => self.cir,
            Register::Pcx => self.pcx,
            Register::Zero => 0,
            _ => return Err(ProcessorError::InvalidRegister(Register::Null as u8)),
        })
    }
}
