/*
 * xF.c: a toothless IRC client frontend
 *
 * Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "config.h"
#define PROGRAM_NAME "xF"

#include "common.c"
#include "xC-proto.c"

#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xft/Xft.h>

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

static struct
{
	bool polling;
	struct connector connector;
	int socket;
}
g;

static void
on_connector_connecting (void *user_data, const char *address)
{
	(void) user_data;
	print_status ("connecting to %s...", address);
}

static void
on_connector_error (void *user_data, const char *error)
{
	(void) user_data;
	print_status ("connection failed: %s", error);
}

static void
on_connector_failure (void *user_data)
{
	(void) user_data;
	exit_fatal ("giving up");
}

static void
on_connector_connected (void *user_data, int socket, const char *hostname)
{
	(void) user_data;
	(void) hostname;
	g.polling = false;
	g.socket = socket;
}

static void
protocol_test (const char *host, const char *port)
{
	struct poller poller = {};
	poller_init (&poller);

	connector_init (&g.connector, &poller);
	g.connector.on_connecting = on_connector_connecting;
	g.connector.on_error      = on_connector_error;
	g.connector.on_connected  = on_connector_connected;
	g.connector.on_failure    = on_connector_failure;

	connector_add_target (&g.connector, host, port);

	g.polling = true;
	while (g.polling)
		poller_run (&poller);

	connector_free (&g.connector);

	struct str s = str_make ();
	str_pack_u32 (&s, 0);
	struct relay_command_message m = {};
	m.data.hello.command = RELAY_COMMAND_HELLO;
	m.data.hello.version = RELAY_VERSION;
	if (!relay_command_message_serialize (&m, &s))
		exit_fatal ("serialization failed");

	uint32_t len = htonl (s.len - sizeof len);
	memcpy (s.str, &len, sizeof len);
	if (errno = 0, write (g.socket, s.str, s.len) != (ssize_t) s.len)
		exit_fatal ("short send or error: %s", strerror (errno));

	char buf[1 << 20] = "";
	while (errno = 0, read (g.socket, &len, sizeof len) == sizeof len)
	{
		len = ntohl (len);
		if (errno = 0, read (g.socket, buf, MIN (len, sizeof buf)) != len)
			exit_fatal ("short read or error: %s", strerror (errno));

		struct msg_unpacker r = msg_unpacker_make (buf, len);
		struct relay_event_message m = {};
		if (!relay_event_message_deserialize (&m, &r))
			exit_fatal ("deserialization failed");
		if (msg_unpacker_get_available (&r))
			exit_fatal ("trailing data");

		printf ("event: %d\n", m.data.event);
		relay_event_message_free (&m);
	}
	exit_fatal ("short read or error: %s", strerror (errno));
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

int
main (int argc, char *argv[])
{
	static const struct opt opts[] =
	{
		{ 'h', "help", NULL, 0, "display this help and exit" },
		{ 'V', "version", NULL, 0, "output version information and exit" },
		{ 0, NULL, NULL, 0, NULL }
	};

	struct opt_handler oh = opt_handler_make (argc, argv, opts,
		"HOST:PORT", "X11 frontend for xC.");

	int c;
	while ((c = opt_handler_get (&oh)) != -1)
	switch (c)
	{
	case 'h':
		opt_handler_usage (&oh, stdout);
		exit (EXIT_SUCCESS);
	case 'V':
		printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
		exit (EXIT_SUCCESS);
	default:
		print_error ("wrong options");
		opt_handler_usage (&oh, stderr);
		exit (EXIT_FAILURE);
	}

	argc -= optind;
	argv += optind;
	if (argc != 1)
	{
		opt_handler_usage (&oh, stderr);
		exit (EXIT_FAILURE);
	}
	opt_handler_free (&oh);

	char *address = xstrdup (argv[0]);
	const char *port = NULL, *host = tokenize_host_port (address, &port);
	if (!port)
		exit_fatal ("missing port number/service name");

	// TODO: Actually implement an X11-based user interface.
	protocol_test (host, port);
	return 0;
}