#include <glib.h>

#ifndef _WIN32
# include <signal.h>
# include <glib-unix.h>
#else /* _WIN32 */
# include <windows.h>
#endif /* _WIN32 */

#include <birb.h>

static void
birb_demo_readline_read_cb(GObject *source, GAsyncResult *result, gpointer data);

static GCancellable *cancellable = NULL;

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
birb_demo_readline_show_prompt(BirbReadline *readline) {
	g_print("%s ", birb_readline_get_prompt(readline));

	birb_readline_read_stdin_async(readline,
	                               cancellable,
	                               birb_demo_readline_read_cb,
	                               NULL);
}

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static void
birb_demo_readline_read_cb(GObject *source,
                           GAsyncResult *result,
                           G_GNUC_UNUSED gpointer data)
{
	BirbReadline *readline = BIRB_READLINE(source);
	GError *error = NULL;
	char *line = NULL;

	line = birb_readline_read_stream_finish(readline, result, &error);
	if(error != NULL) {
		g_warning("failed to read input: %s", error->message);

		g_clear_error(&error);
	} else {
		g_print("you entered: '%s'\n", line);
		g_clear_pointer(&line, g_free);
	}

	birb_demo_readline_show_prompt(readline);
}

#ifndef _WIN32
static gboolean
birb_demo_readline_sigint_cb(G_GNUC_UNUSED gpointer data) {
	g_warning("caught SIGINT, aborting");

	g_cancellable_cancel(cancellable);

	return G_SOURCE_REMOVE;
}

static gboolean
birb_demo_readline_sigterm_cb(G_GNUC_UNUSED gpointer data) {
	g_warning("caught SIGTERM, aborting");

	g_cancellable_cancel(cancellable);

	return G_SOURCE_REMOVE;
}
#else /* _WIN32 */
static BOOL WINAPI
birb_demo_readline_console_ctrl_handler(DWORD sig) {
	switch(sig) {
	case CTRL_C_EVENT:
		g_warning("caught control-c, aborting");
		break;
	case CTRL_BREAK_EVENT:
		g_warning("caught control-break, aborting");
		break;
	case CTRL_CLOSE_EVENT:
		g_warning("window closing, aboring");
		break;
	default:
		break;
	}

	g_cancellable_cancel(cancellable);

	return TRUE;
}
#endif /* _WIN32 */

static void
birb_demo_readline_cancelled_cb(G_GNUC_UNUSED GCancellable *self,
                                gpointer data)
{
	GMainLoop *main_loop = data;

	g_main_loop_quit(main_loop);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) {
	BirbReadline *readline = NULL;
	GMainLoop *main_loop = NULL;

	main_loop = g_main_loop_new(NULL, FALSE);

	cancellable = g_cancellable_new();
	g_cancellable_connect(cancellable,
	                      G_CALLBACK(birb_demo_readline_cancelled_cb),
	                      main_loop,
	                      NULL);

#ifndef _WIN32
	g_unix_signal_add(SIGINT, birb_demo_readline_sigint_cb, NULL);
	g_unix_signal_add(SIGTERM, birb_demo_readline_sigterm_cb, NULL);
#else /* _WIN32 */
	SetConsoleCtrlHandler(birb_demo_readline_console_ctrl_handler, TRUE);
#endif /* _WIN32 */

	readline = birb_readline_new("birb>");

	birb_demo_readline_show_prompt(readline);

	g_main_loop_run(main_loop);

	g_clear_object(&cancellable);
	g_clear_object(&readline);

	return 0;
}
