// Signal-strength icon factory for Wi-Fi rows. Lives in `ui` rather
// than the view because it is raster logic — file reads, byte
// compositing and a process-wide cache — not presentation.

use std::sync::Arc;

const WIFI_ICON_CANVAS: u32 = 24;
// Ordered weakest to strongest; the last entry is the size reference.
const WIFI_ICONS: [ &str; 4 ] = [
	"system/wifi-signal-none",
	"system/wifi-signal-low",
	"system/wifi-signal-medium",
	"system/wifi-signal-full",
];

fn svg_viewbox( name: &str ) -> Option<( f32, f32 )>
{
	let text = std::fs::read_to_string( ltk::theme_icon_path( name )? ).ok()?;
	let vb = text.split( "viewBox=\"" ).nth( 1 )?.split( '"' ).next()?;
	let mut dims = vb.split_whitespace().skip( 2 );
	let w: f32 = dims.next()?.parse().ok()?;
	let h: f32 = dims.next()?.parse().ok()?;
	Some( ( w, h ) )
}

// The signal SVGs' viewBoxes are cropped to their ink, so rasterising them
// all at the same pixel size would draw a lone dot as big as the full fan.
// The per-variant raster size is derived from each viewBox relative to the
// full icon's, computed once from the theme files themselves.
fn wifi_icon_sizes() -> Option<&'static [ u32; 4 ]>
{
	static SIZES: std::sync::OnceLock<Option<[ u32; 4 ]>> = std::sync::OnceLock::new();
	SIZES.get_or_init( ||
	{
		let ( full_w, full_h ) = svg_viewbox( WIFI_ICONS[ 3 ] )?;
		let unit = WIFI_ICON_CANVAS as f32 / full_w.max( full_h );
		let mut out = [ 0u32; 4 ];
		for ( i, name ) in WIFI_ICONS.iter().enumerate()
		{
			let ( w, h ) = svg_viewbox( name )?;
			out[ i ] = ( w.max( h ) * unit ).round().max( 1.0 ) as u32;
		}
		Some( out )
	} ).as_ref()
}

#[ derive( Clone, Copy ) ]
pub( crate ) enum StatusDot
{
	Active,
	Known,
	None,
}

const DOT_CANVAS: u32 = 24;
const DOT_RADIUS: f32 = 5.5;

/// Small filled status circle for connection rows, rendered into the
/// trailing-icon slot. `None` yields a fully transparent buffer so rows
/// without a dot keep their trailing text aligned with the others.
pub( crate ) fn status_dot( kind: StatusDot ) -> ( Arc<Vec<u8>>, u32, u32 )
{
	let mut out = vec![ 0u8; ( DOT_CANVAS * DOT_CANVAS * 4 ) as usize ];
	if let StatusDot::None = kind
	{
		return ( Arc::new( out ), DOT_CANVAS, DOT_CANVAS );
	}
	let fill = match kind
	{
		StatusDot::Active => ltk::Color { r: 0.20, g: 0.78, b: 0.35, a: 1.0 },
		_                 => ltk::Color { r: 1.0,  g: 1.0,  b: 1.0,  a: 1.0 },
	};
	// A hairline in the divider colour keeps the white dot visible on
	// light surfaces.
	let ring = ltk::theme_palette().divider;
	let c  = ( DOT_CANVAS as f32 - 1.0 ) / 2.0;
	let ro = DOT_RADIUS + 1.2;
	for y in 0..DOT_CANVAS
	{
		for x in 0..DOT_CANVAS
		{
			let d  = ( ( x as f32 - c ).powi( 2 ) + ( y as f32 - c ).powi( 2 ) ).sqrt();
			let cf = ( DOT_RADIUS - d + 0.5 ).clamp( 0.0, 1.0 );
			let cr = ( ( ro - d + 0.5 ).clamp( 0.0, 1.0 ) - cf ) * ring.a;
			let a  = cf + cr;
			if a <= 0.0 { continue; }
			let px = | f: f32, r: f32 | ( ( f * cf + r * cr ) / a * 255.0 ).round() as u8;
			let i = ( ( y * DOT_CANVAS + x ) * 4 ) as usize;
			out[ i     ] = px( fill.r, ring.r );
			out[ i + 1 ] = px( fill.g, ring.g );
			out[ i + 2 ] = px( fill.b, ring.b );
			out[ i + 3 ] = ( a * 255.0 ).round() as u8;
		}
	}
	( Arc::new( out ), DOT_CANVAS, DOT_CANVAS )
}

pub( crate ) fn wifi_icon( signal: u8 ) -> Option<( Arc<Vec<u8>>, u32, u32 )>
{
	let idx =
		if signal > 66      { 3 }
		else if signal > 33 { 2 }
		else if signal > 0  { 1 }
		else                { 0 };
	let size = wifi_icon_sizes()?[ idx ];
	let ( rgba, w, h ) = ltk::theme_icon_rgba( WIFI_ICONS[ idx ], size )?;
	if w > WIFI_ICON_CANVAS || h > WIFI_ICON_CANVAS { return None; }
	let tinted = ltk::tint_symbolic( &rgba, ltk::theme_palette().icon );
	let mut out = vec![ 0u8; ( WIFI_ICON_CANVAS * WIFI_ICON_CANVAS * 4 ) as usize ];
	let x0 = ( WIFI_ICON_CANVAS - w ) / 2;
	let y0 = WIFI_ICON_CANVAS - h;
	for row in 0..h
	{
		let src = ( row * w * 4 ) as usize;
		let dst = ( ( ( y0 + row ) * WIFI_ICON_CANVAS + x0 ) * 4 ) as usize;
		out[ dst .. dst + ( w * 4 ) as usize ]
			.copy_from_slice( &tinted[ src .. src + ( w * 4 ) as usize ] );
	}
	Some( ( Arc::new( out ), WIFI_ICON_CANVAS, WIFI_ICON_CANVAS ) )
}
