// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Image draw + SHM serialisation for [`SoftwareCanvas`].
//!
//! `draw_image_data` premultiplies the incoming straight-alpha RGBA
//! into a thread-local scratch buffer, wraps the result in a
//! short-lived tiny-skia pixmap, and composites it into `self.pixmap`
//! honouring the active clip mask + global alpha + `opacity`.
//!
//! `write_to_wayland_buf` is the serialisation path to the `wl_shm`
//! pool used by the software draw path. Either memcpys straight
//! (Abgr8888 matches tiny-skia's byte order) or swaps R/B in blocks
//! of four pixels (Argb8888 fallback).

use tiny_skia::{ Pixmap, PixmapPaint, Transform };

use crate::types::Rect;

use super::SoftwareCanvas;

impl SoftwareCanvas
{
	pub fn draw_image_data( &mut self, rgba_data: &[u8], img_w: u32, img_h: u32, dest: Rect, opacity: f32 )
	{
		if !super::helpers::validate_rgba_dims( "SoftwareCanvas", rgba_data, img_w, img_h )
		{
			return;
		}

		// Snap the destination to integer pixels (matching the GLES backend) so a
		// fractional dest does not sub-texel-offset the bilinear sample and read
		// ~1 px softer than the source; at 1:1 the scale collapses to identity.
		let dest = Rect
		{
			x:      dest.x.round(),
			y:      dest.y.round(),
			width:  dest.width.round(),
			height: dest.height.round(),
		};

		let Some( int_size ) = tiny_skia::IntSize::from_wh( img_w, img_h ) else { return };

		thread_local! {
			static PREMUL_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new( Vec::new() );
		}

		PREMUL_BUF.with( |cell|
		{
			let mut premul = cell.borrow_mut();
			let needed = rgba_data.len();
			premul.resize( needed, 0 );
			for ( dst, src ) in premul.chunks_exact_mut( 4 ).zip( rgba_data.chunks_exact( 4 ) )
			{
				let a = (src[3] as f32 / 255.0) * opacity * self.global_alpha;
				dst[0] = (src[0] as f32 * a) as u8;
				dst[1] = (src[1] as f32 * a) as u8;
				dst[2] = (src[2] as f32 * a) as u8;
				dst[3] = (a * 255.0) as u8;
			}

			if let Some( src_pixmap ) = Pixmap::from_vec( std::mem::take( &mut *premul ), int_size )
			{
				let sx = dest.width / img_w as f32;
				let sy = dest.height / img_h as f32;
				let t  = Transform::from_scale( sx, sy ).post_translate( dest.x, dest.y );
				let paint = PixmapPaint
				{
					quality: tiny_skia::FilterQuality::Bilinear,
					..PixmapPaint::default()
				};
				self.pixmap.draw_pixmap( 0, 0, src_pixmap.as_ref(), &paint, t, self.clip_mask.as_ref() );
				*premul = src_pixmap.take();
			}
		} );
	}

	pub fn write_to_wayland_buf( &self, buf: &mut [u8], swap_rb: bool )
	{
		let src = self.pixmap.data();
		let len = src.len().min( buf.len() );

		if !swap_rb
		{
			buf[..len].copy_from_slice( &src[..len] );
			return;
		}

		let chunks = len / 16;
		let remainder = len % 16;
		let mut i = 0;
		for _ in 0..chunks
		{
			buf[i]      = src[i + 2];
			buf[i + 1]  = src[i + 1];
			buf[i + 2]  = src[i];
			buf[i + 3]  = src[i + 3];
			buf[i + 4]  = src[i + 6];
			buf[i + 5]  = src[i + 5];
			buf[i + 6]  = src[i + 4];
			buf[i + 7]  = src[i + 7];
			buf[i + 8]  = src[i + 10];
			buf[i + 9]  = src[i + 9];
			buf[i + 10] = src[i + 8];
			buf[i + 11] = src[i + 11];
			buf[i + 12] = src[i + 14];
			buf[i + 13] = src[i + 13];
			buf[i + 14] = src[i + 12];
			buf[i + 15] = src[i + 15];
			i += 16;
		}
		for _ in 0..(remainder / 4)
		{
			buf[i]     = src[i + 2];
			buf[i + 1] = src[i + 1];
			buf[i + 2] = src[i];
			buf[i + 3] = src[i + 3];
			i += 4;
		}
	}
}
