/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Gypsy
 *
 * A simple to use and understand GPSD replacement
 * that uses D-Bus, GLib and memory allocations
 *
 * Author: Iain Holmes <iain@gnome.org>
 * Copyright (C) 2007
 *
 * 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <glib.h>

#include <dbus/dbus-protocol.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-bindings.h>

#include "gypsy-debug.h"
#include "gypsy-discovery.h"
#include "gypsy-server.h"

#define GYPSY_NAME "org.freedesktop.Gypsy"
#define DEFAULT_PID_FILE LOCALSTATEDIR"/run/Gypsy.pid"

static GMainLoop *mainloop;
/* This is a bit ugly, but it works */
char* nmea_log = NULL;

guint gypsy_debug_flags = 0; /* global gypsy debug flag */
static const GDebugKey gypsy_debug_keys[] = {
	{ "nmea", GYPSY_DEBUG_NMEA },
	{ "server", GYPSY_DEBUG_SERVER },
	{ "client", GYPSY_DEBUG_CLIENT },
	{ "discovery", GYPSY_DEBUG_DISCOVERY },
};

static void
gypsy_terminate (GObject *object,
		 gpointer userdata)
{
	g_main_loop_quit (mainloop);
}

static void
write_pidfile (const char *pidfile)
{
	FILE *f;

	if ((f = fopen (pidfile, "w")) == NULL) {
		g_printerr ("Opening %s failed: %s\n", pidfile, strerror (errno));
		return;
	}
	if (fprintf (f, "%d", getpid ()) < 0) {
		g_printerr ("Writing to %s failed: %s\n", pidfile, strerror (errno));
	}

	if (fclose (f)) {
		g_printerr ("Closing %s failed: %s\n", pidfile, strerror (errno));
	}
}

static void
name_owner_changed (DBusGProxy  *proxy,
		    const char  *name,
		    const char  *prev_owner,
		    const char  *new_owner,
		    GypsyServer *server)
{
	if (strcmp (new_owner, "") == 0 && strcmp (name, prev_owner) == 0) {
		gypsy_server_remove_clients (server, prev_owner);
	}
}

static gboolean
gypsy_arg_debug_cb (const char *key,
		    const char *value,
		    gpointer    userdata)
{
	gypsy_debug_flags |= g_parse_debug_string
		(value, gypsy_debug_keys, G_N_ELEMENTS (gypsy_debug_keys));
	return TRUE;
}

void
_gypsy_message (const char *format, ...)
{
	va_list ap;

	va_start (ap, format);
	g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, format, ap);
	va_end (ap);
}

int
main (int    argc,
      char **argv)
{
	GOptionContext *context;
	DBusGConnection *conn;
	DBusGProxy *proxy;
	GError *error = NULL;
	guint32 request_name_ret;
	GypsyServer *gypsy;
	GypsyDiscovery *discovery;
	gboolean become_daemon = FALSE;
	gboolean auto_terminate = TRUE;
	char *pidfile = NULL;
	char *user_pidfile = NULL;
	const char *env_string;

	GOptionEntry entries[] = {
		{ "nmea-log", 0, 0, G_OPTION_ARG_FILENAME, &nmea_log, "Log NMEA data to FILE.[device]", "FILE" },
		{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &become_daemon, "Don't become a daemon", NULL },
		{ "pid-file", 0, 0, G_OPTION_ARG_FILENAME, &user_pidfile, "Specify the location of a PID file", "FILE" },
		{ "gypsy-debug", 0, 0, G_OPTION_ARG_CALLBACK, gypsy_arg_debug_cb, "Gypsy debugging flags to set", "FLAGS" },
		{ "no-auto-terminate", 0, 0, G_OPTION_ARG_NONE, &auto_terminate, "Don't terminate after last connection closes", NULL },
		{ NULL }
	};

	env_string = g_getenv ("GYPSY_DEBUG");
	if (env_string != NULL) {
		gypsy_debug_flags = g_parse_debug_string
			(env_string, gypsy_debug_keys,
			 G_N_ELEMENTS (gypsy_debug_keys));
		env_string = NULL;
	}

	context = g_option_context_new ("- GPS daemon");
	g_option_context_add_main_entries (context, entries, NULL);

	if (!g_option_context_parse (context, &argc, &argv, &error)) {
#if GLIB_CHECK_VERSION(2, 14, 0)
		char *help;
		help = g_option_context_get_help (context, TRUE, NULL);
		g_print (help);
		g_free (help);
#else
		g_printerr ("Cannot parse arguments: %s\n", error->message);
#endif
		g_error_free (error);
		return 1;
	}

	pidfile = g_strdup (user_pidfile ? user_pidfile : DEFAULT_PID_FILE);

	/* Tricky: become_daemon is FALSE by default, so unless it's TRUE
	   because of a CLI option, it'll become TRUE after this
	*/
	become_daemon = !become_daemon;
	if (become_daemon) {
		if (daemon (0, 0) < 0) {
			int saved_errno;

			saved_errno = errno;
			g_printerr ("Could not daemonize: %s [error %u]\n",
				    g_strerror (saved_errno), saved_errno);
			return 1;
		}
		write_pidfile (pidfile);
	}
	g_free (pidfile);

	g_option_context_free (context);

	umask (022);
#if !GLIB_CHECK_VERSION(2,35,0)
	g_type_init ();
#endif
	mainloop = g_main_loop_new (NULL, FALSE);

	conn = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
	if (!conn) {
		g_error ("Error getting bus: %s", error->message);
		return 1;
	}

	proxy = dbus_g_proxy_new_for_name (conn,
					   DBUS_SERVICE_DBUS,
					   DBUS_PATH_DBUS,
					   DBUS_INTERFACE_DBUS);
	if (!org_freedesktop_DBus_request_name (proxy, GYPSY_NAME,
						0,  &request_name_ret,
						&error)) {
		g_error ("Error registering D-Bus service %s: %s",
			 GYPSY_NAME, error->message);
		return 1;
	}

	/* Just quit if GPS is already running */
	if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
		return 1;
	}

	gypsy = gypsy_server_new (auto_terminate);
	g_signal_connect (G_OBJECT (gypsy), "terminate",
			  G_CALLBACK (gypsy_terminate), NULL);

	dbus_g_proxy_add_signal (proxy, "NameOwnerChanged",
				 G_TYPE_STRING, G_TYPE_STRING,
				 G_TYPE_STRING, G_TYPE_INVALID);
	dbus_g_proxy_connect_signal (proxy, "NameOwnerChanged",
				     G_CALLBACK (name_owner_changed),
				     gypsy, NULL);

	dbus_g_connection_register_g_object (conn, "/org/freedesktop/Gypsy", G_OBJECT (gypsy));

	discovery = g_object_new (GYPSY_TYPE_DISCOVERY, NULL);
	dbus_g_connection_register_g_object (conn,
					     "/org/freedesktop/Gypsy/Discovery",
					     G_OBJECT (discovery));
	g_main_loop_run (mainloop);

	return 0;
}