/*
 *  $Id: color-dialog.c 30028 2026-06-09 13:45:30Z yeti-dn $
 *  Copyright (C) 2025 David Necas (Yeti).
 *  E-mail: yeti@gwyddion.net.
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any
 *  later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along with this program; if not, write to the
 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>

#include "libgwyddion/macros.h"
#include "libgwyddion/math.h"

#include "libgwyui/color-wheel.h"
#include "libgwyui/color-editor.h"
#include "libgwyui/color-dialog.h"
#include "libgwyui/utils.h"

/* FIXME: Reverse dependence, change sanity.h to normal API and get rid of parts of it, as needed. */

struct _GwyColorDialogPrivate {
    GtkWidget *editor;
    GwyRGBA original_color;

    GtkWindow *parent;
    gboolean parent_is_modal;
    gboolean modal;
};

static void response              (GtkDialog *gtkdialog,
                                   gint response_id);
static void previous_color_changed(GwyColorDialog *dialog);

static GtkDialogClass *parent_class = NULL;

G_DEFINE_TYPE_WITH_CODE(GwyColorDialog, gwy_color_dialog, GTK_TYPE_DIALOG,
                        G_ADD_PRIVATE(GwyColorDialog))

static void
gwy_color_dialog_class_init(GwyColorDialogClass *klass)
{
    GtkDialogClass *dialog_class = GTK_DIALOG_CLASS(klass);

    parent_class = gwy_color_dialog_parent_class;

    dialog_class->response = response;
}

static void
gwy_color_dialog_init(GwyColorDialog *dialog)
{
    GwyColorDialogPrivate *priv;

    dialog->priv = priv = gwy_color_dialog_get_instance_private(dialog);

    GtkDialog *gtkdialog = GTK_DIALOG(dialog);
    gtk_window_set_title(GTK_WINDOW(dialog), _("Change Color"));

    GtkWidget *vbox = gtk_dialog_get_content_area(gtkdialog);
    gtk_box_set_spacing(GTK_BOX(vbox), 8);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
    priv->editor = gwy_color_editor_new();
    gtk_box_pack_start(GTK_BOX(vbox), priv->editor, TRUE, TRUE, 0);
    gwy_color_editor_get_previous_color(GWY_COLOR_EDITOR(priv->editor), &priv->original_color);
    g_signal_connect_swapped(priv->editor, "notify::previous-color", G_CALLBACK(previous_color_changed), dialog);
    gtk_widget_show_all(priv->editor);

    /* Try to choose a sensible size to get an approximately square colour wheel. */
    GtkRequisition natural = { 0, 0 };
    GList *children = gtk_container_get_children(GTK_CONTAINER(priv->editor));
    for (GList *child = children; child; child = g_list_next(child)) {
        if (GWY_IS_COLOR_WHEEL(child->data))
            continue;
        gtk_widget_get_preferred_size(priv->editor, NULL, &natural);
    }
    g_list_free(children);

    if (natural.height != 0)
        gtk_window_set_default_size(GTK_WINDOW(dialog), natural.height + natural.width, natural.height);
}

static void
response(GtkDialog *gtkdialog, gint response_id)
{
    GwyColorDialogPrivate *priv = GWY_COLOR_DIALOG(gtkdialog)->priv;

    /* Emit "color-changed" to the original colour upon cancel. This way the caller can simply handle the signal
     * and does not need to care about anything else. */
    if (!priv->modal)
        return;

    if (response_id != GTK_RESPONSE_OK)
        gwy_color_editor_set_color(GWY_COLOR_EDITOR(priv->editor), &priv->original_color);

    gboolean parent_is_modal = priv->parent_is_modal;
    GtkWindow *parent = priv->parent;
    if (response_id != GTK_RESPONSE_NONE)
        gtk_widget_destroy(GTK_WIDGET(gtkdialog));

    if (parent_is_modal)
        gtk_window_set_modal(parent, TRUE);
}

/**
 * gwy_color_dialog_new:
 * @parent: Parent window for the dialog, or %NULL.
 * @modal: %TRUE to set up the dialog as modal; %FALSE to set it up as persistent.
 *
 * Creates a colour editor dialog.
 *
 * If a parent window is given the dialog is set transient for it and is set to destruct with it.
 *
 * A modal dialog has OK and Cancel buttons and it is supposed to be run with gtk_dialog_run(), after which it will
 * selfdestruct. When the dialog is cancelled or closed it reverts the colour to the editor's previous colour.
 *
 * A persistent dialog does not have buttons and simply exists until destroyed.
 *
 * Returns: A newly created colour editor dialog.
 **/
GtkWidget*
gwy_color_dialog_new(GtkWindow *parent, gboolean modal)
{
    GtkWidget *widget = gtk_widget_new(GWY_TYPE_COLOR_DIALOG, NULL);
    GwyColorDialogPrivate *priv = GWY_COLOR_DIALOG(widget)->priv;

    priv->modal = modal;
    if (parent) {
        g_assert(GTK_IS_WINDOW(parent));
        gtk_window_set_transient_for(GTK_WINDOW(widget), parent);
        gtk_window_set_destroy_with_parent(GTK_WINDOW(widget), TRUE);
        priv->parent = parent;
        /* Steal modality from the parent window, prevents appearing under it on MS Windows */
        if (modal) {
            priv->parent_is_modal = gtk_window_get_modal(parent);
            if (priv->parent_is_modal)
                gtk_window_set_modal(parent, FALSE);
        }
    }
    if (modal) {
        GtkDialog *gtkdialog = GTK_DIALOG(widget);
        gwy_stock_add_button_to_dialog(gtkdialog, GWY_STOCK_CANCEL);
        gwy_stock_add_button_to_dialog(gtkdialog, GWY_STOCK_OK);
        gtk_dialog_set_default_response(gtkdialog, GTK_RESPONSE_OK);
    }

    return widget;
}

/**
 * gwy_color_dialog_get_editor:
 * @dialog: A colour editor dialog.
 *
 * Gets the editor widget of a colour editor dialog.
 *
 * Returns: The colour editor.
 **/
GtkWidget*
gwy_color_dialog_get_editor(GwyColorDialog *dialog)
{
    g_return_val_if_fail(GWY_IS_COLOR_DIALOG(dialog), NULL);
    return dialog->priv->editor;
}

static void
previous_color_changed(GwyColorDialog *dialog)
{
    GwyColorDialogPrivate *priv = dialog->priv;
    gwy_color_editor_get_previous_color(GWY_COLOR_EDITOR(priv->editor), &priv->original_color);
}

/**
 * SECTION: color-dialog
 * @title: GwyColorDialog
 * @short_description: Color editor dialog
 *
 * #GwyColorDialog is a colour editing dialog which can be used to adjust a colour.
 *
 * The dialog has no settings and signals itself. Use gwy_color_dialog_get_editor() to obtain the contained
 * #GwyColorEditor, set it up directly and connect to its signals.
 **/

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
