/* xdaliclock, Copyright © 1991-2022 Jamie Zawinski.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 * This is startup initialization and the "application" class.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define _GNU_SOURCE  // For strcasestr in <string.h>

#include "app.h"
#include "window.h"
#include "prefs.h"

const char *progname;

struct _XDaliClock {
  GtkApplication parent;
};

typedef struct _XDaliClockPrivate XDaliClockPrivate;

struct _XDaliClockPrivate {
  XDaliClockPrefs *prefs;
  dali_config config;
};


G_DEFINE_TYPE_WITH_PRIVATE (XDaliClock, xdaliclock_app, GTK_TYPE_APPLICATION)

dali_config *
xdaliclock_config (XDaliClock *app)
{
  XDaliClockPrivate *priv = xdaliclock_app_get_instance_private (app);
  return &priv->config;
}


static void
xdaliclock_app_init (XDaliClock *app)
{
  XDaliClockPrivate *priv = xdaliclock_app_get_instance_private (app);
  dali_config *c = &priv->config;
  memset (c, 0, sizeof(*c));
}


static void
prefs_closed_cb (GtkWidget *widget, gpointer data)
{
  XDaliClock *app = XDALICLOCK_APP (data);
  XDaliClockPrivate *priv = xdaliclock_app_get_instance_private (app);
  priv->prefs = NULL;
}


static void
preferences_cb (GSimpleAction *action, GVariant *parameter, gpointer data)
{
  XDaliClock *app = XDALICLOCK_APP (data);
  XDaliClockPrivate *priv = xdaliclock_app_get_instance_private (app);
  GtkWindow *win = gtk_application_get_active_window (GTK_APPLICATION (app));
  if (!priv->prefs) {
    priv->prefs = xdaliclock_app_prefs_new (XDALICLOCK_APP_WINDOW (win),
                                            &priv->config);
    g_signal_connect (priv->prefs, "destroy", G_CALLBACK(prefs_closed_cb), app);
  }
  gtk_window_present (GTK_WINDOW (priv->prefs));
}


static void
button_cb (GtkWidget *self, GdkEventButton *event, gpointer data)
{
  if (event->type != GDK_BUTTON_RELEASE)
    return;
  if (event->button != 2 && event->button != 3)
    return;

  XDaliClock *app = XDALICLOCK_APP (data);
  preferences_cb (NULL, NULL, app);
}


// Because of event-propagation bullshit, we can't read mouse events on
// both the app and the window, so window calls in to here.
void
xdaliclock_app_open_prefs (XDaliClock *app)
{
  preferences_cb (NULL, NULL, app);
}


static void
quit_cb (GSimpleAction *action, GVariant *parameter, gpointer app)
{
  g_application_quit (G_APPLICATION (app));
}


static GActionEntry app_entries[] = {
  { "preferences", preferences_cb, NULL, NULL, NULL },
  { "quit",        quit_cb,        NULL, NULL, NULL }
};


static void
xdaliclock_app_startup (GApplication *app)
{
  const gchar *quit_accels[] = { "<Ctrl>Q", "Q", "q", NULL };

  G_APPLICATION_CLASS (xdaliclock_app_parent_class)->startup (app);

  g_action_map_add_action_entries (G_ACTION_MAP (app),
                                   app_entries, G_N_ELEMENTS (app_entries),
                                   app);
  gtk_application_set_accels_for_action (GTK_APPLICATION (app),
                                         "app.quit",
                                         quit_accels);
}


static void
xdaliclock_app_activate (GApplication *app)
{
  XDaliClockPrivate *priv =
    xdaliclock_app_get_instance_private (XDALICLOCK_APP (app));
  XDaliClockWindow *win = xdaliclock_app_window_new (XDALICLOCK_APP (app),
                                                     &priv->config);
  g_signal_connect (win, "button-release-event", G_CALLBACK (button_cb), app);
  gtk_window_present (GTK_WINDOW (win));
}


static void
xdaliclock_app_open (GApplication *app,
                     GFile **files, gint n_files,
                     const gchar *hint)
{
  GList *windows = gtk_application_get_windows (GTK_APPLICATION (app));
  if (windows)
    gtk_window_present (GTK_WINDOW (windows->data));
  else
    xdaliclock_app_activate (app);
}


static void
xdaliclock_app_class_init (XDaliClockClass *class)
{
  G_APPLICATION_CLASS (class)->startup  = xdaliclock_app_startup;
  G_APPLICATION_CLASS (class)->activate = xdaliclock_app_activate;
  G_APPLICATION_CLASS (class)->open     = xdaliclock_app_open;
}

static XDaliClock *
xdaliclock_app_new (void)
{
  return g_object_new (XDALICLOCK_APP_TYPE,
                       "application-id", "org.jwz.xdaliclock",
                       "flags", G_APPLICATION_HANDLES_OPEN,
                       NULL);
}


static void
logger (const gchar *domain, GLogLevelFlags log_level,
        const gchar *message, gpointer data)
{
  if (log_level & G_LOG_LEVEL_DEBUG) return;
  if (log_level & G_LOG_LEVEL_INFO) return;

  fprintf (stderr, "%s: %s: %s\n", progname, domain, message);

  if (strcasestr (message, "Settings schema") &&
      strcasestr (message, "not installed"))
    fprintf (stderr,
      "\n"
      "\tThe app won't launch unless you have run 'make install' to add a\n"
      "\tfile to /usr/share/glib-2.0/schemas/ and regenerate a system-wide\n"
      "\tdatabase first.  Yes, this is absurd.  GTK is a dumpster fire.\n"
      "\n"
#  if !defined(__OPTIMIZE__)
      "\tFor debugging, set $GSETTINGS_SCHEMA_DIR to the source directory.\n"
      "\n"
#  endif
    );
}


int
main (int argc, char *argv[])
{
  progname = argv[0];
  char *s = strrchr (progname, '/');
  if (s) progname = s+1;

  g_log_set_default_handler (logger, NULL);

  // #### Does this mess things up when running installed?
//  g_setenv ("GSETTINGS_SCHEMA_DIR", ".", FALSE);

  return g_application_run (G_APPLICATION (xdaliclock_app_new()), argc, argv);
}
