#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include "dbus.h"
#include "mako.h"
#include "notification.h"
#include "render.h"
#include "surface.h"
#include "wayland.h"

static const char usage[] =
	"Usage: mako [options...]\n"
	"\n"
	"  -h, --help                          Show help message and quit.\n"
	"  -c, --config <path>                 Path to config file.\n"
	"      --font <font>                   Font family and size.\n"
	"      --background-color <color>      Background color.\n"
	"      --text-color <color>            Text color.\n"
	"      --width <px>                    Notification width.\n"
	"      --height <px>                   Max notification height.\n"
	"      --margin <px>[,<px>...]         Margin values, comma separated.\n"
	"                                      Up to four values, with the same\n"
	"                                      meaning as in CSS.\n"
	"      --padding <px>[,<px>...]        Padding values, comma separated.\n"
	"                                      Up to four values, with the same\n"
	"                                      meaning as in CSS.\n"
	"      --border-size <px>              Border size.\n"
	"      --border-color <color>          Border color.\n"
	"      --border-radius <px>            Corner radius\n"
	"      --progress-color <color>        Progress indicator color.\n"
	"      --icons <0|1>                   Show icons in notifications.\n"
	"      --icon-path <path>[:<path>...]  Icon search path, colon delimited.\n"
	"      --max-icon-size <px>            Set max size of icons.\n"
	"      --markup <0|1>                  Enable/disable markup.\n"
	"      --actions <0|1>                 Enable/disable application action\n"
	"                                      execution.\n"
	"      --format <format>               Format string.\n"
	"      --hidden-format <format>        Format string.\n"
	"      --max-visible <n>               Max number of visible notifications.\n"
	"      --max-history <n>               Max size of history buffer.\n"
	"      --history <0|1>                 Add expired notifications to history.\n"
	"      --sort <sort_criteria>          Sorts incoming notifications by time\n"
	"                                      and/or priority in ascending(+) or\n"
	"                                      descending(-) order.\n"
	"      --default-timeout <timeout>     Default timeout in milliseconds.\n"
	"      --ignore-timeout <0|1>          Enable/disable notification timeout.\n"
	"      --output <name>                 Show notifications on this output.\n"
	"      --layer <layer>                 Arrange notifications at this layer.\n"
	"      --anchor <position>             Position on output to put notifications.\n"
	"\n"
	"Colors can be specified with the format #RRGGBB or #RRGGBBAA.\n";

static bool init(struct mako_state *state) {
	if (!init_dbus(state)) {
		return false;
	}
	if (!init_wayland(state)) {
		finish_dbus(state);
		return false;
	}
	if (!init_event_loop(&state->event_loop, state->bus, state->display)) {
		finish_dbus(state);
		finish_wayland(state);
		return false;
	}
	wl_list_init(&state->notifications);
	wl_list_init(&state->history);
	state->current_mode = strdup("default");
	return true;
}

static void finish(struct mako_state *state) {
	free(state->current_mode);

	struct mako_notification *notif, *tmp;
	wl_list_for_each_safe(notif, tmp, &state->notifications, link) {
		destroy_notification(notif);
	}
	wl_list_for_each_safe(notif, tmp, &state->history, link) {
		destroy_notification(notif);
	}

	struct mako_surface *surface, *stmp;
	wl_list_for_each_safe(surface, stmp, &state->surfaces, link) {
		destroy_surface(surface);
	}
	finish_event_loop(&state->event_loop);
	finish_wayland(state);
	finish_dbus(state);
}

static struct mako_event_loop *event_loop = NULL;

int main(int argc, char *argv[]) {
	struct mako_state state = {0};

	state.argc = argc;
	state.argv = argv;

	wl_list_init(&state.surfaces);

	// This is a bit wasteful, but easier than special-casing the reload.
	init_default_config(&state.config);
	int ret = reload_config(&state.config, argc, argv);

	if (ret < 0) {
		finish_config(&state.config);
		return EXIT_FAILURE;
	} else if (ret > 0) {
		finish_config(&state.config);
		printf("%s", usage);
		return EXIT_SUCCESS;
	}

	if (!init(&state)) {
		finish_config(&state.config);
		return EXIT_FAILURE;
	}

	event_loop = &state.event_loop;

	ret = run_event_loop(&state.event_loop);

	finish(&state);
	finish_config(&state.config);

	return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
