// SPDX-License-Identifier: GPL-2.0-only

#include "idle.h"
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include "common/mem.h"

struct lab_idle_inhibitor {
	struct wlr_idle_inhibitor_v1 *wlr_inhibitor;
	struct wl_listener on_destroy;
};

struct lab_idle_manager {
	struct wlr_idle_notifier_v1 *ext;
	struct {
		struct wlr_idle_inhibit_manager_v1 *manager;
		struct wl_listener on_new_inhibitor;
		struct wl_listener on_destroy;
	} inhibitor;
};

static struct lab_idle_manager *manager;

static void
handle_idle_inhibitor_destroy(struct wl_listener *listener, void *data)
{
	struct lab_idle_inhibitor *idle_inhibitor = wl_container_of(listener,
		idle_inhibitor, on_destroy);

	if (manager) {
		/*
		 * The display destroy event might have been triggered
		 * already and thus the manager would be NULL.
		 */
		bool still_inhibited =
			wl_list_length(&manager->inhibitor.manager->inhibitors) > 1;
		wlr_idle_notifier_v1_set_inhibited(manager->ext, still_inhibited);
	}

	wl_list_remove(&idle_inhibitor->on_destroy.link);
	free(idle_inhibitor);
}

static void
handle_idle_inhibitor_new(struct wl_listener *listener, void *data)
{
	assert(manager);
	struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;

	struct lab_idle_inhibitor *inhibitor = znew(*inhibitor);
	inhibitor->wlr_inhibitor = wlr_inhibitor;
	inhibitor->on_destroy.notify = handle_idle_inhibitor_destroy;
	wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->on_destroy);

	wlr_idle_notifier_v1_set_inhibited(manager->ext, true);
}

static void
handle_inhibitor_manager_destroy(struct wl_listener *listener, void *data)
{
	wl_list_remove(&manager->inhibitor.on_new_inhibitor.link);
	wl_list_remove(&manager->inhibitor.on_destroy.link);
	zfree(manager);
}

void
idle_manager_create(struct wl_display *display)
{
	assert(!manager);
	manager = znew(*manager);

	manager->ext = wlr_idle_notifier_v1_create(display);

	manager->inhibitor.manager = wlr_idle_inhibit_v1_create(display);
	manager->inhibitor.on_new_inhibitor.notify = handle_idle_inhibitor_new;
	wl_signal_add(&manager->inhibitor.manager->events.new_inhibitor,
		&manager->inhibitor.on_new_inhibitor);

	manager->inhibitor.on_destroy.notify = handle_inhibitor_manager_destroy;
	wl_signal_add(&manager->inhibitor.manager->events.destroy,
		&manager->inhibitor.on_destroy);
}

void
idle_manager_notify_activity(struct wlr_seat *wlr_seat)
{
	/*
	 * The display destroy event might have been triggered
	 * already and thus the manager would be NULL. Due to
	 * future code changes we might also get called before
	 * the manager has been created.
	 */
	if (!manager) {
		return;
	}

	wlr_idle_notifier_v1_notify_activity(manager->ext, wlr_seat);
}
