//! POD construction for the cast stream's format and buffer negotiation.

use pipewire::spa;

/// Serialize the `EnumFormat` PODs: with a gbm device, a dmabuf pod first —
/// BGRx (the byte order of the Argb8888 bos) with a mandatory linear modifier,
/// which only dmabuf-capable consumers can fixate — then the CPU-readback pod
/// with RGBx preferred. BGRx is deliberately absent from the second pod so the
/// fixated format identifies the mode.
pub(super) fn build_format_params(width: u32, height: u32, dma_capable: bool) -> Vec<Vec<u8>> {
    use spa::param::format::{FormatProperties, MediaSubtype, MediaType};
    use spa::param::video::VideoFormat;
    use spa::pod::{object, property, Object, Property, PropertyFlags, Value};

    let serialize = |obj: Object| {
        spa::pod::serialize::PodSerializer::serialize(
            std::io::Cursor::new(Vec::new()),
            &Value::Object(obj),
        )
        .unwrap()
        .0
        .into_inner()
    };

    let mut pods = Vec::new();

    if dma_capable {
        let obj = Object {
            type_: spa::utils::SpaTypes::ObjectParamFormat.as_raw(),
            id: spa::param::ParamType::EnumFormat.as_raw(),
            properties: vec![
                Property::new(
                    spa::sys::SPA_FORMAT_mediaType,
                    Value::Id(spa::utils::Id(spa::sys::SPA_MEDIA_TYPE_video)),
                ),
                Property::new(
                    spa::sys::SPA_FORMAT_mediaSubtype,
                    Value::Id(spa::utils::Id(spa::sys::SPA_MEDIA_SUBTYPE_raw)),
                ),
                Property::new(
                    spa::sys::SPA_FORMAT_VIDEO_format,
                    Value::Id(spa::utils::Id(spa::sys::SPA_VIDEO_FORMAT_BGRx)),
                ),
                Property {
                    key: spa::sys::SPA_FORMAT_VIDEO_modifier,
                    flags: PropertyFlags::MANDATORY,
                    value: Value::Long(0), // DRM_FORMAT_MOD_LINEAR
                },
                Property::new(
                    spa::sys::SPA_FORMAT_VIDEO_size,
                    Value::Rectangle(spa::utils::Rectangle { width, height }),
                ),
                Property::new(
                    spa::sys::SPA_FORMAT_VIDEO_framerate,
                    Value::Fraction(spa::utils::Fraction { num: 0, denom: 1 }),
                ),
            ],
        };
        pods.push(serialize(obj));
    }

    let obj = object!(
        spa::utils::SpaTypes::ObjectParamFormat,
        spa::param::ParamType::EnumFormat,
        property!(FormatProperties::MediaType, Id, MediaType::Video),
        property!(FormatProperties::MediaSubtype, Id, MediaSubtype::Raw),
        property!(
            FormatProperties::VideoFormat,
            Choice, Enum, Id,
            VideoFormat::RGBx,
            VideoFormat::RGBx,
            VideoFormat::RGBA,
            VideoFormat::BGRA,
        ),
        property!(
            FormatProperties::VideoSize,
            Rectangle,
            spa::utils::Rectangle { width, height }
        ),
        property!(
            FormatProperties::VideoFramerate,
            Fraction,
            spa::utils::Fraction { num: 0, denom: 1 }
        ),
    );
    pods.push(serialize(obj));

    pods
}

/// Serialize the `Buffers` POD declaring buffers of `size` bytes. `dt_default`
/// is the preferred `1 << SPA_DATA_*` carrier, `dt_mask` every acceptable one;
/// PipeWire pre-picks the default before `add_buffer` runs. Built by hand: the
/// `property!` macro only takes typed keys, and the buffer param keys are raw
/// `spa_sys` constants.
pub(super) fn build_buffers_param(size: i32, stride: i32, dt_default: i32, dt_mask: i32) -> Vec<u8> {
    use spa::pod::{ChoiceValue, Object, Property, Value};
    use spa::utils::{Choice, ChoiceEnum, ChoiceFlags};

    let choice_int = |e: ChoiceEnum<i32>| {
        Value::Choice(ChoiceValue::Int(Choice(ChoiceFlags::empty(), e)))
    };
    let obj = Object {
        type_: spa::utils::SpaTypes::ObjectParamBuffers.as_raw(),
        id: spa::param::ParamType::Buffers.as_raw(),
        properties: vec![
            Property::new(
                spa::sys::SPA_PARAM_BUFFERS_buffers,
                choice_int(ChoiceEnum::Range { default: 4, min: 2, max: 8 }),
            ),
            Property::new(spa::sys::SPA_PARAM_BUFFERS_blocks, Value::Int(1)),
            Property::new(spa::sys::SPA_PARAM_BUFFERS_size, Value::Int(size)),
            Property::new(spa::sys::SPA_PARAM_BUFFERS_stride, Value::Int(stride)),
            Property::new(
                spa::sys::SPA_PARAM_BUFFERS_dataType,
                choice_int(ChoiceEnum::Flags { default: dt_default, flags: vec![dt_mask] }),
            ),
        ],
    };
    spa::pod::serialize::PodSerializer::serialize(
        std::io::Cursor::new(Vec::new()),
        &Value::Object(obj),
    )
    .unwrap()
    .0
    .into_inner()
}
