
/*
 * Osmo - a handy personal organizer
 *
 * Copyright (C) 2007 Tomasz Maka <pasp@users.sourceforge.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "gui.h"
#include "calendar.h"
#include "calendar_utils.h"
#include "utils.h"
#include "utils_date.h"
#include "utils_gui.h"
#include "i18n.h"
#include "check_events.h"
#include "options_prefs.h"
#include "contacts.h"

#ifdef BACKUP_SUPPORT
#include "backup.h"
#endif  /* BACKUP_SUPPORT */

#define RUN_FLAG_FILE   "lock"
#define OLD_CONFIG_DIRNAME ".osmo"
#ifdef HAVE_LIBWEBKIT
#define MAX_STYLESHEET_SIZE 1024*1024
#endif  /* HAVE_LIBWEBKIT */

static gboolean migrate_to_xdg_dirs(gchar *user_config, GUI *appGUI);
static gboolean extract_command_line_args(GOptionEntry *options, gint *argc, gchar ***argv, GUI *appGUI);
#ifdef HAVE_LIBWEBKIT
static gchar* load_stylesheet(const gchar* file);
#endif  /* HAVE_LIBWEBKIT */
/*------------------------------------------------------------------------------*/

int main(int argc, char **argv) {

GUI *appGUI = NULL;
CALENDAR *cal = NULL;

#ifdef TASKS_ENABLED
TASKS *tsk = NULL;
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
CONTACTS *cnt = NULL;
#endif  /* CONTACTS_ENABLED */

#ifdef NOTES_ENABLED
NOTES *nte = NULL;
#endif  /* NOTES_ENABLED */

OPTIONS *opt = NULL;
GtkWidget *info_dialog;
gchar tmpbuf[BUFFER_SIZE];
gint response = -1;
#ifndef WIN32
struct flock *s_lock = NULL;
#endif /* WIN32 */
int fhandle = 0;
gchar* output_codeset = NULL;

gboolean cmd_calendar = FALSE;
gboolean cmd_check_events = FALSE;
gint cmd_check_ndays_events = 0;
gchar *cmd_cfg_path = NULL;
#ifdef CONTACTS_ENABLED
gchar *cmd_mutt_query = NULL;
#endif  /* CONTACTS_ENABLED */
#ifdef HAVE_LIBWEBKIT
gchar *cmd_stylesheet_file = NULL;
#endif  /* HAVE_LIBWEBKIT */
GOptionEntry cmd_options[] = {
    { "calendar",   'c', 0, G_OPTION_ARG_NONE, &cmd_calendar, N_("Show small calendar window"), NULL  },
    { "check",      'e', 0, G_OPTION_ARG_NONE, &cmd_check_events, N_("Check for events since last run"), NULL  },
    { "days",       'd', 0, G_OPTION_ARG_INT, &cmd_check_ndays_events, N_("Number of days to check forward for events (default: 0)"), NULL  },
    { "config",     's', 0, G_OPTION_ARG_STRING, &cmd_cfg_path, N_("Set absolute path for settings and data files"), "STRING"  },
#ifdef CONTACTS_ENABLED
    { "mutt-query", 'q', 0, G_OPTION_ARG_STRING, &cmd_mutt_query, N_("Match contacts with given string"), "STRING" },
#endif  /* CONTACTS_ENABLED */
#ifdef HAVE_LIBWEBKIT
    { "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &cmd_stylesheet_file, N_("HTML renderer stylesheet file"), "STRING" },
#endif  /* HAVE_LIBWEBKIT */
    { NULL }
};

    appGUI = g_new0 (GUI, 1);
    g_return_val_if_fail (appGUI != NULL, -1);

    cal = g_new0 (CALENDAR, 1);
    g_return_val_if_fail (cal != NULL, -1);

#ifdef TASKS_ENABLED
    tsk = g_new0 (TASKS, 1);
    g_return_val_if_fail (tsk != NULL, -1);
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
    cnt = g_new0 (CONTACTS, 1);
    g_return_val_if_fail (cnt != NULL, -1);
#endif  /* CONTACTS_ENABLED */

#ifdef NOTES_ENABLED
    nte = g_new0 (NOTES, 1);
    g_return_val_if_fail (nte != NULL, -1);
#endif  /* NOTES_ENABLED */

    opt = g_new0 (OPTIONS, 1);
    g_return_val_if_fail (opt != NULL, -1);

    /* register modules */
    appGUI->cal = cal;

#ifdef TASKS_ENABLED
    appGUI->tsk = tsk;
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
    appGUI->cnt = cnt;
#endif  /* CONTACTS_ENABLED */

#ifdef NOTES_ENABLED
    appGUI->nte = nte;
#endif  /* NOTES_ENABLED */

    appGUI->opt = opt;

    appGUI->run_date = utl_date_get_current_julian ();
    appGUI->run_time = utl_time_get_current_seconds ();
    appGUI->key_counter = 0;

    /* default values */
    appGUI->hovering_over_link = FALSE;
    appGUI->hand_cursor = NULL;
    appGUI->regular_cursor = NULL;
    appGUI->gui_url_tag = NULL;
    appGUI->trayicon_popup_menu = NULL;

    appGUI->calendar_only = FALSE;
    appGUI->cal->datecal_bio = FALSE;
    appGUI->check_events = FALSE;
    appGUI->save_status = 0;
    appGUI->print_font_size = 10;

    g_sprintf (appGUI->version, "%02d%02d%02d", atoi(VERSION_MAJOR), atoi(VERSION_MINOR), atoi(VERSION_MICRO));

    appGUI->cal->last_selected_year = -1;
    appGUI->cal->date = utl_date_new_current ();
    appGUI->about_links_list = NULL;

#ifdef TASKS_ENABLED
    appGUI->tsk->next_id = 1;
    appGUI->tsk->tasks_list_store = NULL;
    appGUI->tsk->notifications = NULL;
    appGUI->tsk->notifications_enable = FALSE;
#endif  /* TASKS_ENABLED */

#ifdef CONTACTS_ENABLED
    appGUI->cnt->export_button = NULL;
    appGUI->cnt->output_file_entry = NULL;
    appGUI->cnt->write_flag = TRUE;
#endif  /* CONTACTS_ENABLED */

    if (!(output_codeset = bind_textdomain_codeset(PACKAGE, NULL))) {
        output_codeset = g_strdup("UTF-8");
    } else {
        output_codeset = g_strdup(output_codeset);
    }

    setlocale (LC_ALL, "");
    bindtextdomain (PACKAGE, LOCALEDIR);

    if (bind_textdomain_codeset(PACKAGE, output_codeset) == NULL) {
        fprintf(stderr, "Error setting gettext output codeset to %s\n", output_codeset);
        g_free (output_codeset);
        goto finish;
    }

    textdomain (PACKAGE);
    g_free (output_codeset);

    if (extract_command_line_args(cmd_options, &argc, &argv, appGUI) == FALSE) {
        goto finish;
    }

    appGUI->calendar_only = cmd_calendar;
    appGUI->check_events = cmd_check_events;
    appGUI->check_ndays_events = cmd_check_ndays_events;
    if(cmd_cfg_path) {
        fprintf(stderr, "%s\n", _("The user config path option is deprecated. Use XDG environment variables instead."));
    }
    if(migrate_to_xdg_dirs(cmd_cfg_path, appGUI) == FALSE) {
        goto finish;
    }
    if (prefs_get_config_filename (CONFIG_FILENAME, appGUI) == NULL) {
        fprintf(stderr, "%s\n", _("ERROR: Cannot create config files"));
        goto finish;
    }

#if defined(BACKUP_SUPPORT) && defined(HAVE_LIBGRINGOTTS)
    backup_restore_run (appGUI);
#endif  /* BACKUP_SUPPORT & HAVE_LIBGRINGOTTS */

#ifndef WIN32
    s_lock = g_new0 (struct flock, 1);
    s_lock->l_type = F_WRLCK;
    s_lock->l_whence = SEEK_SET;
    s_lock->l_start = 0;
    s_lock->l_len = 0;
#endif /* WIN32 */

    prefs_read_config (appGUI);

    gtk_init (&argc, &argv);

#ifdef CONTACTS_ENABLED
    if (cmd_mutt_query)
    {
        query (appGUI, cmd_mutt_query);
        goto finish;
    }
#endif  /* CONTACTS_ENABLED */

    if (appGUI->calendar_only == FALSE) {

        close(creat(prefs_get_runtime_filename (RUN_FLAG_FILE, appGUI), S_IRUSR | S_IWUSR));     /* create lock file */

        fhandle = open(prefs_get_runtime_filename (RUN_FLAG_FILE, appGUI), O_RDWR);
#ifndef WIN32
        if (fhandle) {
            if (fcntl(fhandle, F_SETLK, s_lock) == -1) {
                close (fhandle);

                g_snprintf (tmpbuf, BUFFER_SIZE, "%s %s\n\n%s",
                            _("Another copy of OSMO is already running."),
                            _("Simultaneously use two or more copies of OSMO can be a cause of data loss."),
                            _("Do you really want to continue?"));

                GtkWidget *toplevel_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
                info_dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(toplevel_window), GTK_DIALOG_MODAL,
                                                      GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, tmpbuf, NULL);

                gtk_window_set_type_hint (GTK_WINDOW (info_dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
                gtk_window_set_skip_pager_hint(GTK_WINDOW(info_dialog), TRUE);
                gtk_window_set_skip_taskbar_hint(GTK_WINDOW(info_dialog), TRUE);
                gtk_window_set_keep_above(GTK_WINDOW(info_dialog), TRUE);
                gtk_window_set_title(GTK_WINDOW(info_dialog), _("Warning"));
                gtk_window_set_position(GTK_WINDOW(info_dialog), GTK_WIN_POS_CENTER);
                gtk_widget_show (info_dialog);

                response = gtk_dialog_run(GTK_DIALOG(info_dialog));
                gtk_widget_destroy(info_dialog);
                gtk_widget_destroy(toplevel_window);

                if (response == GTK_RESPONSE_NO || response == GTK_RESPONSE_DELETE_EVENT) {
                    goto finish;
                }
            }
        }
#endif /* WIN32 */
    }


#ifdef HAVE_LIBNOTIFY
#ifdef TASKS_ENABLED
    notify_init("Osmo notification");
#endif  /* TASKS_ENABLED */
#endif  /* HAVE_LIBNOTIFY */

    if (appGUI->calendar_only != TRUE) {
        /* setup timer at 1000ms (1s) interval */
        appGUI->timer = g_timeout_add (1000, time_handler, appGUI);
    }

    config.run_counter++;

#ifndef REV
    g_set_application_name ("Osmo " VERSION_MAJOR "." VERSION_MINOR "." VERSION_MICRO);
#else
    g_set_application_name ("Osmo (development)");
#endif

#ifdef HAVE_LIBWEBKIT
    if(cmd_stylesheet_file) {
        appGUI->stylesheet = load_stylesheet(cmd_stylesheet_file);
    }
#endif  /* HAVE_LIBWEBKIT */

    gtk_window_set_default_icon_name ("osmo");

    if (gui_create_window (appGUI) == TRUE) {
        gtk_main ();
    }

    if (appGUI->calendar_only == FALSE) {
        config.lastrun_date = utl_date_get_current_julian ();
        config.lastrun_time = utl_time_get_current_seconds ();
    }

    if (appGUI->check_events == FALSE) {
        prefs_write_config (appGUI);
    }

    if (fhandle && appGUI->calendar_only == FALSE) {
 #ifndef WIN32
        s_lock->l_type = F_UNLCK;
        fcntl(fhandle, F_SETLK, s_lock);
 #endif /* WIN32 */
        close (fhandle);
    }

finish:
    g_date_free (appGUI->cal->date);
    utl_gui_url_remove_links (&appGUI->about_links_list, NULL);
    if (appGUI->hand_cursor != NULL) {
        g_object_unref (appGUI->hand_cursor);
        g_object_unref (appGUI->regular_cursor);
    }
    g_free (opt);

#ifdef NOTES_ENABLED
    g_free (nte);
#endif  /* NOTES_ENABLED */

#ifdef CONTACTS_ENABLED
    g_free (cnt);
#endif  /* CONTACTS_ENABLED */

#ifdef TASKS_ENABLED
    g_free (tsk);
#endif  /* TASKS_ENABLED */

#ifdef HAVE_LIBWEBKIT
    g_free(appGUI->stylesheet);
#endif  /* HAVE_LIBWEBKIT */

    g_free (cal);
    g_free (appGUI);
    g_free(cmd_cfg_path);
#ifdef CONTACTS_ENABLED
    g_free(cmd_mutt_query);
#endif  /* CONTACTS_ENABLED */
#ifdef HAVE_LIBWEBKIT
    g_free(cmd_stylesheet_file);
#endif  /* HAVE_LIBWEBKIT */
#ifndef WIN32
    g_free (s_lock);
#endif /* WIN32 */
    return 0;
}

/*------------------------------------------------------------------------------*/
/* This is a temporary migration from the old Osmo .osmo directory to           */
/* XDG directories. DELETE IT after the migration support is no longer needed.  */
/*------------------------------------------------------------------------------*/

gchar* 
get_old_config_dir (gchar *user_config, GUI *appGUI) {

static gchar dirname[PATH_MAX];

    if (user_config == NULL) {
#if defined(CONFIG_PATH) && defined(CONFIG_DIR)
        g_snprintf (dirname, PATH_MAX, "%s%c%s", CONFIG_PATH, G_DIR_SEPARATOR, CONFIG_DIR);
#elif defined(CONFIG_DIR)
        g_snprintf (dirname, PATH_MAX, "%s%c%s", g_get_home_dir(), G_DIR_SEPARATOR, CONFIG_DIR);
#elif defined(CONFIG_PATH)
        g_snprintf (dirname, PATH_MAX, "%s%c%s", CONFIG_PATH, G_DIR_SEPARATOR, OLD_CONFIG_DIRNAME);
#else
        g_snprintf (dirname, PATH_MAX, "%s%c%s", g_get_home_dir(), G_DIR_SEPARATOR, OLD_CONFIG_DIRNAME);
#endif
    } else {
        g_strlcpy (dirname, user_config, PATH_MAX);
    }

    return dirname;
}

static gboolean
migrate_to_xdg_dirs(gchar *user_config, GUI *appGUI) {
    gchar *config = prefs_get_config_filename(CONFIG_FILENAME, appGUI);
    if (!g_file_test(config, G_FILE_TEST_EXISTS)) {
        gchar *old_dirname = get_old_config_dir(user_config, appGUI);
        if (g_file_test(old_dirname, G_FILE_TEST_EXISTS)) {
            fprintf(stderr, _("The old Osmo config directory %s is found. "
                    "The config files and data will be copied to XDG compatible directories. "
                    "Delete the old config directory after a successful migration.\n"),
                    old_dirname);
            return prefs_restore(old_dirname, appGUI);
        }
    }
    return TRUE;
}
/*------------------------------------------------------------------------------*/
static gboolean
extract_command_line_args(GOptionEntry *options, gint *argc, gchar ***argv, GUI *appGUI) {
    GOptionContext *cmd_context;
    gchar *program_summary;
    GError *err = NULL;

    program_summary = g_strdup_printf(N_("OSMO v%s (handy personal organizer)\n"
            "Copyright (c) 2007-2017 Tomasz Maka <pasp@users.sourceforge.net>\n\n"
            "Configuration directory = %s\n"
            "Data directory          = %s"),
            VERSION, prefs_get_config_filename("", appGUI), prefs_get_data_filename("", appGUI));

    cmd_context = g_option_context_new(NULL);
    g_option_context_set_translation_domain(cmd_context, TRANSLATION_DOMAIN);
    g_option_context_set_summary(cmd_context, program_summary);
    g_free(program_summary);
    g_option_context_add_main_entries(cmd_context, options, TRANSLATION_DOMAIN);
    if (g_option_context_parse(cmd_context, argc, argv, &err) == FALSE) {
        fprintf(stderr, _("Failed to parse the command line arguments %s.\n"), err->message);
        g_option_context_free(cmd_context);
        g_error_free (err);
        return FALSE;
    }
    g_option_context_free(cmd_context);
    return TRUE;
}
/*------------------------------------------------------------------------------*/
#ifdef HAVE_LIBWEBKIT
static gchar*
load_stylesheet(const gchar* file) {
    GStatBuf st;
    gchar *content;

    g_return_val_if_fail(g_stat(file, &st) == 0, NULL);
    if (st.st_size > MAX_STYLESHEET_SIZE) {
        fprintf(stderr, _("Stylesheet file is too large. Max size is %d.\n"), MAX_STYLESHEET_SIZE);
        return NULL;
    }
    g_return_val_if_fail(g_file_get_contents(file, &content, NULL, NULL), NULL);
    return content;
}
/*------------------------------------------------------------------------------*/
#endif  /* HAVE_LIBWEBKIT */
