// Power policy over powermanagerd (`net.liberux.PowerManager`, system bus).

use std::collections::HashMap;

use zbus::blocking::{ Connection, Proxy };
use zbus::zvariant::{ OwnedValue, Value };

const NAME: &str = "net.liberux.PowerManager";
const PATH: &str = "/net/liberux/PowerManager";

/// Detents of the low-battery warning slider, in percent; 0 = never.
pub const BATTERY_WARN_STEPS:     [ u32; 7 ] = [ 0, 5, 10, 15, 20, 25, 30 ];
/// Detents of the critical-suspend slider, in percent; 0 = never.
pub const BATTERY_SUSPEND_STEPS: [ u32; 6 ] = [ 0, 2, 3, 5, 8, 10 ];

#[ derive( Clone, Copy, Debug, Default ) ]
pub struct PowerPolicy
{
	pub battery_warn_percent:     u32,
	pub battery_suspend_percent: u32,
}

fn proxy() -> Option<( Connection, Proxy<'static> )>
{
	let conn  = Connection::system().ok()?;
	let proxy = Proxy::new( &conn, NAME, PATH, NAME ).ok()?;
	Some( ( conn, proxy ) )
}

pub fn power_policy() -> PowerPolicy
{
	let Some( ( _conn, proxy ) ) = proxy() else { return PowerPolicy::default() };
	let dict: HashMap<String, OwnedValue> = proxy.call( "GetPolicy", &() ).unwrap_or_default();
	let get = |key: &str| dict.get( key ).and_then( |v| u32::try_from( v ).ok() ).unwrap_or( 0 );
	PowerPolicy
	{
		battery_warn_percent:     get( "battery_warn_percent" ),
		battery_suspend_percent: get( "battery_suspend_percent" ),
	}
}

pub fn set_power_policy_u32( key: &str, value: u32 )
{
	let Some( ( _conn, proxy ) ) = proxy() else { return };
	let mut values: HashMap<&str, Value> = HashMap::new();
	values.insert( key, Value::from( value ) );
	let _ = proxy.call::<_, _, ()>( "SetPolicy", &( values, ) );
}

/// Detent nearest to a slider position in [0, 1].
pub fn step_at( steps: &[ u32 ], t: f32 ) -> u32
{
	let last = steps.len() - 1;
	let idx  = ( t.clamp( 0.0, 1.0 ) * last as f32 ).round() as usize;
	steps[ idx.min( last ) ]
}

/// Slider position of the detent closest to `value`.
pub fn step_norm( steps: &[ u32 ], value: u32 ) -> f32
{
	let mut best = 0;
	for ( i, s ) in steps.iter().enumerate()
	{
		if s.abs_diff( value ) < steps[ best ].abs_diff( value )
		{
			best = i;
		}
	}
	best as f32 / ( steps.len() - 1 ) as f32
}
