#![allow(clippy::needless_lifetimes)]
pub mod error;
use std::ops::Range;

pub use gpu_backend::Texture;
use ribir_painter::{Color, ColorFormat, GradientStop, VertexBuffers};
use ribir_types::{DevicePoint, DeviceRect, DeviceSize};
mod gpu_backend;
use zerocopy::AsBytes;

#[cfg(feature = "wgpu")]
pub mod wgpu_impl;
pub use gpu_backend::*;
#[cfg(feature = "wgpu")]
pub use wgpu_impl::*;

/// Trait to help implement a gpu backend.
///
/// The call graph:
///
/// -- begin_frame()
///
///   +--->-------- Draw Phase --------------------------+
///   |                                                  |
///   |    +->- new_texture()----+                       |   
///   |    +-<-------<------<----+                       |
///   |                                                  v
///   | -> load_alpha_vertices()                         |
///   |                                                  |
///   | -> + draw_alpha_triangles_with_scissor()--+      |
///   |    ^                                      v      |
///   |    ^----<-----------<---------------------+      |
///   |                                                  |
///   | -> + draw_alpha_triangles()---------------+      |
///   |    ^                                      v      |
///   |    +----<-----------<---------------------+      |
///   |                                                  |
///   | -> load_textures()                               |
///   | -> load_mask_layers()                            |
///   |                                                  |    
///   |        +--------------------------+              |
///   |        |  load_color_vertices()   |              |
///   |     +->|  draw_color_triangles()  |              |
///   |     |  +--------------------------+              |
///   |     |                                            |
///   |     |  +--------------------------+              |
///   |     |  | load_img_primitives()    |              |
///   |     +->| load_image_vertices()    |              |
///   |     |  | draw_img_triangles()     |              |
///   |     |  +--------------------------+              |
///   |     |                                            |
///   | ->  |  +------------------------------------+    |
///   |     |  | load_radial_gradient_primitives()  |    v
///   |     +->| load_radial_gradient_stops()       |    |
///   |     |  | load_radial_gradient_vertices()    |    |
///   |     |  | draw_radial_gradient_triangles()   |    |
///   |     |  +------------------------------------+    |
///   |     |                                            |
///   |     |  +------------------------------------+    |
///   |     |  | load_linear_gradient_primitives()  |    |
///   |     +->| load_linear_gradient_stops()       |    |
///   |        | load_linear_gradient_vertices()    |    |
///   |        | draw_linear_gradient_triangles()   |    |
///   |        +------------------------------------+    |
///   +---<----------------------------------------------+
///
/// -+ ->- end_frame()
///
/// The coordinate always start from the left-top to right-bottom. Vertices
/// use percent as value, and others use pixel value.
///     Vertices Axis           Device axis
///  0  +----x----+> 1       0 +----x-----+> width
///     |         |            |          |   
///     y         |            y          |
///     |         |            |          |
///     +---------+            +----------+
///     v                      v
///     1                     height
pub trait GPUBackendImpl {
  type Texture: Texture;

  /// A frame start, call once per frame
  fn begin_frame(&mut self);

  /// Returns the limits of the GPU backend.
  fn limits(&self) -> &DrawPhaseLimits;

  /// Create a texture.
  fn new_texture(&mut self, size: DeviceSize, format: ColorFormat) -> Self::Texture;
  /// Load the vertices and indices buffer that `draw_alpha_triangles` &
  /// `draw_alpha_triangles_with_scissor` will use.
  fn load_alpha_vertices(&mut self, buffers: &VertexBuffers<()>);
  /// Draw triangles only alpha channel with 1.0. Caller guarantee the texture
  /// format is `ColorFormat::Alpha8`, caller will try to batch as much as
  /// possible, but also possibly call multi times in a frame.
  fn draw_alpha_triangles(
    &mut self, indices: &Range<u32>, texture: &mut Self::Texture, size_offset: u32,
  );
  /// Same behavior as `draw_alpha_triangles`, but the Vertex with a offset and
  /// gives a clip rectangle for the texture, the path should only painting in
  /// the rectangle.
  fn draw_alpha_triangles_with_scissor(
    &mut self, indices: &Range<u32>, texture: &mut Self::Texture, scissor: DeviceRect,
    size_offset: u32,
  );

  fn load_alpha_size(&mut self, size: DeviceSize) -> u32;

  /// load textures that will be use in this draw phase
  fn load_textures(&mut self, textures: &[&Self::Texture]);
  /// load the mask layers that the current draw phase will use, called at
  /// most once per draw phase.
  fn load_mask_layers(&mut self, layers: &[MaskLayer]) -> u32;
  /// Load the vertices and indices buffer that `draw_color_triangles` will
  /// use.
  fn load_color_vertices(&mut self, buffers: &VertexBuffers<ColorAttr>);
  /// Load the vertices and indices buffer that `draw_img_triangles` will use.
  fn load_img_data(
    &mut self, primitives: &[ImgPrimitive], buffers: &VertexBuffers<ImagePrimIndex>,
  ) -> u32;

  /// Load the primitives and gradient color stops that
  /// `draw_radial_gradient_triangles` will use.
  fn load_radial_gradient_data(
    &mut self, primitives: &[RadialGradientPrimitive], stops: &[GradientStopPrimitive],
    buffers: &VertexBuffers<RadialGradientPrimIndex>,
  ) -> (u32, u32);

  /// Load the primitives and gradient color stops that
  /// `draw_linear_gradient_triangles` will use.
  fn load_linear_gradient_data(
    &mut self, primitives: &[LinearGradientPrimitive], stops: &[GradientStopPrimitive],
    buffers: &VertexBuffers<LinearGradientPrimIndex>,
  ) -> (u32, u32);

  /// load the filter
  fn load_filter_data(
    &mut self, primitives: &FilterPrimitive, kernel_matrix: &[f32], buffers: &VertexBuffers<()>,
  ) -> u32;

  /// Draw pure color triangles in the texture. And use the clear color clear
  /// the texture first if it's a Some-Value
  fn draw_color_triangles(
    &mut self, texture: &mut Self::Texture, indices: Range<u32>, clear: Option<Color>,
    mask_offset: u32,
  );
  /// Draw triangles fill with image. And use the clear color clear the texture
  /// first if it's a Some-Value
  fn draw_img_triangles(
    &mut self, texture: &mut Self::Texture, indices: Range<u32>, clear: Option<Color>,
    mask_offset: u32, prims_offset: u32,
  );

  fn draw_filter_triangles(
    &mut self, texture: &mut Self::Texture, origin: &Self::Texture, indices: Range<u32>,
    clear: Option<Color>, mask_offset: u32, prims_offset: u32,
  );

  /// Draw triangles fill with color radial gradient. And use the clear color
  /// clear the texture first if it's a Some-Value
  fn draw_radial_gradient_triangles(
    &mut self, texture: &mut Self::Texture, indices: Range<u32>, clear: Option<Color>,
    mask_offset: u32, prims_offset: u32, stops_offset: u32,
  );

  /// Draw triangles fill with color linear gradient. And use the clear color
  /// clear the texture first if it's a Some-Value
  fn draw_linear_gradient_triangles(
    &mut self, texture: &mut Self::Texture, indices: Range<u32>, clear: Option<Color>,
    mask_offset: u32, prims_offset: u32, stops_offset: u32,
  );

  /// Flush any pending draw commands to the queue.
  fn flush_draw_commands(&mut self);

  fn copy_texture_from_texture(
    &mut self, dest_tex: &mut Self::Texture, copy_to: DevicePoint, from_tex: &Self::Texture,
    from_rect: &DeviceRect,
  );

  /// Load the primitives and vertices that `draw_texture_triangles` will use.
  fn load_texture_data(
    &mut self, primitives: &[TexturePrimitive], buffers: &VertexBuffers<TexturePrimIndex>,
  ) -> u32;

  /// Draw triangles fill with texture. And use the clear color clear the
  /// texture first if it's a Some-Value
  fn draw_texture_triangles(
    &mut self, texture: &mut Self::Texture, indices: Range<u32>, clear: Option<Color>,
    from_texture: &Self::Texture, mask_offset: u32, prims_offset: u32,
  );

  /// A frame end, call once per frame
  fn end_frame(&mut self);
}

/// Represents the sets of limits an GPU backend can provide in a single draw
pub struct DrawPhaseLimits {
  /// The maximum size of the texture that the backend can create.
  pub texture_size: DeviceSize,
  /// The maximum number of textures that the backend can load in a single draw
  pub max_tex_load: usize,
  /// The maximum number of mask layers that the backend can load in a single
  /// draw phase
  pub max_image_primitives: usize,
  /// The maximum number of radial gradient primitives that the backend can load
  /// in a single draw
  pub max_radial_gradient_primitives: usize,
  /// The maximum number of linear gradient primitives that the backend can load
  /// in a single draw
  pub max_linear_gradient_primitives: usize,
  /// The maximum number of gradient stops that the backend can load in a single
  /// draw phase
  pub max_gradient_stop_primitives: usize,
  /// The maximum number of texture primitives that the backend can load in a
  /// single draw phase
  pub max_texture_primitives: usize,

  /// The maximum size of the filter matrix that the backend can load in a
  /// single draw phase
  pub max_filter_matrix_len: usize,
  /// The maximum number of mask layers that the backend can load in a single
  pub max_mask_layers: usize,
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy)]
pub struct ColorAttr {
  /// brush's Rgba color
  pub color: [u8; 4],
  /// The index of the head mask layer.
  pub mask_head: i32,
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct ImagePrimIndex(u32);

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct RadialGradientPrimIndex(u32);

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct LinearGradientPrimIndex(u32);

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct GradientStopPrimitive {
  pub color: u32,
  pub offset: f32,
}

impl GradientStopPrimitive {
  fn new(stop: &GradientStop) -> Self {
    GradientStopPrimitive { color: stop.color.into_u32(), offset: stop.offset }
  }
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct RadialGradientPrimitive {
  /// A 2x3 column-major matrix, transform a vertex position to the texture
  /// position
  pub transform: [f32; 6],
  /// The color stop's start index
  pub stop_start: u32,
  /// The size of the color stop
  pub stop_cnt: u32,
  /// position of the start center
  pub start_center: [f32; 2],
  /// position of the end center
  pub end_center: [f32; 2],
  /// the radius of the start circle.
  pub start_radius: f32,
  /// the radius of the end circle.
  pub end_radius: f32,
  /// The index of the head mask layer.
  pub mask_head: i32,
  /// the spread method of the gradient. 0 for pad, 1 for reflect and 2
  /// for repeat
  pub spread: u32,
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct LinearGradientPrimitive {
  /// A 2x3 column-major matrix, transform a vertex position to the texture
  /// position
  pub transform: [f32; 6],
  /// position of the start center
  pub start_position: [f32; 2],
  /// position of the end center
  pub end_position: [f32; 2],
  /// The color stop information, there are two parts:
  /// - The high 16-bit index represents the start index of the color stop.
  /// - The low 16-bit index represents the size of the color stop.
  pub stop: u32,
  /// A mix of two 16-bit values:
  /// - The high 16-bit index represents the head mask layer.
  /// - The low 16-bit represents the spread method of the gradient. 0 for pad,
  ///   1 for reflect and 2 for repeat
  pub mask_head_and_spread: i32,
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy)]
pub struct ImgPrimitive {
  /// A 2x3 column-major matrix, transform a vertex position to the image
  /// texture slice position.
  pub transform: [f32; 6],
  /// The origin of the image placed in texture.
  pub img_start: [f32; 2],
  /// The size of the image image.
  pub img_size: [f32; 2],

  /// This represents a mix of two 16-bit indices:
  /// - The high 16-bit index represents the head mask layer. It is an i16.
  /// - The low 16-bit index represents the texture. It is a u16.
  pub mask_head_and_tex_idx: i32,

  /// 1 for premultiplied alpha, 0 for non-premultiplied alpha
  pub is_premultiplied: i32,

  /// the final pix color will be color * color_matrix + base_color
  pub base_color: [f32; 4],

  /// the final pix color will be color * color_matrix + base_color
  pub color_matrix: [f32; 4 * 4],
}

/// The mask layer describes an alpha channel layer that is used in the fragment
/// shader to sample the alpha channel and apply it to the color.
#[derive(AsBytes, Clone)]
#[repr(C, packed)]
pub struct MaskLayer {
  /// A 2x3 column-major matrix, transform a vertex position to its mask texture
  /// position for atlas, or local position for sdf.
  pub transform: [f32; 6],
  /// The min bound for atlas texture space, or local rect min for sdf.
  pub min: [f32; 2],
  /// The max bound for atlas texture space, or local rect max for sdf.
  pub max: [f32; 2],
  /// The index of the texture(alpha) that contained this layer,
  /// `load_textures` method provide all textures a draw phase need.
  pub mask_tex_idx: u32,
  /// The index of the previous mask layer needs to continue to be applied. The
  /// negative value means there isn't any more mask layer that needs to be
  /// applied.
  pub prev_mask_idx: i32,
  /// 0 for atlas, 1 for sdf-rect.
  pub kind: u32,
  /// 0 for fill, 1 for stroke.
  pub paint_style: u32,
  /// half stroke width in local space when paint_style is stroke.
  pub stroke_half_width: f32,
  /// anti-aliasing epsilon.
  pub aa_epsilon: f32,
  /// generic sdf parameters. for circle: [cx, cy, r, 0], for round-rect: corner
  /// radii.
  pub p0: [f32; 4],
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MaskKind {
  Atlas = 0,
  Sdf = 1,
}

#[repr(C, packed)]
#[derive(AsBytes, Clone, Copy, Debug, PartialEq)]
pub struct SdfMaskData {
  /// A 2x3 column-major matrix, transform a view position to local position.
  pub transform: [f32; 6],
  /// 1=Rect, 2=RoundRect, 3=Circle.
  pub shape_kind: u32,
  /// Bit flags for fill/stroke/inverse/fill-rule.
  pub flags: u32,
  /// Generic parameters for sdf shape data.
  pub p0: [f32; 4],
  /// Generic parameters for sdf shape data.
  pub p1: [f32; 4],
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct FilterPrimitive {
  pub color_matrix: [f32; 16],
  /// The origin of the image placed in texture.
  /// Used to locate the original image position in the texture.
  pub sample_offset: [f32; 2],
  /// Filter effect offset for drop-shadow etc.
  /// This offset is applied during filter convolution sampling to create
  /// displaced effects like shadows.
  pub offset: [f32; 2],
  /// The origin of the mask layer in the texture.
  pub mask_offset: [f32; 2],
  /// The size of the filter kernel.
  pub kernel_size: [i32; 2],
  /// The index of the head mask layer.
  pub mask_head: i32,
  /// The composite mode for filter application.
  /// - 0 = Replace: filter result completely replaces the original content
  /// - 1 = ExcludeSource: filter result is only applied where original alpha is
  ///   0
  pub composite: i32,

  /// Explicit padding to align `base_color` to the 16-byte boundary required
  /// by WGSL `vec4<f32>`. Do NOT remove — removing this will corrupt the GPU
  /// struct layout and cause `base_color` to be read as zeros.
  pub dummy: [f32; 2],
  /// the final pix color will be color * color_matrix + base_color
  pub base_color: [f32; 4],
}

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy, Debug)]
pub struct TexturePrimIndex(pub u32);

#[repr(C, packed)]
#[derive(AsBytes, PartialEq, Clone, Copy)]
pub struct TexturePrimitive {
  /// A 2x3 column-major matrix, transform a vertex position to the texture
  /// position
  pub transform: [f32; 6],
  /// The index of the head mask layer.
  pub mask_head: i32,
  /// opacity
  pub opacity: f32,
  /// The index of the texture.
  /// 1 for premultiplied alpha, 0 for non-premultiplied alpha
  pub is_premultiplied: u32,
  /// dummy for align
  pub _padding: [u32; 3],
}
