/*
 * 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@yahoo.fr>
 */

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

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

#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"

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,                    }
/* *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) {
        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 && !gtk_initialized)
        /*
         * We want a "graphical" gliv, so init it after gtk
         * to be able to show the progress dialog when loading collections.
         */
        return res;


    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)
        quit(serialize_collection_nogui(ggo->collection_arg));

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

    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);
}

gboolean gui_quit(void)
{
    GtkMessageDialog *dialog;
    gchar *msg;

    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));

    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);

    set_hide_cursor_enabled(FALSE);
    if (run_modal_dialog(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
        quit(0);

    gtk_widget_destroy(GTK_WIDGET(dialog));
    set_hide_cursor_enabled(TRUE);
    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;
}
