// This file is part of GNOME Games. License: GPL-3.0+.

[GtkTemplate (ui = "/org/gnome/Games/ui/snapshots-list.ui")]
private class Games.SnapshotsList : Gtk.Box {
	public signal void hidden ();

	[GtkChild]
	private unowned Gtk.ListBox list_box;
	[GtkChild]
	private unowned Gtk.ListBoxRow new_snapshot_row;
	[GtkChild]
	private unowned Gtk.ScrolledWindow scrolled_window;
	[GtkChild]
	private unowned Gtk.Button delete_btn;
	[GtkChild]
	private unowned Gtk.Button rename_btn;

	[GtkChild]
	private unowned Gtk.Popover rename_popover;
	[GtkChild]
	private unowned Gtk.Entry rename_entry;
	[GtkChild]
	private unowned Gtk.Button rename_popover_btn;
	[GtkChild]
	private unowned Gtk.Label rename_error_label;

	private Snapshot selected_snapshot;

	public bool is_revealed { get; set; }
	public Runner runner { get; set; }

	construct {
		list_box.set_header_func (update_header);
	}

	[GtkCallback]
	private void on_move_cursor () {
		var row = list_box.get_selected_row ();

		if (row != null && row is SnapshotRow) {
			var snapshot_row = row as SnapshotRow;
			var snapshot = snapshot_row.snapshot;

			if (snapshot != selected_snapshot)
				select_snapshot_row (row);
		}
	}

	[GtkCallback]
	private void on_row_activated (Gtk.ListBoxRow activated_row) {
		if (activated_row == new_snapshot_row) {
			var snapshot = runner.try_create_snapshot (false);

			if (snapshot != null) {
				var snapshot_row = new SnapshotRow (snapshot);

				list_box.insert (snapshot_row, 1);
				select_snapshot_row (snapshot_row);
				snapshot_row.reveal ();
			}
			else {
				select_snapshot_row (list_box.get_row_at_index (1));

				// TODO: Add a warning
			}
		} else
			select_snapshot_row (activated_row);
	}

	private void populate_list_box () {
		var list_rows = list_box.get_children ();
		foreach (var row in list_rows) {
			if (row != new_snapshot_row)
				list_box.remove (row);
		}

		if (runner == null)
			return;

		var snapshots = _runner.get_snapshots ();
		foreach (var snapshot in snapshots) {
			var list_row = new SnapshotRow (snapshot);

			// Reveal it early so that it doesn't animate
			list_row.reveal ();
			list_box.add (list_row);
		}
	}

	[GtkCallback]
	private void on_revealed_changed () {
		if (is_revealed) {
			runner.pause ();
			populate_list_box ();
			select_snapshot_row (null);
		}
	}

	[GtkCallback]
	private void on_revealer_transition_end () {
		if (!is_revealed)
			hidden ();
	}

	[GtkCallback]
	private void on_delete_clicked () {
		var selected_row = list_box.get_selected_row ();
		var selected_row_index = selected_row.get_index ();
		var snapshot_row = selected_row as SnapshotRow;
		var snapshot = snapshot_row.snapshot;

		ensure_row_is_visible (selected_row);
		runner.delete_snapshot (snapshot);

		// Select and preview a new row
		var next_row_index = selected_row_index + 1;
		var new_selected_row = list_box.get_row_at_index (next_row_index);
		while (new_selected_row != null && !new_selected_row.selectable) {
			next_row_index++;
			new_selected_row = list_box.get_row_at_index (next_row_index);
		}

		if (new_selected_row == null) {
			// There are no more selectable rows after the selected row
			// Check if there are any selectable rows before the selected row

			var prev_row_index = selected_row_index - 1;
			new_selected_row = list_box.get_row_at_index (prev_row_index);
			while (prev_row_index > 1 && !new_selected_row.selectable) {
				prev_row_index--;
				new_selected_row = list_box.get_row_at_index (prev_row_index);
			}
		}

		if (new_selected_row != null && new_selected_row.selectable)
			select_snapshot_row (new_selected_row);
		else
			select_snapshot_row (null);

		snapshot_row.remove_animated ();
	}

	[GtkCallback]
	private void on_rename_clicked () {
		var selected_row = list_box.get_selected_row ();

		ensure_row_is_visible (selected_row);

		rename_entry.text = selected_snapshot.name;
		rename_popover.relative_to = selected_row;
		rename_popover.popup ();
	}

	// Adapted from gtklistbox.c, ensure_row_visible()
	private void ensure_row_is_visible (Gtk.ListBoxRow row) {
		Gtk.Allocation allocation;

		row.get_allocation (out allocation);
		var y = allocation.y;
		var height = allocation.height;

		scrolled_window.kinetic_scrolling = false;
		scrolled_window.vadjustment.clamp_page (y, y + height);
		scrolled_window.kinetic_scrolling = true;
	}

	[GtkCallback]
	private void on_rename_entry_activated () {
		if (check_rename_is_valid ())
			apply_rename ();
	}

	[GtkCallback]
	private void on_rename_entry_text_changed () {
		check_rename_is_valid ();
	}

	private bool check_rename_is_valid () {
		var entry_text = rename_entry.text.strip ();

		if (entry_text == _("Autosave") || entry_text == "") {
			rename_entry.get_style_context ().add_class ("error");
			rename_popover_btn.sensitive = false;
			/* Translators: This message is shown to the user if he tried to rename
			 * his snapshot either with an empty string, or with the name of the
			 * autosave */
			rename_error_label.label = _("Invalid name");

			return false;
		}

		foreach (var list_child in list_box.get_children ()) {
			if (!(list_child is SnapshotRow))
				continue;

			var snapshot_row = list_child as SnapshotRow;
			var snapshot = snapshot_row.snapshot;

			if (snapshot.is_automatic)
				continue;

			if (snapshot.name == entry_text) {
				rename_entry.get_style_context ().add_class ("error");
				rename_popover_btn.sensitive = false;
				rename_error_label.label = _("A snapshot with this name already exists");

				return false;
			}
		}

		// All checks passed, rename operation is valid
		rename_entry.get_style_context ().remove_class ("error");
		rename_popover_btn.sensitive = true;
		rename_error_label.label = "";

		return true;
	}

	[GtkCallback]
	private void apply_rename () {
		var selected_row = list_box.get_selected_row ();
		var snapshot_row = selected_row as SnapshotRow;

		snapshot_row.set_name (rename_entry.text.strip ());
		rename_popover.popdown ();
	}

	private void update_header (Gtk.ListBoxRow row, Gtk.ListBoxRow? before) {
		if (before != null && row.get_header () == null) {
			var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
			row.set_header (separator);
		}
	}

	private SimpleAction lookup_action (string name) {
		var group = get_action_group ("display") as ActionMap;
		assert (group != null);

		var action = group.lookup_action (name);
		assert (action is SimpleAction);

		return action as SimpleAction;
	}

	private void select_snapshot_row (Gtk.ListBoxRow? row) {
		list_box.select_row (row);

		if (row == null) {
			runner.preview_current_state ();
			selected_snapshot = null;
			lookup_action ("load-snapshot").set_enabled (false);
		}
		else {
			row.grab_focus ();

			if (!(row is SnapshotRow))
				return;

			var snapshot_row = row as SnapshotRow;
			var snapshot = snapshot_row.snapshot;

			if (snapshot == selected_snapshot) {
				lookup_action ("load-snapshot").activate (null);
				return;
			}

			runner.preview_snapshot (snapshot);
			selected_snapshot = snapshot;
			lookup_action ("load-snapshot").set_enabled (true);
		}

		delete_btn.sensitive = (selected_snapshot != null);
		rename_btn.sensitive = (selected_snapshot != null &&
		                        !selected_snapshot.is_automatic);
	}

	public bool on_key_press_event (uint keyval, Gdk.ModifierType state) {
		// FIXME: Move the other list shortcuts here

		if (keyval == Gdk.Key.Delete || keyval == Gdk.Key.KP_Delete) {
			on_delete_clicked ();
			return true;
		}

		return false;
	}
}
