//! Device data structures and options

use crate::{io::terminal, process::ProcessGroupId};

/// Represents a video device framebuffer
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Framebuffer {
    /// Width in pixels
    pub width: u32,
    /// Height in pixels
    pub height: u32,
    /// Amount of bytes to add to move from one row to the next
    pub stride: usize,
    /// Size of the framebuffer in bytes
    pub size: usize,
}

request_group!(
    #[doc = "Terminal device requests"]
    pub enum TerminalRequestVariant<'de> {
        #[doc = "Update the terminal options"]
        0x1000: SetTerminalOptions(terminal::TerminalOptions, ()),
        #[doc = "Query the terminal options"]
        0x1001: GetTerminalOptions((), terminal::TerminalOptions),
        #[doc = "Update the terminal size"]
        0x1002: SetTerminalSize(terminal::TerminalSize, ()),
        #[doc = "Query the terminal size"]
        0x1003: GetTerminalSize((), terminal::TerminalSize),
        #[doc = "Update the terminal's foreground process group ID"]
        0x1004: SetTerminalGroup(ProcessGroupId, ()),
        #[doc = "Dummy option to test if a device is a terminal"]
        0x1005: IsTerminal((), ()),
    }
);

request_group!(
    #[doc = "Common device requests"]
    pub enum DeviceRequestVariant<'de> {
        #[doc = "\"Acquire\" ownership of the device, preventing others from accessing it"]
        0x0001: AcquireDevice((), ()),
        #[doc = "\"Releases\" ownership of the device"]
        0x0002: ReleaseDevice((), ()),
    }
);

request_group!(
    #[doc = "Video/graphics device requests"]
    pub enum VideoRequestVariant<'de> {
        #[doc = "Retrieves the display's currently active framebuffer info"]
        0x2000: GetActiveFramebuffer((), Framebuffer),
        // TODO allow flushing a region of the display
        #[doc = "Tells the display driver to send the framebuffer data to the display"]
        0x2001: FlushDisplay((), ()),
    }
);

/// I²C device requests
pub mod i2c {
    /// I²C master device information
    #[derive(Debug, Clone, Copy)]
    #[repr(C)]
    pub struct I2CMasterInfo {
        /// Maximum clock rate supported by the controller
        pub max_speed_hz: u32,
        /// `true` if the controller supports 10-bit addressing mode
        pub supports_10bit_addresses: bool,
    }

    request_group!(
        #[doc = "I²C device requests"]
        pub enum I2CRequestVariant<'de> {
            #[doc = "Selects the address on the I²C bus"]
            0x2000: SetAddress(u16, ()),
            #[doc = "Configures device speed (in Hertz)"]
            0x2001: SetSpeed(u32, u32),
            #[doc = "Queries I²C master capabilities"]
            0x2010: GetMasterInfo((), I2CMasterInfo),
        }
    );

    abi_serde::impl_struct_serde!(I2CMasterInfo: [
        max_speed_hz, supports_10bit_addresses
    ]);
}

/// SPI device requests
pub mod spi {
    #![allow(missing_docs)]

    use crate::{bitflags, primitive_enum};

    /// Represents a single SPI transfer
    #[derive(Clone, Copy, Debug)]
    #[repr(C)]
    pub struct SpiTransfer {
        /// Tx buffer base pointer
        pub tx_buffer: *const u8,
        /// Rx buffer base pointer
        pub rx_buffer: *mut u8,
        /// Transfer size
        pub len: usize,
    }

    primitive_enum! {
        pub enum SpiClockPolarity: u32 {
            IdleLow = 0,
            IdleHigh = 1,
        }
        [with_serde]
    }

    primitive_enum! {
        pub enum SpiClockPhase: u32 {
            CaptureOnFirstTransition = 0,
            CaptureOnSecondTransition = 1
        }
        [with_serde]
    }

    #[derive(Clone, Copy, Debug, Default)]
    #[repr(C)]
    pub struct SpiConfig {
        pub clock_polarity: Option<SpiClockPolarity>,
        pub clock_phase: Option<SpiClockPhase>,
        pub frequency: Option<u32>,
        pub chip_select_index: Option<u32>,
    }

    bitflags! {
        #[doc = "SPI supported bits-per-word settings"]
        pub struct SpiSupportedBitsPerWord: u32 {
            #[doc = "8-bit words support"]
            const BITS_8: bit 0;
        }
    }

    /// SPI controller capabilities
    pub struct SpiCapabilities {
        /// Maximum clock rate supported
        pub max_speed_hz: u32,
        /// Supports SPI master mode
        pub supports_master_mode: bool,
        /// Supports SPI slave mode
        pub supports_slave_mode: bool,
        /// Mask
        pub supported_bits_per_word: u32,
    }

    request_group!(
        #[doc = "SPI device requests"]
        pub enum SpiRequestVariant<'de> {
            #[doc = "Perform a SPI transfer"]
            0x2000: Transfer(SpiTransfer, usize),
            #[doc = "Set SPI configuration"]
            0x2001: SetConfig(SpiConfig, ()),
        }
    );

    abi_serde::impl_struct_serde!(SpiTransfer: [
        tx_buffer, rx_buffer, len
    ]);

    abi_serde::impl_struct_serde!(SpiConfig: [
        clock_polarity, clock_phase, frequency, chip_select_index
    ]);
}

abi_serde::impl_struct_serde!(Framebuffer: [
    width, height, stride, size
]);
