use core::fmt::Debug;

mod card_bridge;
mod endpoint;
mod pci_bridge;
mod unknown;

pub use card_bridge::*;
pub use endpoint::Endpoint;
pub use pci_bridge::*;
use pci_types::{
    CommandRegister, ConfigRegionAccess, HeaderType, PciAddress, PciHeader, StatusRegister,
};
use rdif_pcie::ConfigAccess;
pub use unknown::*;

use crate::chip::PcieController;

#[derive(Debug)]
pub enum PciConfigSpace {
    PciPciBridge(PciPciBridge),
    Endpoint(Endpoint),
    CardBusBridge(CardBusBridge),
    Unknown(Unknown),
}

pub struct PciHeaderBase {
    vid: u16,
    did: u16,
    root: ConfigAccess,
    header: PciHeader,
}

impl PciHeaderBase {
    pub(crate) fn new(root: &mut PcieController, address: PciAddress) -> Option<Self> {
        let root = root.config_access(address);
        let header = PciHeader::new(address);
        let (vid, did) = header.id(&root);
        if vid == 0xffff {
            return None;
        }

        Some(Self {
            vid,
            did,
            root,
            header,
        })
    }

    pub fn header(&self) -> PciHeader {
        PciHeader::new(self.address())
    }

    pub fn address(&self) -> PciAddress {
        self.header.address()
    }

    pub fn header_type(&self) -> HeaderType {
        self.header.header_type(&self.root)
    }

    pub fn has_multiple_functions(&self) -> bool {
        self.header.has_multiple_functions(&self.root)
    }

    pub fn update_command<F>(&mut self, f: F)
    where
        F: FnOnce(CommandRegister) -> CommandRegister,
    {
        self.header.update_command(&self.root, f);
    }

    pub fn status(&self) -> StatusRegister {
        self.header.status(&self.root)
    }

    pub fn command(&self) -> CommandRegister {
        self.header.command(&self.root)
    }

    pub fn revision_and_class(&self) -> RevisionAndClass {
        let (revision_id, base_class, sub_class, interface) =
            self.header.revision_and_class(&self.root);
        RevisionAndClass {
            revision_id,
            base_class,
            sub_class,
            interface,
        }
    }

    pub fn vendor_id(&self) -> u16 {
        self.vid
    }

    pub fn device_id(&self) -> u16 {
        self.did
    }

    pub fn read(&self, offset: u16) -> u32 {
        unsafe { self.root.read(self.address(), offset) }
    }

    pub fn write(&self, offset: u16, value: u32) {
        unsafe { self.root.write(self.address(), offset, value) }
    }
}

#[derive(Debug, Clone)]
pub struct RevisionAndClass {
    pub revision_id: u8,
    pub base_class: u8,
    pub sub_class: u8,
    pub interface: u8,
}

impl Debug for PciHeaderBase {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("PciHeaderBase")
            .field("address", &self.address())
            .field("vid", &format_args!("{:#06x}", self.vid))
            .field("did", &format_args!("{:#06x}", self.did))
            .field("command", &self.command())
            .field("status", &self.status())
            .field("has_multiple_functions", &self.has_multiple_functions())
            .field("revision_and_class", &self.revision_and_class())
            .finish()
    }
}
