mod app;
mod appinfo;
mod assets;
mod config;
mod dock;
mod dock_reveal;
mod drag;
mod editmenu;
mod homescreen;
mod ipc;
mod launcher;
mod message;
mod mpris;
mod osd;
mod overlays;
mod paging;
mod powermenu;
mod quicksettings;
mod running;
mod splash;
mod state;
mod system;

use anyhow::Result;

rust_i18n::i18n!( "locales", fallback = "en" );

fn parse_locale( val: &str ) -> Option<String>
{
	let val = val.trim();
	if val.is_empty() || val == "C" || val == "POSIX" { return None; }
	Some( val.split( '.' ).next().unwrap_or( val ).to_string() )
}

fn read_system_locale( path: &str ) -> Option<String>
{
	let content = std::fs::read_to_string( path ).ok()?;
	for line in content.lines()
	{
		if let Some( val ) = line.strip_prefix( "LANG=" )
		{
			return parse_locale( val.trim_matches( '"' ) );
		}
	}
	None
}

fn best_locale( candidate: String ) -> Option<String>
{
	let available = rust_i18n::available_locales!();
	if available.contains( &candidate.as_str() ) { return Some( candidate ); }
	let lang = candidate.split( '_' ).next().unwrap_or( &candidate ).to_string();
	if available.contains( &lang.as_str() ) { return Some( lang ); }
	None
}

fn detect_locale() -> String
{
	for var in &[ "LC_ALL", "LC_MESSAGES", "LANG" ]
	{
		if let Ok( val ) = std::env::var( var )
		{
			if let Some( locale ) = parse_locale( &val ).and_then( best_locale )
			{
				return locale;
			}
		}
	}
	for path in &[ "/etc/locale.conf", "/etc/default/locale" ]
	{
		if let Some( locale ) = read_system_locale( path ).and_then( best_locale )
		{
			return locale;
		}
	}
	"en".to_string()
}

fn main() -> Result<()>
{
	tracing_subscriber::fmt()
		.with_target( false )
		.with_env_filter(
			tracing_subscriber::EnvFilter::from_default_env()
				.add_directive( tracing::Level::INFO.into() )
				.add_directive( "usvg=error".parse().unwrap() )
				.add_directive( "resvg=error".parse().unwrap() ),
		)
		.init();

	tracing::info!( "Crustace shell starting..." );
	rust_i18n::set_locale( &detect_locale() );

	let mut config = config::Config::load().unwrap_or_else( |e|
	{
		tracing::warn!( "Failed to load config: {}, using defaults", e );
		config::Config::default()
	} );

	let force_light = std::env::args().any( |a| a == "--light" );
	let force_dark  = std::env::args().any( |a| a == "--dark" );

	// CLI override beats config. `--dark` wins if both are passed.
	if force_dark       { config.shell.theme = ltk::ThemePreference::Dark;  }
	else if force_light { config.shell.theme = ltk::ThemePreference::Light; }

	tracing::info!( "Crustace mode: {:?}", config.display.mode );

	use chrono::Timelike as _;
	let hour = chrono::Local::now().hour();
	let resolved_mode = config.shell.theme.resolve( hour );

	// Install the active theme. Hard-fails if the `default` theme cannot be
	// located — the `ltk-theme-default` package (Provides: ltk-theme) or
	// an `LTK_THEMES_DIR` pointing at a dev checkout must supply it.
	let doc = ltk::ThemeDocument::find( "default" ).unwrap_or_else( |e|
	{
		tracing::error!( "crustace: default theme not loadable ({e}). Install ltk-theme-default or set LTK_THEMES_DIR." );
		std::process::exit( 1 );
	} );
	let doc_id = doc.id.clone();
	ltk::set_active_document( doc );
	ltk::set_active_mode( resolved_mode );
	tracing::info!(
		"Crustace theme: {} / {:?} (selection: {:?})",
		doc_id, resolved_mode, config.shell.theme
	);

	app::run( config );

	Ok( () )
}
