/*
 * Copyright (c) 2012-2025 Daniele Bartolini et al.
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

using Gtk;

namespace Crown
{
public class ComboBoxMap : Gtk.ComboBox, Property
{
	// Data
	public bool _stop_emit;
	public bool _inconsistent;
	public Gtk.ListStore _store;
	public Gtk.TreeModelFilter _filter;

	// Signals
	public signal void value_changed();

	public void set_inconsistent(bool inconsistent)
	{
		if (_inconsistent != inconsistent) {
			_inconsistent = inconsistent;

			_filter.refilter();

			if (_inconsistent) {
				_stop_emit = true;
				this.set_active_id(INCONSISTENT_ID);
				_stop_emit = false;
			}
		}
	}

	public bool is_inconsistent()
	{
		return _inconsistent;
	}

	public Value? generic_value()
	{
		return this.value;
	}

	public void set_generic_value(Value? val)
	{
		this.value = (string)val;
	}

	public string? value
	{
		get
		{
			return this.get_active_id();
		}
		set
		{
			_stop_emit = true;
			_filter.refilter();
			bool success = this.set_active_id((string)value);
			_stop_emit = false;
			set_inconsistent(!success);
		}
	}

	public bool filter_visible_func(Gtk.TreeModel model, Gtk.TreeIter iter)
	{
		Value id_val;
		model.get_value(iter, 0, out id_val);

		if (!_inconsistent && (string)id_val == INCONSISTENT_ID)
			return false;

		return true;
	}

	public ComboBoxMap(int default_id = 0, string[]? labels = null, string[]? ids = null)
	{
		// Data
		_stop_emit = false;
		_inconsistent = false;

		_store = new Gtk.ListStore(2
			, typeof(string) // ID
			, typeof(string) // Label
			);
		_filter = new Gtk.TreeModelFilter(_store, null);
		_filter.set_visible_func(filter_visible_func);

		this.model = _filter;
		this.id_column = 0;
		this.entry_text_column = 1;

		Gtk.CellRendererText renderer = new Gtk.CellRendererText ();
		this.pack_start(renderer, true);
		this.add_attribute(renderer, "text", 1);

		insert_special_values();

		if (labels != null) {
			Gtk.TreeIter iter;
			for (int ii = 0; ii < ids.length; ++ii) {
				unowned string? id = ids != null ? ids[ii] : null;
				_store.insert_with_values(out iter, -1, 0, id, 1, labels[ii], -1);
			}
			if (ids != null && default_id < ids.length)
				this.value = ids[default_id];
		}

		// Widgets
		this.changed.connect(on_changed);
		this.scroll_event.connect(on_scroll);
	}

	public void append(string? id, string label)
	{
		Gtk.TreeIter iter;
		_store.insert_with_values(out iter, -1, 0, id, 1, label, -1);
	}

	public void clear()
	{
		_stop_emit = true;
		_store.clear();
		insert_special_values();
		_inconsistent = false;
		_stop_emit = false;
	}

	public string any_valid_id()
	{
		string some_id = INCONSISTENT_ID;

		if (_store.iter_n_children(null) > 1u) {
			_store.foreach((model, path, iter) => {
					Value id_val;
					model.get_value(iter, 0, out id_val);
					if ((string)id_val != INCONSISTENT_ID) {
						some_id = (string)id_val;
						return true;
					}

					return false;
				});
		}

		return some_id;
	}

	private void on_changed()
	{
		if (_inconsistent && this.get_active_id() == INCONSISTENT_ID)
			return;

		if (!_stop_emit) {
			if (_inconsistent) {
				_inconsistent = false;
				_filter.refilter();
			}
			value_changed();
		}
	}

	private bool on_scroll(Gdk.EventScroll ev)
	{
		GLib.Signal.stop_emission_by_name(this, "scroll-event");
		return Gdk.EVENT_PROPAGATE;
	}

	private void insert_special_values()
	{
		assert(_store.iter_n_children(null) == 0u);

		// Insert the "inconsistent" ID and label.
		Gtk.TreeIter iter;
		_store.insert_with_values(out iter, -1, 0, INCONSISTENT_ID, 1, INCONSISTENT_LABEL, -1);
	}
}

} /* namespace Crown */
