
use femtos::Instant;
use emulator_hal::bus::{BusAccess};

use moa_core::{Error, Address, Addressable};

use crate::state::{M68k, M68kError, Exceptions};
use crate::instructions::Size;

#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FunctionCode {
    Reserved0           = 0,
    UserData            = 1,
    UserProgram         = 2,
    Reserved3           = 3,
    Reserved4           = 4,
    SupervisorData      = 5,
    SupervisorProgram   = 6,
    CpuSpace            = 7,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MemType {
    Program,
    Data,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MemAccess {
    Read,
    Write,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
// TODO change to MemoryState or RequestState or AccessState or maybe even BusState
pub struct MemoryRequest {
    pub i_n_bit: bool,
    pub access: MemAccess,
    pub code: FunctionCode,
    pub size: Size,
    pub address: u32,
    pub clock: Instant,
}

impl FunctionCode {
    pub fn program(is_supervisor: bool) -> Self {
        if is_supervisor {
            FunctionCode::SupervisorProgram
        } else {
            FunctionCode::UserProgram
        }
    }

    pub fn data(is_supervisor: bool) -> Self {
        if is_supervisor {
            FunctionCode::SupervisorData
        } else {
            FunctionCode::UserData
        }
    }
}

impl Default for MemoryRequest {
    fn default() -> Self {
        Self {
            i_n_bit: false,
            access: MemAccess::Read,
            code: FunctionCode::Reserved0,
            size: Size::Word,
            address: 0,
            clock: Instant::START,
        }
    }
}

impl MemoryRequest {
    pub(crate) fn instruction<BusError>(&mut self, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>> {
        self.i_n_bit = false;
        self.code = FunctionCode::program(is_supervisor);
        self.access = MemAccess::Read;
        self.address = addr;

        validate_address(addr)
    }

    #[inline]
    pub fn get_type_code(&self) -> u16 {
        let ins = match self.i_n_bit {
            false => 0x0000,
            true => 0x0008,
        };

        let rw = match self.access {
            MemAccess::Write => 0x0000,
            MemAccess::Read => 0x0010,
        };

        ins | rw | (self.code as u16)
    }
}

//pub type M68kAddress = (FunctionCode, u32);
pub type M68kAddress = u64;

#[derive(Clone, Debug)]
pub struct InstructionRequest {
    pub request: MemoryRequest,
    pub current_clock: Instant,
}

#[derive(Clone, Debug)]
pub struct M68kBusPort {
    pub request: MemoryRequest,
    pub cycle_start_clock: Instant,
    pub current_clock: Instant,
}


impl M68k {
    // TODO should some of the ones from execute.rs move here
}

impl Default for M68kBusPort {
    fn default() -> Self {
        Self {
            request: Default::default(),
            cycle_start_clock: Instant::START,
            current_clock: Instant::START,
        }
    }
}

impl M68kBusPort {
    pub fn new(clock: Instant) -> Self {
        Self {
            request: Default::default(),
            cycle_start_clock: clock,
            current_clock: clock,
        }
    }

    pub(crate) fn read_data_sized<Bus, BusError>(&mut self, port: &mut Bus, is_supervisor: bool, addr: Address, size: Size) -> Result<u32, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.start_request(is_supervisor, addr as u32, size, MemAccess::Read, MemType::Data, false)?;
        Ok(match size {
            Size::Byte => port.read_u8(self.current_clock, addr).map(|value| value as u32),
            Size::Word => port.read_beu16(self.current_clock, addr).map(|value| value as u32),
            Size::Long => port.read_beu32(self.current_clock, addr),
        }.map_err(|err| M68kError::BusError(err))?)
    }

    pub(crate) fn write_data_sized<Bus, BusError>(&mut self, port: &mut Bus, is_supervisor: bool, addr: Address, value: u32, size: Size) -> Result<(), M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.start_request(is_supervisor, addr as u32, size, MemAccess::Write, MemType::Data, false)?;
        Ok(match size {
            Size::Byte => port.write_u8(self.current_clock, addr, value as u8),
            Size::Word => port.write_beu16(self.current_clock, addr, value as u16),
            Size::Long => port.write_beu32(self.current_clock, addr, value),
        }.map_err(|err| M68kError::BusError(err))?)
    }

    pub(crate) fn read_instruction_word<Bus, BusError>(&mut self, port: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u16, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.request.instruction(is_supervisor, addr)?;
        Ok(port.read_beu16(self.current_clock, addr as Address).map_err(|err| M68kError::BusError(err))?)
    }

    pub(crate) fn read_instruction_long<Bus, BusError>(&mut self, port: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.request.instruction(is_supervisor, addr)?;
        Ok(port.read_beu32(self.current_clock, addr as Address).map_err(|err| M68kError::BusError(err))?)
    }

    pub(crate) fn start_request<BusError>(&mut self, is_supervisor: bool, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result<u32, M68kError<BusError>> {
        self.request.i_n_bit = i_n_bit;
        self.request.code = match mtype {
            MemType::Program => FunctionCode::program(is_supervisor),
            MemType::Data => FunctionCode::data(is_supervisor),
        };

        self.request.access = access;
        self.request.address = addr;

        if size == Size::Byte {
            Ok(addr)
        } else {
            validate_address(addr)
        }
    }

    pub(crate) fn dump_memory<Bus, BusError>(&mut self, port: &mut Bus, addr: u32, length: usize)
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        // TODO temporarily disabled
        //port.dump_memory(self.current_clock, addr as Address, length as u64);
    }
}

fn validate_address<BusError>(addr: u32) -> Result<u32, M68kError<BusError>> {
    if addr & 0x1 == 0 {
        Ok(addr)
    } else {
        Err(M68kError::Exception(Exceptions::AddressError))
    }
}

/*
impl BusType for M68kBusPort {
    type Instant = Instant;
    type Error = Error;
}

impl BusAccess<u32> for M68kBusPort {
    fn read(&mut self, now: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
        self.
    }

    fn write(&mut self, now: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {

    }
}
*/

/*
pub(crate) struct TargetAccess {
    must_read: bool,
    must_write: bool,
    size: Size,
    target: Target,
}

impl TargetAccess {
    pub(crate) fn read_only(size: Size) -> Self {

    }

    pub(crate) fn read_update(size: Size) -> Self {

    }

    pub(crate) fn updated_only(size: Size) -> Self {

    }

    pub(crate) fn get(&mut self, cpu: &M68k) -> Result<u32, M68kError> {

    }

    pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), M68kError> {

    }

    pub(crate) fn complete(&self) -> Result<Self, M68kError> {
        
    }
}


impl Target {
    pub(crate) fn read_once(self, size: Size) -> ReadOnceAccess {
        ReadOnceAccess {
            size,
            target: self,
            accessed: false,
        }
    }

    pub(crate) fn read_update(self, size: Size) -> ReadUpdateAccess {
        ReadUpdateAccess {
            size,
            target: self,
        }
    }

    pub(crate) fn write_once(self, size: Size) -> WriteOnceAccess {
        WriteOnceAccess {
            size,
            target: self,
        }
    }
}



pub(crate) struct ReadOnceAccess {
    size: Size,
    target: Target,
    accessed: bool,
}

impl ReadOnceAccess {
    pub(crate) fn get(&mut self, cpu: &M68k) -> Result<u32, M68kError> {

    }

    pub(crate) fn complete(&self) -> Result<Self, M68kError> {
        
    }
}

pub(crate) struct ReadUpdateAccess {
    size: Size,
    target: Target,
}

impl ReadUpdateAccess {
    pub(crate) fn get(&mut self, cpu: &M68k) -> Result<u32, M68kError> {

    }

    pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), M68kError> {

    }

    pub(crate) fn complete(&self) -> Result<Self, M68kError> {
        
    }
}

pub(crate) struct WriteOnceAccess {
    size: Size,
    target: Target,
}

impl WriteOnceAccess {
    pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), M68kError> {

    }

    pub(crate) fn complete(&self) -> Result<Self, M68kError> {
        
    }
}
*/
