use core::cmp;
use core::fmt::Write;
use femtos::Instant;
use emulator_hal::bus::BusAccess;

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

#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[rustfmt::skip]
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 = u32;
pub type M68kAddressSpace = (FunctionCode, u32);

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

#[derive(Clone, Debug)]
pub struct M68kBusPort {
    pub request: MemoryRequest,
    pub data_bytewidth: usize,
    pub address_mask: u32,
    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(),
            data_bytewidth: 32 / 8,
            address_mask: 0xFFFF_FFFF,
            cycle_start_clock: Instant::START,
            current_clock: Instant::START,
        }
    }
}

impl M68kBusPort {
    pub fn from_info(info: &CpuInfo, clock: Instant) -> Self {
        Self {
            request: Default::default(),
            data_bytewidth: info.data_width as usize / 8,
            address_mask: 1_u32.checked_shl(info.address_width as u32).unwrap_or(0).wrapping_sub(1),
            cycle_start_clock: clock,
            current_clock: clock,
        }
    }

    fn read<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        clock: Instant,
        addr: M68kAddress,
        data: &mut [u8],
    ) -> Result<(), M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        let addr = addr & self.address_mask;
        for i in (0..data.len()).step_by(self.data_bytewidth) {
            let addr_index = (addr + i as M68kAddress) & self.address_mask;
            let end = cmp::min(i + self.data_bytewidth, data.len());
            bus.read(clock, addr_index, &mut data[i..end])
                .map_err(|err| M68kError::BusError(err))?;
        }
        Ok(())
    }

    fn write<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        clock: Instant,
        addr: M68kAddress,
        data: &[u8],
    ) -> Result<(), M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        let addr = addr & self.address_mask;
        for i in (0..data.len()).step_by(self.data_bytewidth) {
            let addr_index = (addr + i as M68kAddress) & self.address_mask;
            let end = cmp::min(i + self.data_bytewidth, data.len());
            bus.write(clock, addr_index, &data[i..end])
                .map_err(|err| M68kError::BusError(err))?;
        }
        Ok(())
    }

    fn read_sized<Bus, BusError>(&mut self, bus: &mut Bus, addr: M68kAddress, size: Size) -> Result<u32, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        let mut data = [0; 4];
        match size {
            Size::Byte => self.read(bus, self.current_clock, addr, &mut data[3..4]),
            Size::Word => self.read(bus, self.current_clock, addr, &mut data[2..4]),
            Size::Long => self.read(bus, self.current_clock, addr, &mut data[0..4]),
        }
        .map(|_| u32::from_be_bytes(data))
    }

    fn write_sized<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        addr: M68kAddress,
        size: Size,
        value: u32,
    ) -> Result<(), M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        let data = value.to_be_bytes();
        match size {
            Size::Byte => self.write(bus, self.current_clock, addr, &data[3..4]),
            Size::Word => self.write(bus, self.current_clock, addr, &data[2..4]),
            Size::Long => self.write(bus, self.current_clock, addr, &data[0..4]),
        }
    }

    pub(crate) fn read_data_sized<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        is_supervisor: bool,
        addr: M68kAddress,
        size: Size,
    ) -> Result<u32, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.start_request(is_supervisor, addr, size, MemAccess::Read, MemType::Data, false)?;
        self.read_sized(bus, addr, size)
    }

    pub(crate) fn write_data_sized<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        is_supervisor: bool,
        addr: M68kAddress,
        size: Size,
        value: u32,
    ) -> Result<(), M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.start_request(is_supervisor, addr, size, MemAccess::Write, MemType::Data, false)?;
        self.write_sized(bus, addr, size, value)
    }

    pub(crate) fn read_instruction_word<Bus, BusError>(
        &mut self,
        bus: &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(self.read_sized(bus, addr, Size::Word)? as u16)
    }

    pub(crate) fn read_instruction_long<Bus, BusError>(
        &mut self,
        bus: &mut Bus,
        is_supervisor: bool,
        addr: u32,
    ) -> Result<u32, M68kError<BusError>>
    where
        Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
    {
        self.request.instruction(is_supervisor, addr)?;
        self.read_sized(bus, addr, Size::Long)
    }

    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)
        }
    }
}

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

pub fn dump_memory<Bus, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
where
    Bus: BusAccess<Address, Instant>,
    Address: From<u32> + Into<u32> + Copy,
    Instant: Copy,
{
    let mut addr = addr.into();
    let mut count = count.into();
    while count > 0 {
        let mut line = format!("{:#010x}: ", addr);

        let to = if count < 16 { count / 2 } else { 8 };
        for _ in 0..to {
            let word = bus.read_beu16(clock, Address::from(addr));
            if word.is_err() {
                println!("{}", line);
                return;
            }
            write!(line, "{:#06x} ", word.unwrap()).unwrap();
            addr += 2;
            count -= 2;
        }
        println!("{}", line);
    }
}

/*
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> {

    }
}
*/
