//! Render commands for a single row of strips.

// Copyright 2026 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::coarse::depth::BucketRange;
use crate::peniko::BlendMode;
use crate::util::Span;
use core::num::NonZeroU32;
use vello_common::mask::Mask;
use vello_common::paint::Paint;

// TODO: If we wanted to, we could likely reduce the memory footprint from 16 bytes to 8 bytes
// by using 4 bits from a span (since tile size is 4, for `x` and `width` we can always store
// the value divided by 4), and creating a bit-packed representation of all commands.

/// A bucketed render command.
#[derive(Debug, Clone, Copy)]
pub(crate) enum RenderCmd {
    /// See [`PaintFill`].
    PaintFill(PaintFill),
    /// Push a new temporary layer buffer.
    PushBuf(Option<Span>),
    /// Pop the last temporary layer buffer.
    PopBuf,
    /// See [`LayerFill`].
    LayerFill(LayerFill),
}

/// Fill a span with the given paint and optionally some alpha coverage.
#[derive(Debug, Clone, Copy)]
pub(crate) struct PaintFill {
    pub(crate) span: Span,
    alpha_idx: Option<AlphaIdx>,
    pub(crate) attrs_idx: u32,
}

impl PaintFill {
    pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self {
        Self {
            span,
            alpha_idx: alpha_idx.map(AlphaIdx::new),
            attrs_idx,
        }
    }

    pub(crate) fn alpha_idx(self) -> Option<u32> {
        self.alpha_idx.map(AlphaIdx::get)
    }
}

/// Fill a whole range of depth buckets with the given paint.
#[derive(Debug, Clone, Copy)]
pub(crate) struct DepthFill {
    bucket_range: BucketRange,
    pub(crate) attrs_idx: u32,
}

impl DepthFill {
    pub(crate) fn new(bucket_range: BucketRange, attrs_idx: u32) -> Self {
        Self {
            bucket_range,
            attrs_idx,
        }
    }

    pub(crate) fn bucket_range(self) -> BucketRange {
        self.bucket_range
    }

    pub(crate) fn span(self) -> Span {
        self.bucket_range.span()
    }
}

/// Composite a span from the current temporary layer buffer into the parent
/// buffer and optionally apply some alpha coverage.
#[derive(Debug, Clone, Copy)]
pub(crate) struct LayerFill {
    pub(crate) span: Span,
    alpha_idx: Option<AlphaIdx>,
    pub(crate) attrs_idx: u32,
}

impl LayerFill {
    pub(crate) fn new(span: Span, alpha_idx: Option<u32>, attrs_idx: u32) -> Self {
        Self {
            span,
            alpha_idx: alpha_idx.map(AlphaIdx::new),
            attrs_idx,
        }
    }

    pub(crate) fn alpha_idx(self) -> Option<u32> {
        self.alpha_idx.map(AlphaIdx::get)
    }
}

// We use `NonZeroU32` so that `Option<AlphaIdx>` still only needs 4 bytes.
#[derive(Debug, Clone, Copy)]
struct AlphaIdx(NonZeroU32);

impl AlphaIdx {
    fn new(alpha_idx: u32) -> Self {
        Self(NonZeroU32::new(alpha_idx.checked_add(1).expect("alpha index overflow")).unwrap())
    }

    fn get(self) -> u32 {
        self.0.get() - 1
    }
}

#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct PaintFillAttrs {
    pub paint: Paint,
    pub blend_mode: BlendMode,
    pub mask: Option<Mask>,
    pub draw_id: u32,
    pub thread_idx: u8,
    /// See the comment in `CommandBucketer::bucket_commands`.
    pub origin: (u16, u16),
}

#[derive(Debug, Clone)]
pub(crate) struct LayerFillAttrs {
    pub(crate) blend_mode: BlendMode,
    pub(crate) opacity: f32,
    pub(crate) mask: Option<Mask>,
    pub(crate) draw_id: u32,
    /// In case there is any alpha associated with the layer command, this stores
    /// the index of the thread that stores the alpha.
    pub(crate) thread_idx: u8,
}

#[cfg(test)]
mod tests {
    use super::{AlphaIdx, RenderCmd};
    use core::mem::{needs_drop, size_of};

    #[test]
    fn alpha_idx_size() {
        assert_eq!(size_of::<Option<AlphaIdx>>(), 4);
    }

    #[test]
    fn render_cmd_assertions() {
        assert_eq!(size_of::<RenderCmd>(), 16);
        assert!(!needs_drop::<RenderCmd>());
    }
}
