/*
   Kickshaw - A Menu Editor for Openbox

   Copyright (c) 2010–2024        Marcus Schätzle

   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 Kickshaw. If not, see http://www.gnu.org/licenses/.
*/

#include <gtk/gtk.h>

#include "declarations_definitions_and_enumerations.h"
#include "check_for_external_changes_timeout.h"

/* 

    Checks if...
    ...the theme has been changed; if so, check if it is necessary to change the special font colors.
    ...the font size has been changed; if so, changes the size of icons and/or readjusts the tab stops 
    of the hints window according to the new font size. Also changes the font size of the Enter Values 
    label.
    ...a valid icon file path has become invalid; if so, changes the icon to a broken one.
    ...an invalid icon file path has become valid; if so, replaces the broken icon with the icon image,
    if it is a proper image file.

*/

// The identifier is not used by the function, but to switch the timeout on and off.
gboolean check_for_external_file_and_settings_changes (G_GNUC_UNUSED gpointer identifier)
{
    g_autofree gchar *font_desc;
    gboolean font_size_has_changed = FALSE; // Default
    gboolean create_new_invalid_icon_imgs = FALSE; // Default

    determine_theme_type ();

    g_object_get (gtk_settings_get_default (), "gtk-font-name", &font_desc, NULL);

    if (G_UNLIKELY (!STREQ (font_desc, ks.font_desc))) {
        guint font_size_updated;

        FREE_AND_REASSIGN (ks.font_desc, g_strdup (font_desc));

        // Check if font size has changed.
        if ((font_size_has_changed = (G_UNLIKELY (ks.font_size != (font_size_updated = get_font_size ()))))) {
            ks.font_size = font_size_updated;

            if (ks.rows_with_icons) {
                create_new_invalid_icon_imgs = TRUE;
            }

            if (ks.hints_window) {
                for (guint8 hint_txts_cnt = 0; hint_txts_cnt < NUMBER_OF_HINT_TXTS; ++hint_txts_cnt) {
                    g_autoptr(PangoTabArray) tab_array = create_and_set_tab_array ();

                    gtk_text_view_set_tabs (GTK_TEXT_VIEW (ks.text_views[hint_txts_cnt]), tab_array);
                }
            }

            if (gtk_widget_get_visible (ks.enter_values_box)) {
                g_autofree gchar *font_name = get_font_name ();
                const gchar *label_txt = gtk_label_get_text (GTK_LABEL (ks.enter_values_label));
                g_autofree gchar *label_markup = g_strdup_printf ("<span font_desc='%s %i'>%s</span>", 
                                                                  font_name, ks.font_size + 2, label_txt);

                gtk_label_set_markup (GTK_LABEL (ks.enter_values_label), label_markup);
            }
        }
    }

    if (ks.rows_with_icons) {
        GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
        const gint number_of_selected_rows = gtk_tree_selection_count_selected_rows (selection);

        g_autofree gchar *current_icon_theme_name;

        GSList *rows_with_icons_loop;
    

        // Check if icon theme has changed.

        g_object_get (gtk_settings_get_default (), "gtk-icon-theme-name", &current_icon_theme_name, NULL);

        if (G_UNLIKELY (!STREQ (ks.icon_theme_name, current_icon_theme_name))) {
            FREE_AND_REASSIGN (ks.icon_theme_name, g_strdup (current_icon_theme_name));
            create_new_invalid_icon_imgs = TRUE;
        }

        /* If font size or icon theme has changed, recreate the invalid icon images.
           create_new_invalid_icon_imgs was already set to TRUE if the font size was changed, 
           so this separate if statement is set to act upon both a font size and icon theme change. */
        if (G_UNLIKELY (create_new_invalid_icon_imgs)) {
            create_invalid_icon_imgs ();
        }

        FOREACH_IN_LIST (ks.rows_with_icons, rows_with_icons_loop) {
            GtkTreePath *path_loop = rows_with_icons_loop->data;
            GtkTreeIter iter_loop;
            g_autofree gchar *icon_modification_time_txt_loop, 
                             *icon_path_txt_loop;
            guint icon_img_status_uint_loop;
            gboolean replacement_done = FALSE; // Default

            gtk_tree_model_get_iter (ks.ts_model, &iter_loop, path_loop);

            gtk_tree_model_get (ks.ts_model, &iter_loop,
                                TS_ICON_IMG_STATUS, &icon_img_status_uint_loop, 
                                TS_ICON_MODIFICATION_TIME, &icon_modification_time_txt_loop,
                                TS_ICON_PATH, &icon_path_txt_loop, 
                                -1);

            g_autoptr(GFile) icon_loop = g_file_new_for_path (icon_path_txt_loop);

            /*
                Case 1: Stored path no longer points to a valid icon image, so icon has to replaced with broken icon.
                Case 2: Invalid path to an icon image has become valid.
                Case 3: Icon image file is replaced with another one.
            */
            if (G_LIKELY (icon_img_status_uint_loop != INVALID_PATH) && 
                G_UNLIKELY (!(g_file_query_exists (icon_loop, NULL)))) { // Case 1
                gtk_tree_store_set (ks.treestore, &iter_loop, 
                                    TS_ICON_IMG, ks.invalid_icon_imgs[INVALID_PATH_ICON], 
                                    TS_ICON_IMG_STATUS, INVALID_PATH, 
                                    TS_ICON_MODIFICATION_TIME, NULL, 
                                    -1);

                if (number_of_selected_rows == 1 && gtk_tree_selection_iter_is_selected (selection, &iter_loop)) {
                    visually_indicate_request_for_user_intervention (ks.entry_fields[ICON_PATH_ENTRY], ks.icon_path_entry_css_provider);
                }

                replacement_done = TRUE;
            }
            else if (G_LIKELY (g_file_query_exists (icon_loop, NULL))) {
                g_autofree gchar *time_stamp = get_modification_time_for_icon (icon_path_txt_loop);

                if (G_UNLIKELY (icon_img_status_uint_loop == INVALID_PATH ||
                                !STREQ (time_stamp, icon_modification_time_txt_loop))) {
                    // Case 2 & 3, FALSE 1 = don't show error message when error occurs, FALSE 2 = not during undo/redo
                    if (G_UNLIKELY (!(set_icon (&iter_loop, icon_path_txt_loop, FALSE, FALSE)))) { 
                        gtk_tree_store_set (ks.treestore, &iter_loop, 
                                            TS_ICON_IMG, ks.invalid_icon_imgs[INVALID_FILE_ICON], 
                                            TS_ICON_IMG_STATUS, INVALID_FILE, 
                                            TS_ICON_MODIFICATION_TIME, time_stamp, 
                                            -1);
                    }

                    if (number_of_selected_rows == 1 && gtk_tree_selection_iter_is_selected (selection, &iter_loop)) {
                        gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[ICON_PATH_ENTRY]), 
                                                        "user_intervention_requested");
                    }

                    replacement_done = TRUE;
                }
            }

            /*
                If the font size or icon theme has changed, replace the icon images accordingly,
                but only if the icons have not been replaced already, since the replacement icons already have the new size.
            */
            if (G_UNLIKELY (!replacement_done && 
                            (font_size_has_changed || (create_new_invalid_icon_imgs && icon_img_status_uint_loop)))) {
                if (!icon_img_status_uint_loop) { // Icon path is OK and valid icon exists -> create and store larger or smaller icon
                    // FALSE 1 = don't show error message when error occurs, FALSE 2 = not during undo/redo
                    set_icon (&iter_loop, icon_path_txt_loop, FALSE, FALSE);
                }
                else { // Icon path is wrong or icon image is invalid = store broken icon with larger or smaller size
                    gtk_tree_store_set (ks.treestore, &iter_loop, TS_ICON_IMG, 
                                        ks.invalid_icon_imgs[(icon_img_status_uint_loop == INVALID_PATH) ? 
                                                             INVALID_PATH_ICON : INVALID_FILE_ICON], -1);
                }

                gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ks.treeview)); // In case that the font size has been reduced.
            }
        }
    }

    if (GTK_IS_WIDGET (ks.about) && gtk_widget_is_visible (ks.about)) {
        const gchar *current_version = gtk_about_dialog_get_version (GTK_ABOUT_DIALOG (ks.about));

        g_autofree gchar *regex_str = g_strdup_printf ("[0-9]$|%s$", _("Check for newer version requires internet connection."));

        if (ks.stopped_trying_to_connect_to_server && g_regex_match_simple (regex_str, gtk_about_dialog_get_version (GTK_ABOUT_DIALOG (ks.about)), 0, 0)) {
            g_autoptr(GSocketClient) socket_client = g_socket_client_new ();

            ks.stopped_trying_to_connect_to_server = FALSE;

            g_socket_client_connect_to_uri_async (socket_client, "http://download.savannah.gnu.org", 80, NULL, (GAsyncReadyCallback) get_latest_version_str_from_server_callback, NULL);

            if (!STREQ (current_version, ks.version_info)) {
                gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (ks.about), ks.version_info);
            }
        }
    }

    return G_SOURCE_CONTINUE;
}

/* 

    Stops the timer and erases the list of icon occourrences.

*/

void stop_timeout (void)
{
    g_source_remove_by_user_data ("timeout");
    ks.timeout_id = 0;
#if GLIB_CHECK_VERSION(2,64,0)
    g_clear_slist (&ks.rows_with_icons, (GDestroyNotify) gtk_tree_path_free);
#else
    g_slist_free_full (ks.rows_with_icons, (GDestroyNotify) gtk_tree_path_free);
    ks.rows_with_icons = NULL;
#endif
}
