/* Copyright (C) 2016 Canonical Ltd.
 * Author: Luke Yelavich <luke.yelavich@canonical.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

private class ProfileIndicator : Object
{
	private Settings profile_settings;
	private Settings a11y_settings;
	private string current_profile;
	private AppIndicator.Indicator indicator;
	private bool installer_mode;
	private Pid at_pid = 0;

	private HashTable<Gtk.CheckMenuItem, A11yProfileManager.Profile> profiles;
	private unowned Gtk.CheckMenuItem active_item;
	private Gtk.RadioMenuItem no_profile_item;

	private ProfileIndicator(bool mode)
	{
		this.profile_settings = new Settings("com.canonical.a11y-profile-manager");
		this.a11y_settings = new Settings("org.gnome.desktop.a11y");
		this.current_profile = this.profile_settings.get_string("active-profile");
		this.installer_mode = mode;

		this.profiles = new HashTable<Gtk.CheckMenuItem,
				A11yProfileManager.Profile>.full(direct_hash,
				direct_equal, g_object_unref, g_object_unref);
	}

	private void
	setup()
	{
		Gtk.Menu profiles_menu = new Gtk.Menu();
		this.no_profile_item = new Gtk.RadioMenuItem.with_label(null, _("No Profile"));
		unowned SList<Gtk.RadioMenuItem> radio_group = this.no_profile_item.get_group();
		SList<A11yProfileManager.Profile> profile_list = A11yProfileManager.list_profiles();

		this.no_profile_item.toggled.connect(this.deactivate_profile);
		this.no_profile_item.show();
		profiles_menu.add(this.no_profile_item);

		foreach (var profile in profile_list)
		{
			var menu_item = new Gtk.RadioMenuItem.with_label(radio_group, profile.name);
			if (profile.dir_name == current_profile)
			{
				menu_item.set_active(true);
				this.active_item = menu_item;
			}
			this.profiles.insert(menu_item, profile);
			menu_item.toggled.connect(this.activate_profile);
			menu_item.show();
			profiles_menu.add(menu_item);
		}
		
		this.indicator = new AppIndicator.Indicator("a11y-profile-manager-indicator",
							    "accessibility-directory",
							    AppIndicator.IndicatorCategory.OTHER);
		this.indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE);
		this.indicator.set_menu(profiles_menu);
		this.indicator.set_icon_full("accessibility-directory", _("Accessibility Profiles"));

		this.profile_settings.changed["active-profile"].connect(this.active_profile_setting_changed);
		this.a11y_settings.changed["always-show-universal-access-status"].connect(this.uv_access_status_changed);
	}

	private void
	activate_profile(Gtk.CheckMenuItem item)
	{
		unowned A11yProfileManager.Profile profile = this.profiles.get(item);

		if (profile != null)
		{
			if (item.get_active())
			{
				if (item == this.active_item)
				{
					return;
				}

				try
				{
					profile.set_settings();
				}
				catch (Error e)
				{
					warning(_("Unable to set profile settings: %s"), e.message);
					this.active_item = null;
					this.current_profile = null;
					item.set_active(false);
					this.no_profile_item.set_active(true);
					this.profile_settings.reset("active-profile");
					return;
				}

				this.active_item = item;
				this.current_profile = profile.dir_name;
				this.profile_settings.set_string("active-profile", profile.dir_name);

				if (this.installer_mode && profile.at_command != null)
				{
					this.start_at(profile.at_command);
				}
			}
			else
			{
				if (this.at_pid > 0)
				{
					Process.close_pid(this.at_pid);
					this.at_pid = 0;
				}

				try
				{
					profile.reset_settings();
				}
				catch (Error e)
				{
					warning(_("Unable to reset profile settings: %s"), e.message);
				}
			}
		}

		return;
	}

	private void
	deactivate_profile(Gtk.CheckMenuItem item)
	{
		if (!item.get_active())
		{
			return;
		}

		unowned A11yProfileManager.Profile profile = this.profiles.get(active_item);

		this.current_profile = "";
		this.active_item = null;

		if (profile != null)
		{
			if (this.at_pid > 0)
			{
				Process.close_pid(this.at_pid);
				this.at_pid = 0;
			}

			try
			{
				profile.reset_settings();
			}
			catch (Error e)
			{
				warning(_("Unable to reset profile settings: %s"), e.message);
			}
		}

		this.profile_settings.reset("active-profile");
		return;
	}

	private void
	active_profile_setting_changed(Settings settings, string key)
	{
		string current_setting = settings.get_string(key);

		if (current_setting != this.current_profile)
		{
			this.current_profile = current_setting;
			List<unowned Gtk.CheckMenuItem> item_list = this.profiles.get_keys();

			foreach (unowned Gtk.CheckMenuItem item in item_list)
			{
				unowned A11yProfileManager.Profile profile = this.profiles.get(item);
				if (profile.dir_name == current_setting)
				{
					this.active_item = item;

					try
					{
						profile.set_settings();
					}
					catch (Error e)
					{
						warning(_("Unable to set profile settings: %s"), e.message);
						this.active_item = null;
						this.current_profile = null;
						item.set_active(false);
						this.no_profile_item.set_active(true);
						this.profile_settings.reset("active-profile");
					}

					item.set_active(true);

					if (this.installer_mode && profile.at_command != null)
					{
						this.start_at(profile.at_command);
					}
				}
			}
		}

		return;
	}

	private void
	uv_access_status_changed(Settings settings, string key)
	{
		bool uv_status = settings.get_boolean("always-show-universal-access-status");

		if (!uv_status)
		{
			Gtk.main_quit();
			return;
		}
	}

	private void
	start_at(string command)
	{
		if (this.at_pid == 0)
		{
			try
			{
				string[] argv;
				Shell.parse_argv(command, out argv);
				Process.spawn_async (null,
						     argv,
						     null,
						     SpawnFlags.SEARCH_PATH,
						     null,
						     out this.at_pid);
			}
			catch (Error e)
			{
				warning(_("Unable to start assistive technology: %s"), e.message);
			}
		}
	}

	public static int
	main(string[] args)
	{
		/* Initialize i18n */
		Intl.setlocale (LocaleCategory.ALL, "");
		Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
		Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
		Intl.textdomain (Config.GETTEXT_PACKAGE);

		Gtk.init (ref args);

		/* Command-line options */
		bool installer_mode = false;

		OptionEntry installer_option = { "installer", 'i', 0, OptionArg.NONE, ref installer_mode, N_("Start the indicator in installer mode"), null };
		OptionEntry null_option = { null };
		OptionEntry[] options = { installer_option, null_option };

		var context = new OptionContext(_("- Accessibility Profile Manager Unity Desktop Indicator"));
		context.add_main_entries(options, Config.GETTEXT_PACKAGE);
		context.add_group(Gtk.get_option_group (true));

		try
		{
			context.parse(ref args);
		}
		catch (Error e)
		{
			stderr.printf("%s\n", e.message);
			stderr.printf(_("Run '%s --help' to see a full list of available command line options."), args[0]);
			stderr.printf ("\n");
			return Posix.EXIT_FAILURE;
		}

		ProfileIndicator profile_indicator = new ProfileIndicator(installer_mode);

		profile_indicator.setup();

		/* Setup a handler for TERM so we quit cleanly */
		GLib.Unix.signal_add(GLib.ProcessSignal.TERM, () => {
			Gtk.main_quit();
			return true;
		});

		Gtk.main();

		return Posix.EXIT_SUCCESS;
	}
}
