#pragma once
#include "GtkCommon.h"

namespace gui {

	/**
	 * Operating-system specific interface for inhibiting the screen saver. The App class provides
	 * an interface to the public members of this class. The class does, however, live inside the
	 * AppWait class, mostly to allow non-Storm data structures to be used.
	 *
	 * Sadly, the implementation is fairly complex in order to properly support different window
	 * managers on Linux. This is why the implementation is separated from App.h/cpp
	 */
	class ScreenSaver {
	public:
		// Create.
		ScreenSaver();

		// Initialize from the proper thread. This happens *after* other platform-specific initialization.
		void platformInit();

		// Platform-specific destruction. Happens before destruction of the object.
		void platformDestroy();

		// Start inhibiting the screen saver until "resume" is called.
		void inhibit();

		// Resume the screen saver. Assumed to be called after "inhibit".
		void resume();

#if defined(WINDOW)

		// Windows has a system-level interface that needs no additional data.

#elif defined(LINUX) && defined(GUI_GTK)

		// The Linux implementation is a bit more complex. Essentially, the proper "modern" way to
		// do it is through DBus. However, the well-known name does not seem to be standardized (at
		// least, the well-known name does not seem to be used), so we need to look for it. There
		// are also some quirks that we need to take into account. We also have a fallback when we
		// are running on X11.

		// Name of a device on DBus.
		struct DBusName {
			std::string busName;
			std::string path;
			std::string interfaceName;
		};

	private:
		// DBus connection to the session bus.
		GDBusConnection *sessionBus;

		// State of the class.
		enum State {
			// Initial state, unknown connection status.
			sUnknown,
			// Currently initializing the connection. In particular, we are asking around for
			// suitable endpoints.
			sInitializing,
			// We are initialized properly, and know who to talk to.
			sInitialized,
			// No remote endpoint to talk to. We need to use fallbacks if we can.
			sNoRemoteEndpoint,
		};
		State state;

		// List of candidates we have found so far. We remove the names we have verified to not
		// work. When status is sInitialized, there is only one name here.
		std::vector<DBusName> names;

		// Do we have a cookie from the screen saver service?
		bool hasCookie;

		// Do we want to have a cookie? (i.e. waiting for initialization first, or we asked one but no longer want it)
		bool wantCookie;

		// Store the cookie from the screen saver. If state is 'sNoRemoteEndpoint', we use the ID of
		// the timer we create as a cookie.
		uint32_t cookie;

		// Initialize the connection. Returns true if we should attempt to use it.
		bool initConnection();

		// Called when initialization is done. Either successful or as a failure.
		void initDone();

		// Sedn a Inhibit request to the next client in 'names'. Also used to send subsequent requests.
		void sendInhibit();

		// Inhibit the screen saver using the X11 protocol. Returns true if we can do this (i.e. if
		// we actually have an X11 connection).
		bool inhibitX11();

		// Extract endpoints from a tuple.
		void extractEndpoints(GVariant *tuple);

		// Callbacks:
		static void nameListReceived(GObject *source, GAsyncResult *result, gpointer data);
		static void cookieReceived(GObject *source, GAsyncResult *result, gpointer data);
		static gboolean backupTimer(gpointer data);

#endif
	};

}
