// Multi-monitor state over forge's IPC: inventory, per-connector settings
// and the mirror/extend session mode.

use serde::Deserialize;

use super::desktop::forge_request;

#[ derive( Clone, Debug, Default, Deserialize ) ]
pub struct Output
{
	pub connector: String,
	#[ serde( default ) ]
	pub make:          String,
	#[ serde( default ) ]
	pub model:         String,
	#[ serde( default ) ]
	pub external:      bool,
	#[ serde( default ) ]
	pub primary:       bool,
	#[ serde( default ) ]
	pub enabled:       bool,
	/// Scale the connector's own panel DPI asks for.
	#[ serde( default ) ]
	pub auto_scale:    f64,
	/// The user's pick; `0.0` means automatic.
	#[ serde( default ) ]
	pub scale:         f64,
	/// Scale actually in force right now (absent when the connector is
	/// not lit, e.g. disabled).
	#[ serde( default ) ]
	pub scale_applied: f64,
	#[ serde( default ) ]
	pub current_mode:  String,
	#[ serde( default ) ]
	pub modes:         Vec<String>,
	/// Live top-left corner in global logical coordinates.
	#[ serde( default ) ]
	pub position:      Option<( i32, i32 )>,
	/// Live logical size, present while the connector is lit.
	#[ serde( default ) ]
	pub logical_size:  Option<( i32, i32 )>,
}

#[ derive( Clone, Debug, Default ) ]
pub struct Outputs
{
	pub extend:  bool,
	pub outputs: Vec<Output>,
}

/// Where a screen sits relative to the primary.
#[ derive( Clone, Copy, PartialEq, Eq, Debug ) ]
pub enum Placement
{
	Left,
	Right,
	Above,
	Below,
}

/// Read the placement back from a live position, given the primary's
/// logical size.
pub fn placement_of( position: Option<( i32, i32 )>, primary: ( i32, i32 ) ) -> Placement
{
	match position
	{
		Some( ( x, _ ) ) if x < 0            => Placement::Left,
		Some( ( _, y ) ) if y < 0            => Placement::Above,
		Some( ( _, y ) ) if y >= primary.1   => Placement::Below,
		_                                    => Placement::Right,
	}
}

pub fn list_outputs() -> Option<Outputs>
{
	let reply = forge_request( r#"{"action":"list_outputs"}"# )?;
	let v: serde_json::Value = serde_json::from_str( reply.trim() ).ok()?;
	let extend  = v.get( "mode" )?.as_str()? == "extend";
	let outputs = serde_json::from_value( v.get( "outputs" )?.clone() ).ok()?;
	Some( Outputs { extend, outputs } )
}

pub fn set_display_mode( extend: bool )
{
	let mode = if extend { "extend" } else { "mirror" };
	let _ = forge_request( &format!( r#"{{"action":"set_display_mode","mode":"{mode}"}}"# ) );
}

/// Pin one connector's scale; `0.0` returns it to its panel-DPI automatic.
pub fn set_output_scale_pick( connector: &str, scale: f64 )
{
	let _ = forge_request( &format!(
		r#"{{"action":"set_output_config","connector":"{connector}","scale":{scale:.2}}}"#
	) );
}

pub fn set_output_position( connector: &str, x: i32, y: i32 )
{
	let _ = forge_request( &format!(
		r#"{{"action":"set_output_config","connector":"{connector}","position":[{x},{y}]}}"#
	) );
}

pub fn set_output_mode( connector: &str, mode: &str )
{
	let _ = forge_request( &format!(
		r#"{{"action":"set_output_config","connector":"{connector}","mode":"{mode}"}}"#
	) );
}

pub fn set_output_enabled( connector: &str, enabled: bool )
{
	let _ = forge_request( &format!(
		r#"{{"action":"set_output_config","connector":"{connector}","enabled":{enabled}}}"#
	) );
}

/// `"1920x1080@60"` → `"1920 × 1080 · 60 Hz"`.
pub fn mode_label( mode: &str ) -> String
{
	let ( res, hz ) = mode.split_once( '@' ).unwrap_or( ( mode, "" ) );
	let res = res.replace( 'x', " × " );
	if hz.is_empty() { res } else { format!( "{res} · {hz} Hz" ) }
}
