/*
 * Copyright (C) 2008-2011 Robert Ancell
 *
 * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
 * license.
 */

#include <glib/gi18n.h>
#include <gtk/gtk.h>

#include "math-preferences.h"
#include "utility.h"

enum {
    PROP_0,
    PROP_EQUATION
};

struct MathPreferencesDialogPrivate
{
    MathEquation *equation;
    GtkBuilder *ui;
};

G_DEFINE_TYPE_WITH_PRIVATE (MathPreferencesDialog, math_preferences, GTK_TYPE_DIALOG);

#define UI_DIALOGS_RESOURCE_PATH "/org/mate/calculator/ui/preferences.ui"
#define GET_WIDGET(ui, name) \
          GTK_WIDGET(gtk_builder_get_object(ui, name))

MathPreferencesDialog *
math_preferences_dialog_new(MathEquation *equation)
{
    return g_object_new(math_preferences_get_type(), "equation", equation, NULL);
}

static void
preferences_response_cb(GtkWidget *widget, gint id)
{
    gtk_widget_hide(widget);
}

static gboolean
preferences_dialog_delete_cb(GtkWidget *widget, GdkEvent *event)
{
    preferences_response_cb(widget, 0);
    return TRUE;
}

void number_format_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
number_format_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog)
{
    MpDisplayFormat value;
    GtkTreeModel *model;
    GtkTreeIter iter;

    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
    gtk_tree_model_get(model, &iter, 1, &value, -1);
    math_equation_set_number_format(dialog->priv->equation, value);
}

void angle_unit_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
angle_unit_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog)
{
    MPAngleUnit value;
    GtkTreeModel *model;
    GtkTreeIter iter;

    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
    gtk_tree_model_get(model, &iter, 1, &value, -1);
    math_equation_set_angle_units(dialog->priv->equation, value);
}

void word_size_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
word_size_combobox_changed_cb(GtkWidget *combo, MathPreferencesDialog *dialog)
{
    gint value;
    GtkTreeModel *model;
    GtkTreeIter iter;

    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
    gtk_tree_model_get(model, &iter, 1, &value, -1);
    math_equation_set_word_size(dialog->priv->equation, value);
}

void decimal_places_spin_change_value_cb(GtkWidget *spin, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
decimal_places_spin_change_value_cb(GtkWidget *spin, MathPreferencesDialog *dialog)
{
    gint value = 0;

    value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
    math_equation_set_accuracy(dialog->priv->equation, value);
}

void thousands_separator_check_toggled_cb(GtkWidget *check, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
thousands_separator_check_toggled_cb(GtkWidget *check, MathPreferencesDialog *dialog)
{
    gboolean value;

    value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
    math_equation_set_show_thousands_separators(dialog->priv->equation, value);
}

void trailing_zeroes_check_toggled_cb(GtkWidget *check, MathPreferencesDialog *dialog);
G_MODULE_EXPORT
void
trailing_zeroes_check_toggled_cb(GtkWidget *check, MathPreferencesDialog *dialog)
{
    gboolean value;

    value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
    math_equation_set_show_trailing_zeroes(dialog->priv->equation, value);
}

static void
set_combo_box_from_int(GtkWidget *combo, int value)
{
    GtkTreeModel *model;
    GtkTreeIter iter;
    gboolean valid;

    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
    valid = gtk_tree_model_get_iter_first(model, &iter);

    while (valid) {
        gint v;

        gtk_tree_model_get(model, &iter, 1, &v, -1);
        if (v == value)
            break;
        valid = gtk_tree_model_iter_next(model, &iter);
    }
    if (!valid)
        valid = gtk_tree_model_get_iter_first(model, &iter);

    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter);
}

static void
accuracy_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(dialog->priv->ui, "decimal_places_spin")),
                              math_equation_get_accuracy(equation));
    g_settings_set_int(g_settings_var, "accuracy", math_equation_get_accuracy(equation));
}

static void
show_thousands_separators_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(dialog->priv->ui, "thousands_separator_check")),
                                 math_equation_get_show_thousands_separators(equation));
    g_settings_set_boolean(g_settings_var, "show-thousands", math_equation_get_show_thousands_separators(equation));
}

static void
show_trailing_zeroes_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(dialog->priv->ui, "trailing_zeroes_check")),
                                 math_equation_get_show_trailing_zeroes(equation));
    g_settings_set_boolean(g_settings_var, "show-zeroes", math_equation_get_show_trailing_zeroes(equation));
}

static void
number_format_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    set_combo_box_from_int(GET_WIDGET(dialog->priv->ui, "number_format_combobox"), math_equation_get_number_format(equation));
    g_settings_set_enum(g_settings_var, "number-format", math_equation_get_number_format(equation));
}

static void
word_size_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    set_combo_box_from_int(GET_WIDGET(dialog->priv->ui, "word_size_combobox"), math_equation_get_word_size(equation));
    g_settings_set_int(g_settings_var, "word-size", math_equation_get_word_size(equation));
}

static void
angle_unit_cb(MathEquation *equation, GParamSpec *spec, MathPreferencesDialog *dialog)
{
    set_combo_box_from_int(GET_WIDGET(dialog->priv->ui, "angle_unit_combobox"), math_equation_get_angle_units(equation));
    g_settings_set_enum(g_settings_var, "angle-units", math_equation_get_angle_units(equation));
}

static void
create_gui(MathPreferencesDialog *dialog)
{
    GtkWidget *widget;
    GtkTreeModel *model;
    GtkTreeIter iter;
    GtkCellRenderer *renderer;
    gchar *string, **tokens;
    GError *error = NULL;
    static gchar *objects[] = { "preferences_table", "angle_unit_model", "number_format_model",
                                "word_size_model", "decimal_places_adjustment", "number_base_model", NULL };

    // FIXME: Handle errors
    dialog->priv->ui = gtk_builder_new();
    gtk_builder_add_objects_from_resource(dialog->priv->ui, UI_DIALOGS_RESOURCE_PATH, objects, &error);
    if (error)
        g_warning("Error loading preferences UI: %s", error->message);
    g_clear_error(&error);

    gtk_window_set_title(GTK_WINDOW(dialog),
                         /* Title of preferences dialog */
                         _("Preferences"));
    gtk_container_set_border_width(GTK_CONTAINER(dialog), 8);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          /* Icon name on close button in preferences dialog */
                          "gtk-close", GTK_RESPONSE_CLOSE);

    gtk_window_set_icon_name (GTK_WINDOW(dialog), "accessories-calculator");

    g_signal_connect(dialog, "response", G_CALLBACK(preferences_response_cb), NULL);
    g_signal_connect(dialog, "delete-event", G_CALLBACK(preferences_dialog_delete_cb), NULL);
    gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GET_WIDGET(dialog->priv->ui, "preferences_table"), TRUE, TRUE, 0);

    widget = GET_WIDGET(dialog->priv->ui, "angle_unit_combobox");
    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Preferences dialog: Angle unit combo box: Use degrees for trigonometric calculations */
                       _("Degrees"), 1, MP_DEGREES, -1);
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Preferences dialog: Angle unit combo box: Use radians for trigonometric calculations */
                       _("Radians"), 1, MP_RADIANS, -1);
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Preferences dialog: Angle unit combo box: Use gradians for trigonometric calculations */
                       _("Gradians"), 1, MP_GRADIANS, -1);
    renderer = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);

    widget = GET_WIDGET(dialog->priv->ui, "number_format_combobox");
    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Number display mode combo: Automatic, e.g. 1234 (or scientific for large number 1.234×10^99) */
                       _("Automatic"), 1, MP_DISPLAY_FORMAT_AUTOMATIC, -1);
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Number display mode combo: Fixed, e.g. 1234 */
                       _("Fixed"), 1, MP_DISPLAY_FORMAT_FIXED, -1);
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Number display mode combo: Scientific, e.g. 1.234×10^3 */
                       _("Scientific"), 1, MP_DISPLAY_FORMAT_SCIENTIFIC, -1);
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
                       /* Number display mode combo: Engineering, e.g. 1.234k */
                       _("Engineering"), 1, MP_DISPLAY_FORMAT_ENGINEERING, -1);
    renderer = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);

    widget = GET_WIDGET(dialog->priv->ui, "word_size_combobox");
    renderer = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);

    /* Label used in preferences dialog.  The %d is replaced by a spinbutton */
    string = _("Show %d decimal _places");
    tokens = g_strsplit(string, "%d", 2);
    widget = GET_WIDGET(dialog->priv->ui, "decimal_places_label1");
    if (tokens[0])
        string = g_strstrip(tokens[0]);
    else
        string = "";
    if (string[0] != '\0')
        gtk_label_set_text_with_mnemonic(GTK_LABEL(widget), string);
    else
        gtk_widget_hide(widget);

    widget = GET_WIDGET(dialog->priv->ui, "decimal_places_label2");
    if (tokens[0] && tokens[1])
        string = g_strstrip(tokens[1]);
    else
        string = "";
    if (string[0] != '\0')
        gtk_label_set_text_with_mnemonic(GTK_LABEL(widget), string);
    else
        gtk_widget_hide(widget);

    g_strfreev(tokens);

    gtk_builder_connect_signals(dialog->priv->ui, dialog);

    g_signal_connect(dialog->priv->equation, "notify::accuracy", G_CALLBACK(accuracy_cb), dialog);
    g_signal_connect(dialog->priv->equation, "notify::show-thousands-separators", G_CALLBACK(show_thousands_separators_cb), dialog);
    g_signal_connect(dialog->priv->equation, "notify::show-trailing_zeroes", G_CALLBACK(show_trailing_zeroes_cb), dialog);
    g_signal_connect(dialog->priv->equation, "notify::number-format", G_CALLBACK(number_format_cb), dialog);
    g_signal_connect(dialog->priv->equation, "notify::word-size", G_CALLBACK(word_size_cb), dialog);
    g_signal_connect(dialog->priv->equation, "notify::angle-units", G_CALLBACK(angle_unit_cb), dialog);

    accuracy_cb(dialog->priv->equation, NULL, dialog);
    show_thousands_separators_cb(dialog->priv->equation, NULL, dialog);
    show_trailing_zeroes_cb(dialog->priv->equation, NULL, dialog);
    number_format_cb(dialog->priv->equation, NULL, dialog);
    word_size_cb(dialog->priv->equation, NULL, dialog);
    angle_unit_cb(dialog->priv->equation, NULL, dialog);
}

static void
math_preferences_set_property(GObject      *object,
                              guint         prop_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
    MathPreferencesDialog *self;

    self = MATH_PREFERENCES(object);

    switch (prop_id) {
    case PROP_EQUATION:
        self->priv->equation = g_value_get_object(value);
        create_gui(self);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}

static void
math_preferences_get_property(GObject    *object,
                              guint       prop_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
    MathPreferencesDialog *self;

    self = MATH_PREFERENCES(object);

    switch (prop_id) {
    case PROP_EQUATION:
        g_value_set_object(value, self->priv->equation);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}

static void
math_preferences_class_init(MathPreferencesDialogClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);

    object_class->get_property = math_preferences_get_property;
    object_class->set_property = math_preferences_set_property;

    g_object_class_install_property(object_class,
                                    PROP_EQUATION,
                                    g_param_spec_object("equation",
                                                        "equation",
                                                        "Equation being configured",
                                                        math_equation_get_type(),
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}

static void
math_preferences_init(MathPreferencesDialog *dialog)
{
    dialog->priv = math_preferences_get_instance_private (dialog);
}
