/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/linux_gtk_integration.h"

#include "base/platform/linux/base_linux_gtk_integration.h"
#include "base/platform/linux/base_linux_gtk_integration_p.h"
#include "platform/linux/linux_gtk_integration_p.h"
#include "platform/linux/linux_gdk_helper.h"
#include "platform/linux/linux_gtk_file_dialog.h"
#include "platform/linux/linux_open_with_dialog.h"

namespace Platform {
namespace internal {

using namespace Platform::Gtk;
using BaseGtkIntegration = base::Platform::GtkIntegration;

namespace {

bool GetImageFromClipboardSupported() {
	return (gtk_clipboard_get != nullptr)
		&& (gtk_clipboard_wait_for_contents != nullptr)
		&& (gtk_clipboard_wait_for_image != nullptr)
		&& (gtk_selection_data_targets_include_image != nullptr)
		&& (gtk_selection_data_free != nullptr)
		&& (gdk_pixbuf_get_pixels != nullptr)
		&& (gdk_pixbuf_get_width != nullptr)
		&& (gdk_pixbuf_get_height != nullptr)
		&& (gdk_pixbuf_get_rowstride != nullptr)
		&& (gdk_pixbuf_get_has_alpha != nullptr)
		&& (gdk_atom_intern != nullptr);
}

} // namespace

GtkIntegration::GtkIntegration() {
}

GtkIntegration *GtkIntegration::Instance() {
	if (!BaseGtkIntegration::Instance()) {
		return nullptr;
	}

	static GtkIntegration instance;
	return &instance;
}

void GtkIntegration::load() {
	static bool Loaded = false;
	Expects(!Loaded);

	if (!BaseGtkIntegration::Instance()->loaded()) {
		return;
	}

	auto &lib = BaseGtkIntegration::Instance()->library();

	LOAD_GTK_SYMBOL(lib, "gtk_widget_show", gtk_widget_show);
	LOAD_GTK_SYMBOL(lib, "gtk_widget_hide", gtk_widget_hide);
	LOAD_GTK_SYMBOL(lib, "gtk_widget_get_window", gtk_widget_get_window);
	LOAD_GTK_SYMBOL(lib, "gtk_widget_realize", gtk_widget_realize);
	LOAD_GTK_SYMBOL(lib, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete);
	LOAD_GTK_SYMBOL(lib, "gtk_widget_destroy", gtk_widget_destroy);
	LOAD_GTK_SYMBOL(lib, "gtk_clipboard_get", gtk_clipboard_get);
	LOAD_GTK_SYMBOL(lib, "gtk_clipboard_store", gtk_clipboard_store);
	LOAD_GTK_SYMBOL(lib, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents);
	LOAD_GTK_SYMBOL(lib, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image);
	LOAD_GTK_SYMBOL(lib, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image);
	LOAD_GTK_SYMBOL(lib, "gtk_selection_data_free", gtk_selection_data_free);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_type", gtk_file_chooser_get_type);
	LOAD_GTK_SYMBOL(lib, "gtk_image_get_type", gtk_image_get_type);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter);
	LOAD_GTK_SYMBOL(lib, "gtk_window_get_type", gtk_window_get_type);
	LOAD_GTK_SYMBOL(lib, "gtk_window_set_title", gtk_window_set_title);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_action", gtk_file_chooser_set_action);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter);
	LOAD_GTK_SYMBOL(lib, "gtk_file_filter_set_name", gtk_file_filter_set_name);
	LOAD_GTK_SYMBOL(lib, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename);
	LOAD_GTK_SYMBOL(lib, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active);
	LOAD_GTK_SYMBOL(lib, "gtk_file_filter_new", gtk_file_filter_new);
	LOAD_GTK_SYMBOL(lib, "gtk_image_new", gtk_image_new);
	LOAD_GTK_SYMBOL(lib, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf);

	LOAD_GTK_SYMBOL(lib, "gdk_window_set_modal_hint", gdk_window_set_modal_hint);
	LOAD_GTK_SYMBOL(lib, "gdk_window_focus", gdk_window_focus);
	LOAD_GTK_SYMBOL(lib, "gtk_dialog_get_type", gtk_dialog_get_type);
	LOAD_GTK_SYMBOL(lib, "gtk_dialog_run", gtk_dialog_run);

	LOAD_GTK_SYMBOL(lib, "gdk_atom_intern", gdk_atom_intern);

	LOAD_GTK_SYMBOL(lib, "gdk_display_get_default", gdk_display_get_default);
	LOAD_GTK_SYMBOL(lib, "gdk_display_get_monitor", gdk_display_get_monitor);
	LOAD_GTK_SYMBOL(lib, "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor);
	LOAD_GTK_SYMBOL(lib, "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor);

	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size);
	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha);
	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels);
	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_width", gdk_pixbuf_get_width);
	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_height", gdk_pixbuf_get_height);
	LOAD_GTK_SYMBOL(lib, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride);

	GdkHelperLoad(lib);

	LOAD_GTK_SYMBOL(lib, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response);
	LOAD_GTK_SYMBOL(lib, "gtk_button_set_label", gtk_button_set_label);
	LOAD_GTK_SYMBOL(lib, "gtk_button_get_type", gtk_button_get_type);

	LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new);
	LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info);
	LOAD_GTK_SYMBOL(lib, "gtk_app_chooser_get_type", gtk_app_chooser_get_type);

	Loaded = true;
}

std::optional<int> GtkIntegration::scaleFactor() const {
	if ((gdk_display_get_default == nullptr)
		|| (gdk_display_get_monitor == nullptr)
		|| (gdk_display_get_primary_monitor == nullptr)
		|| (gdk_monitor_get_scale_factor == nullptr)) {
		return std::nullopt;
	}

	const auto display = gdk_display_get_default();
	if (!display) {
		return std::nullopt;
	}

	const auto monitor = [&] {
		if (const auto primary = gdk_display_get_primary_monitor(display)) {
			return primary;
		}
		return gdk_display_get_monitor(display, 0);
	}();

	if (!monitor) {
		return std::nullopt;
	}

	return gdk_monitor_get_scale_factor(monitor);
}

bool GtkIntegration::fileDialogSupported() const {
	return FileDialog::Gtk::Supported();
}

bool GtkIntegration::useFileDialog(FileDialogType type) const {
	return FileDialog::Gtk::Use(type);
}

bool GtkIntegration::getFileDialog(
		QPointer<QWidget> parent,
		QStringList &files,
		QByteArray &remoteContent,
		const QString &caption,
		const QString &filter,
		FileDialogType type,
		QString startFile) const {
	return FileDialog::Gtk::Get(
		parent,
		files,
		remoteContent,
		caption,
		filter,
		type,
		startFile);
}

bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
	return File::internal::ShowOpenWithDialog(filepath);
}

QImage GtkIntegration::getImageFromClipboard() const {
	QImage data;

	if (!GetImageFromClipboardSupported()) {
		return data;
	}

	const auto clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
	if (!clipboard) {
		return data;
	}

	auto gsel = gtk_clipboard_wait_for_contents(
		clipboard,
		gdk_atom_intern("TARGETS", true));

	if (gsel) {
		if (gtk_selection_data_targets_include_image(gsel, false)) {
			auto img = gtk_clipboard_wait_for_image(clipboard);

			if (img) {
				data = QImage(
					gdk_pixbuf_get_pixels(img),
					gdk_pixbuf_get_width(img),
					gdk_pixbuf_get_height(img),
					gdk_pixbuf_get_rowstride(img),
					gdk_pixbuf_get_has_alpha(img)
						? QImage::Format_RGBA8888
						: QImage::Format_RGB888).copy();

				g_object_unref(img);
			}
		}

		gtk_selection_data_free(gsel);
	}

	return data;
}

} // namespace internal
} // namespace Platform
