use scroll::{LE, Pread};

use super::{CommandId, Request, SendError, Status};

#[repr(u8)]
#[expect(unused)]
#[derive(Copy, Clone, Debug)]
pub enum TransportRequest {
    NoTransport = 0,
    DataCommand = 1,
    WinUsbEndpoint = 2,
}

impl Request for TransportRequest {
    const COMMAND_ID: CommandId = CommandId::SwoTransport;

    type Response = TransportResponse;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        buffer[0] = *self as u8;
        Ok(1)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        Ok(TransportResponse {
            status: Status::from_byte(buffer[0])?,
        })
    }
}

#[derive(Debug)]
pub struct TransportResponse {
    pub(crate) status: Status,
}

#[repr(u8)]
#[expect(unused)]
#[derive(Copy, Clone, Debug)]
pub enum ModeRequest {
    Off = 0,
    Uart = 1,
    Manchester = 2,
}

impl Request for ModeRequest {
    const COMMAND_ID: CommandId = CommandId::SwoMode;

    type Response = ModeResponse;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        buffer[0] = *self as u8;
        Ok(1)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        Ok(ModeResponse {
            status: Status::from_byte(buffer[0])?,
        })
    }
}

#[derive(Debug)]
pub struct ModeResponse {
    pub(crate) status: Status,
}

#[derive(Copy, Clone, Debug)]
pub struct BaudrateRequest {
    pub(crate) baudrate: u32,
}

impl Request for BaudrateRequest {
    const COMMAND_ID: CommandId = CommandId::SwoBaudrate;

    type Response = u32;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        assert!(
            buffer.len() >= 4,
            "Buffer for CMSIS-DAP command is too small. This is a bug, please report it."
        );
        buffer[0..4].copy_from_slice(&self.baudrate.to_le_bytes());
        Ok(4)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        if buffer.len() < 4 {
            return Err(SendError::NotEnoughData);
        }

        let baud: u32 = buffer
            .pread_with(0, LE)
            .map_err(|_| SendError::NotEnoughData)?;

        Ok(baud)
    }
}

#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum ControlRequest {
    Stop = 0,
    Start = 1,
}

impl Request for ControlRequest {
    const COMMAND_ID: CommandId = CommandId::SwoControl;

    type Response = ControlResponse;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        buffer[0] = *self as u8;
        Ok(1)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        Ok(ControlResponse {
            status: Status::from_byte(buffer[0])?,
        })
    }
}

#[derive(Debug)]
pub struct ControlResponse {
    pub(crate) status: Status,
}

#[derive(Debug)]
pub struct StatusRequest;

impl Request for StatusRequest {
    const COMMAND_ID: CommandId = CommandId::SwoStatus;

    type Response = StatusResponse;

    fn to_bytes(&self, _buffer: &mut [u8]) -> Result<usize, SendError> {
        Ok(0)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        let status = TraceStatus::from(buffer[0]);
        let count = u32::from_le_bytes(
            buffer[1..5]
                .try_into()
                .map_err(|_| SendError::NotEnoughData)?,
        );
        Ok(StatusResponse {
            _status: status,
            _count: count,
        })
    }
}

#[derive(Copy, Clone, Debug)]
pub struct TraceStatus {
    pub(crate) _active: bool,
    pub(crate) error: bool,
    pub(crate) _overrun: bool,
}

impl From<u8> for TraceStatus {
    fn from(value: u8) -> Self {
        Self {
            _active: value & (1 << 0) != 0,
            error: value & (1 << 6) != 0,
            _overrun: value & (1 << 7) != 0,
        }
    }
}

#[derive(Debug)]
pub struct StatusResponse {
    pub(crate) _status: TraceStatus,
    pub(crate) _count: u32,
}

#[derive(Debug)]
pub struct ExtendedStatusRequest {
    pub(crate) request_status: bool,
    pub(crate) request_count: bool,
    pub(crate) request_index_timestamp: bool,
}

impl Request for ExtendedStatusRequest {
    const COMMAND_ID: CommandId = CommandId::SwoExtendedStatus;

    type Response = ExtendedStatusResponse;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        let control = (self.request_status as u8)
            | ((self.request_count as u8) << 1)
            | ((self.request_index_timestamp as u8) << 2);
        buffer[0] = control;
        Ok(1)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        if buffer.len() < 13 {
            return Err(SendError::NotEnoughData);
        }

        let status = TraceStatus::from(buffer[0]);
        let count = u32::from_le_bytes(
            buffer[1..5]
                .try_into()
                .map_err(|_| SendError::NotEnoughData)?,
        );
        let index = u32::from_le_bytes(
            buffer[5..9]
                .try_into()
                .map_err(|_| SendError::NotEnoughData)?,
        );
        let timestamp = u32::from_le_bytes(
            buffer[9..13]
                .try_into()
                .map_err(|_| SendError::NotEnoughData)?,
        );
        Ok(ExtendedStatusResponse {
            _status: status,
            _count: count,
            _index: index,
            _timestamp: timestamp,
        })
    }
}

#[derive(Debug)]
pub struct ExtendedStatusResponse {
    pub(crate) _status: TraceStatus,
    pub(crate) _count: u32,
    pub(crate) _index: u32,
    pub(crate) _timestamp: u32,
}

#[derive(Debug)]
pub struct DataRequest {
    pub(crate) max_count: u16,
}

impl Request for DataRequest {
    const COMMAND_ID: CommandId = CommandId::SwoData;

    type Response = DataResponse;

    fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, SendError> {
        assert!(
            buffer.len() >= 2,
            "Buffer for CMSIS-DAP command is too small. This is a bug, please report it."
        );
        buffer[0..2].copy_from_slice(&self.max_count.to_le_bytes());
        Ok(2)
    }

    fn parse_response(&self, buffer: &[u8]) -> Result<Self::Response, SendError> {
        let status = TraceStatus::from(buffer[0]);
        let count = u16::from_le_bytes(
            buffer[1..3]
                .try_into()
                .map_err(|_| SendError::NotEnoughData)?,
        );
        let start = 3;
        let end = start + count as usize;
        if end > buffer.len() {
            return Err(SendError::NotEnoughData);
        }

        Ok(DataResponse {
            status,
            data: buffer[start..end].to_vec(),
        })
    }
}

#[derive(Debug)]
pub struct DataResponse {
    pub(crate) status: TraceStatus,
    pub(crate) data: Vec<u8>,
}
