/*
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * See the COPYING file for license information.
 *
 * Guillaume Chazarain <guichaz@gmail.com>
 */

/********************
 * Main source file *
 ********************/

#include <unistd.h>             /* close(), dup(), dup2(), STDERR_FILENO */
#include <stdlib.h>             /* exit() */

#include "gliv.h"
#include "main.h"
#include "cmdline.h"
#include "options.h"
#include "gliv-image.h"
#include "rcfile.h"
#include "files_list.h"
#include "messages.h"
#include "windows.h"
#include "ipc.h"
#include "collection.h"
#include "opengl.h"
#include "mnemonics.h"

rt_struct *rt;
options_struct *options;
GlivImage *current_image = NULL;
GtkWidget *gl_widget;
GtkMenuBar *menu_bar;

static gboolean option_add_all = FALSE;

/* May exit :) */
static gboolean get_on_off(const gchar * str)
{
    if (str == NULL)
        return TRUE;

    while (*str == '=' || *str == ' ')
        str++;

    if (g_str_equal(str, "on"))
        return TRUE;

    if (g_str_equal(str, "off"))
        return FALSE;

    g_printerr(_("Command line flags should be on or off, not %s\n"), str);
    quit(1);
}

typedef struct {
    gboolean given;
    gboolean *flag;
    gchar *str;
    gboolean not;
} ggo_flag;

typedef struct {
    gboolean given;
    gint *opt_val;
    gint value;
    gint minimum;
} ggo_int;

static void fill_options(struct gengetopt_args_info *ggo)
{
    ggo_flag flags[] = {
/* *INDENT-OFF* */
{ ggo->recursive_given,    &options->recursive,    ggo->recursive_arg,    0 },
{ ggo->force_load_given,   &options->force,        ggo->force_load_arg,   0 },
{ ggo->build_menus_given,  &options->build_menus,  ggo->build_menus_arg,  1 },
{ ggo->slide_show_given,   &options->start_show,   ggo->slide_show_arg,   0 },
{ ggo->add_all_given,      &option_add_all,        ggo->add_all_arg,      0 },
{ FALSE,                   NULL,                   NULL,                  0 }
/* *INDENT-ON* */
    };

    ggo_int ints[] = {
/* *INDENT-OFF* */
{ FALSE,                   NULL,                   FALSE,              0 }
/* *INDENT-ON* */
    };

    ggo_flag *flag;
    ggo_int *opt_int;

    for (flag = flags; flag->flag != NULL; flag++)
        if (flag->given) {
            *flag->flag = get_on_off(flag->str);
            if (flag->not)
                *flag->flag ^= TRUE;
        }

    for (opt_int = ints; opt_int->opt_val != NULL; opt_int++)
        if (opt_int->given && opt_int->value >= opt_int->minimum)
            *opt_int->opt_val = opt_int->value;
}

#define FLAG_ON(flag) (flag##_given && get_on_off(flag##_arg))

/*
 * We temporarily close stderr because unknown and gtk arguments are handled
 * afterwards.
 */
static struct gengetopt_args_info *parse_cmdline(gint argc, gchar ** argv,
                                                 gboolean silent)
{
    struct gengetopt_args_info *ggo = g_new(struct gengetopt_args_info, 1);
    gint fd = -1;

    if (silent) {
        /* Silence stderr. */
        fd = dup(STDERR_FILENO);
        close(STDERR_FILENO);
    }

    if (cmdline_parser(argc, argv, ggo) != 0) {
        cmdline_parser_free(ggo);
        g_free(ggo);
        ggo = NULL;
    }

    if (silent) {
        /* Restore stderr. */
        dup2(fd, STDERR_FILENO);
        close(fd);
    }

    return ggo;
}

typedef enum {
    INIT_BAD_CMDLINE,
    INIT_OK,
    INIT_NO_IMAGES
} init_res;

static init_res init_args(gint argc, gchar ** argv, gboolean gtk_initialized)
{
    options_struct *rc_file;
    gint nb_inserted = 0;
    struct gengetopt_args_info *ggo =
        parse_cmdline(argc, argv, !gtk_initialized);
    init_res res = INIT_BAD_CMDLINE;

    if (ggo == NULL)
        return res;

    if (!ggo->collection_given && !FLAG_ON(ggo->client) &&
        !FLAG_ON(ggo->client_clear) && !gtk_initialized)
        /*
         * We want a "graphical" gliv, so init it after gtk
         * to be able to show the progress dialog when loading collections.
         */
        goto end;


    res = INIT_OK;

    /* Command line (some flags only). */

    if (FLAG_ON(ggo->sort) && FLAG_ON(ggo->shuffle)) {
        g_printerr(_("Cannot sort and shuffle at the same time\n"));
        quit(1);
    }

    /* Configuration file. */
    rc_file = load_rc(!ggo->glivrc_given, ggo->glivrc_arg);
    options = g_memdup(rc_file, sizeof(options_struct));

    /* Command line (remaining options). */
    fill_options(ggo);

    if (gtk_initialized) {
        /* We use the (rt == NULL) check to see if gtk is initialized or not */
        rt = g_new(rt_struct, 1);

        rt->cursor_hidden = FALSE;
        rt->help = FALSE;
        rt->alpha_checks_changed = TRUE;
    }

    if (FLAG_ON(ggo->null))
        nb_inserted =
            init_from_null_filenames(FLAG_ON(ggo->sort), FLAG_ON(ggo->shuffle),
                                     option_add_all);

    if (ggo->inputs_num > 0) {
        /* There are filenames on the command line. */
        nb_inserted +=
            init_list(ggo->inputs, ggo->inputs_num, FLAG_ON(ggo->sort),
                      FLAG_ON(ggo->shuffle), option_add_all);

        if (nb_inserted == 0 && gtk_initialized)
            res = INIT_NO_IMAGES;
    }

    if (ggo->collection_given) {
        options->save_quit = FALSE;
        quit(serialize_collection_nogui(ggo->collection_arg));
    }

    if (FLAG_ON(ggo->client) || FLAG_ON(ggo->client_clear)) {
        options->save_quit = FALSE;
        if (FLAG_ON(ggo->client) && FLAG_ON(ggo->client_clear)) {
            g_printerr(_("The --client and --client-clear command line options"
                         " are mutually exclusive\n"));
            quit(1);
        }

        if (connect_server(FLAG_ON(ggo->client_clear)))
            /* Successfully reused a GLiv window. */
            quit(0);
    }

    options->initial_geometry = g_strdup(ggo->geometry_arg);

  end:
    cmdline_parser_free(ggo);
    g_free(ggo);
    return res;
}

G_GNUC_NORETURN void quit(gint code)
{
    if (options != NULL && options->save_quit)
        save_rc(options);

    exit(code);
}

static GtkCheckButton *get_stop_confirm_button(void)
{
    GtkCheckButton *confirm;
    gchar *message;

    if (options->save_quit)
        message = g_strdup(_("Do not ask again"));
    else
        message =
            g_strconcat(_("Do not ask again"),
                        " (", _("Options will be saved"), ")", NULL);

    confirm =
        GTK_CHECK_BUTTON(gtk_check_button_new_with_mnemonic
                         (add_mnemonic(message)));

    gtk_widget_show_all(GTK_WIDGET(confirm));
    g_free(message);

    return confirm;
}

gboolean gui_quit(void)
{
    GtkMessageDialog *dialog;
    GtkCheckButton *stop_confirm;
    gchar *msg;
    gint response;

    if (options->confirm_quit == FALSE)
        quit(0);

    msg = _("Do you really want to quit GLiv?");

    dialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(NULL,
                                                       GTK_DIALOG_MODAL,
                                                       GTK_MESSAGE_QUESTION,
                                                       GTK_BUTTONS_YES_NO,
                                                       "%s", msg));

    stop_confirm = get_stop_confirm_button();
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
                      GTK_WIDGET(stop_confirm));

    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);

    response = run_modal_dialog(GTK_DIALOG(dialog));

    if (response == GTK_RESPONSE_YES) {
        options->confirm_quit =
            !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_confirm));

        if (!options->confirm_quit && !options->save_quit)
            save_rc(options);

        quit(0);
    }

    gtk_widget_destroy(GTK_WIDGET(dialog));
    return TRUE;
}

gint main(gint argc, gchar ** argv)
{
    init_res res;

    /* i18n */
#ifdef ENABLE_NLS
    gtk_set_locale();
#ifdef LOCALEDIR
    bindtextdomain(PACKAGE, LOCALEDIR);
#endif
    textdomain(PACKAGE);
    bind_textdomain_codeset(PACKAGE, "UTF-8");
#endif

    g_thread_init(NULL);
    g_type_init();

    res = init_args(argc, argv, FALSE);

    gtk_init(&argc, &argv);
    gtk_gl_init(&argc, &argv);

    if (res == INIT_BAD_CMDLINE)
        /* Now that GTK may have removed some arguments */
        res = init_args(argc, argv, TRUE);

    switch (res) {
    case INIT_NO_IMAGES:
        DIALOG_MSG(_("No image found"));
        break;

    case INIT_BAD_CMDLINE:
        quit(1);
        break;

    case INIT_OK:
        /* OK */
        break;
    }

    create_windows();
    start_server();

    gtk_main();

    return 0;
}
