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

//! Construction + accessors for [`SoftwareCanvas`]: new / sub_canvas
//! / resize plus the font-registry installer and `blit`.

use std::collections::HashMap;
use std::sync::Arc;

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

use crate::system_fonts::default_handle;
use crate::theme::{ FontRegistry, FontStyle };

use super::SoftwareCanvas;

impl SoftwareCanvas
{
	/// Create a canvas of the given pixel dimensions, loading a system font.
	///
	/// Fallback fonts (Noto Sans / CJK / Devanagari / …) are NOT loaded
	/// here — they are owned by the crate-private system-fonts chain
	/// and loaded lazily per codepoint. A canvas that only ever paints
	/// Latin text will never touch those files; the first non-Latin
	/// glyph triggers a single targeted load and the rest of the
	/// process reuses the cached `Arc<Font>`.
	pub fn new( width: u32, height: u32 ) -> Self
	{
		let handle = default_handle();
		Self
		{
			pixmap:          Pixmap::new( width, height ).expect( "pixmap" ),
			font:            handle.font.clone(),
			font_bytes:      handle.bytes.clone(),
			font_face:       handle.face,
			font_registry:   None,
			dpi_scale:       1.0,
			layout_viewport: None,
			density:         None,
			global_alpha:    1.0,
			glyph_cache:     HashMap::new(),
			clip_mask:       None,
			clip_bounds:     Vec::new(),
		}
	}

	/// Create a blank sub-canvas sharing the same font and DPI scale.
	pub fn sub_canvas( &self, width: u32, height: u32 ) -> SoftwareCanvas
	{
		SoftwareCanvas
		{
			pixmap:         Pixmap::new( width, height ).expect( "pixmap" ),
			font:           Arc::clone( &self.font ),
			font_bytes:     Arc::clone( &self.font_bytes ),
			font_face:      self.font_face,
			font_registry:  self.font_registry.as_ref().map( Arc::clone ),
			dpi_scale:      self.dpi_scale,
			layout_viewport: Some( self.layout_viewport.unwrap_or(
				( self.pixmap.width() as f32, self.pixmap.height() as f32 ) ) ),
			density:        self.density,
			global_alpha:   self.global_alpha,
			glyph_cache:    HashMap::new(),
			clip_mask:      None,
			clip_bounds:    Vec::new(),
		}
	}

	/// Install a theme font registry so [`Self::font_for`] can
	/// resolve family+weight+style triples declared by the theme's
	/// `fonts` block. The default [`Self::font`] stays in place as a
	/// fallback.
	pub fn set_font_registry( &mut self, registry: Arc<FontRegistry> )
	{
		self.font_registry = Some( registry );
	}

	/// Resolve a specific font from the theme registry, falling back
	/// to the canvas' default [`Self::font`] when no registry is
	/// installed or the triple cannot be satisfied.
	pub fn font_for( &self, family: &str, weight: u16, style: FontStyle ) -> Arc<Font>
	{
		self.font_registry
			.as_ref()
			.and_then( |r| r.resolve( family, weight, style ) )
			.unwrap_or_else( || Arc::clone( &self.font ) )
	}

	/// Pick the right font for `ch`. Tries the primary [`Self::font`]
	/// first; on a miss, delegates to the crate-private system-fonts
	/// fallback chain (lazy load of the relevant Noto pack). Falls
	/// back to the primary (which paints a `.notdef` box) when no
	/// installed fallback covers the codepoint.
	pub fn font_for_char( &self, ch: char ) -> Arc<Font>
	{
		if self.font.lookup_glyph_index( ch ) != 0
		{
			return Arc::clone( &self.font );
		}
		crate::system_fonts::lookup( ch ).unwrap_or_else( || Arc::clone( &self.font ) )
	}

	/// Bytes-aware variant of [`Self::font_for_char`]. Returns the
	/// full `FontHandle` so callers that invoke the HarfBuzz shaper
	/// can hand the raw bytes directly to rustybuzz. The primary
	/// font supplies the bytes from
	/// [`Self::font_bytes`]; fallback chars borrow the bytes that
	/// were loaded into the system fallback cache.
	pub fn font_handle_for_char( &self, ch: char ) -> crate::system_fonts::FontHandle
	{
		if self.font.lookup_glyph_index( ch ) != 0
		{
			return crate::system_fonts::FontHandle
			{
				font:  Arc::clone( &self.font ),
				bytes: Arc::clone( &self.font_bytes ),
				face:  self.font_face,
			};
		}
		crate::system_fonts::lookup_handle( ch ).unwrap_or_else( ||
			crate::system_fonts::FontHandle
			{
				font:  Arc::clone( &self.font ),
				bytes: Arc::clone( &self.font_bytes ),
				face:  self.font_face,
			}
		)
	}

	pub fn blit( &mut self, src: &SoftwareCanvas, dest_x: i32, dest_y: i32 )
	{
		let paint = PixmapPaint::default();
		let t     = Transform::from_translate( dest_x as f32, dest_y as f32 );
		self.pixmap.draw_pixmap( 0, 0, src.pixmap.as_ref(), &paint, t, self.clip_mask.as_ref() );
	}

	pub fn resize( &mut self, width: u32, height: u32 )
	{
		self.pixmap = Pixmap::new( width, height ).expect( "pixmap" );
	}
}
