/*	$Id$ */
/*
 * Copyright (c) 2014, 2015 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * 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 <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifdef MAC_INTEGRATION
#include <gtkosxapplication.h>
#endif
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_histogram.h>
#include <kplot.h>

#include "extern.h"

static void
swin_init(struct swin *c, enum view view, GtkBuilder *b)
{

	c->window = win_init_window(b, "window1");
	c->menu = win_init_menubar(b, "menubar1");
	c->draw = win_init_draw(b, "drawingarea1");
	c->boxconfig = win_init_box(b, "box3");
	c->menufile = win_init_menuitem(b, "menuitem1");
	c->menuview = win_init_menuitem(b, "menuitem2");
	c->menutools = win_init_menuitem(b, "menuitem3");
	c->viewclone = win_init_menuitem(b, "menuitem15");
	c->viewpause = win_init_menuitem(b, "menuitem20");
	c->viewunpause = win_init_menuitem(b, "menuitem21");
	c->views[VIEW_ISLANDMEAN] = win_init_menucheck(b, "menuitem45");
	c->views[VIEW_ISLANDERMEAN] = win_init_menucheck(b, "menuitem46");
	c->views[VIEW_MEAN] = win_init_menucheck(b, "menuitem8");
	c->views[VIEW_SMEAN] = win_init_menucheck(b, "menuitem37");
	c->views[VIEW_SEXTM] = win_init_menucheck(b, "menuitem43");
	c->views[VIEW_SEXTI] = win_init_menucheck(b, "menuitem44");
	c->views[VIEW_EXTM] = win_init_menucheck(b, "menuitem25");
	c->views[VIEW_TIMESPDF] = win_init_menucheck(b, "menuitem38");
	c->views[VIEW_TIMESCDF] = win_init_menucheck(b, "menuitem39");
	c->views[VIEW_EXTMMAXPDF] = win_init_menucheck(b, "menuitem28");
	c->views[VIEW_EXTMMAXCDF] = win_init_menucheck(b, "menuitem29");
	c->views[VIEW_EXTI] = win_init_menucheck(b, "menuitem26");
	c->views[VIEW_EXTIMINPDF] = win_init_menucheck(b, "menuitem27");
	c->views[VIEW_EXTIMINCDF] = win_init_menucheck(b, "menuitem30");
	c->views[VIEW_EXTIMINS] = win_init_menucheck(b, "menuitem35");
	c->views[VIEW_DEV] = win_init_menucheck(b, "menuitem6");
	c->views[VIEW_POLY] = win_init_menucheck(b, "menuitem7");
	c->views[VIEW_POLYMINPDF] = win_init_menucheck(b, "menuitem9");
	c->views[VIEW_POLYMINCDF] = win_init_menucheck(b, "menuitem11");
	c->views[VIEW_MEANMINPDF] = win_init_menucheck(b, "menuitem10");
	c->views[VIEW_MEANMINCDF] = win_init_menucheck(b, "menuitem12");
	c->views[VIEW_MEANMINQ] = win_init_menucheck(b, "menuitem13");
	c->views[VIEW_MEANMINS] = win_init_menucheck(b, "menuitem22");
	c->views[VIEW_POLYMINS] = win_init_menucheck(b, "menuitem31");
	c->views[VIEW_EXTMMAXS] = win_init_menucheck(b, "menuitem33");
	c->views[VIEW_POLYMINQ] = win_init_menucheck(b, "menuitem14");
	c->menuquit = win_init_menuitem(b, "menuitem5");
	c->menuautoexport = win_init_menuitem(b, "menuitem49");
	c->menuunautoexport = win_init_menuitem(b, "menuitem50");
	c->menuclose = win_init_menuitem(b, "menuitem24");
	c->menusave = win_init_menuitem(b, "menuitem34");
	c->menusaveall = win_init_menuitem(b, "menuitem47");

	gtk_widget_show_all(GTK_WIDGET(c->window));

	gtk_window_set_title(GTK_WINDOW(c->window),
		gtk_menu_item_get_label
		(GTK_MENU_ITEM(c->views[view])));

	gtk_widget_hide(GTK_WIDGET(c->menuunautoexport));

#ifdef MAC_INTEGRATION
	gtk_widget_hide(GTK_WIDGET(c->menu));
	gtk_widget_hide(GTK_WIDGET(c->menuquit));
	gtkosx_application_set_menu_bar
		(gtkosx_application_get(),
		 GTK_MENU_SHELL(c->menu));
	gtkosx_application_sync_menubar
		(gtkosx_application_get());
#endif
}

/*
 * Dereference a simulation.
 * This means that we've closed an output window that's looking at a
 * particular simulation.
 * When the simulation has no more references (i.e., no more windows are
 * painting that simulation), then the simulation destroys itself.
 */
static void
on_sim_deref(gpointer dat)
{
	struct sim	*sim = dat;

	g_debug("%p: Simulation deref (now %zu)", sim, sim->refs - 1);
	if (0 != --sim->refs)
		return;
	g_debug("%p: Simulation terminating", sim);
	sim_stop(sim, NULL);
}

/*
 * Dereference all simulations owned by a given window.
 * This happens when a window is closing.
 */
static void
on_sims_deref(gpointer dat)
{

	g_list_free_full(dat, on_sim_deref);
}

static void
curwin_free(gpointer dat)
{
	struct curwin	*cur = dat;
	size_t		 i;

	g_debug("%p: Simwin freeing", cur);
	kdata_destroy(cur->winmean);
	kdata_destroy(cur->winstddev);
	kdata_destroy(cur->winfitmean);
	kdata_destroy(cur->winfitstddev);
	kdata_destroy(cur->winmextinctmean);
	kdata_destroy(cur->winmextinctstddev);
	kdata_destroy(cur->winiextinctmean);
	kdata_destroy(cur->winiextinctstddev);
	for (i = 0; i < VIEW__MAX; i++)
		kplot_free(cur->views[i]);
	cur->b->windows = g_list_remove(cur->b->windows, cur);
	on_sims_deref(cur->sims);
	g_free(cur->autosave);
	g_free(cur);
}

static void
window_add_configmarkup(GtkWidget *box, const gchar *fmt, ...)
{
	gchar		 buf[1024];
	GtkWidget	*w;
	va_list		 ap;

	va_start(ap, fmt);
	g_vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);

	w = gtk_label_new(NULL);
	gtk_label_set_markup(GTK_LABEL(w), buf);
	gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
	gtk_container_add(GTK_CONTAINER(box), w);
}

static void
window_add_config(GtkWidget *box, const gchar *fmt, ...)
{
	gchar		 buf[1024];
	GtkWidget	*w;
	va_list		 ap;

	va_start(ap, fmt);
	g_vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);

	w = gtk_label_new(buf);
	gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
	gtk_container_add(GTK_CONTAINER(box), w);
}

static void
window_add_sim(struct curwin *cur, struct sim *sim)
{
	GtkWidget	*box, *outbox, *leftbox;
	struct kdata	*stats[2];
	enum kplottype	 ts[2];
	struct kdatacfg	 solidcfg, transcfg;
	struct kdatacfg	*cfgs[2];
	double		 solid[4], trans[4];
	gchar		 label[64];
	struct ksmthcfg	 smth;

	/* Get our colours. */

	memcpy(solid, cur->b->clrs[sim->colour %
		cur->b->clrsz].rgba, sizeof(solid));
	memcpy(trans, solid, sizeof(trans));
	trans[3] = 0.4;

	/* Append to the per-window simulation views. */
	kdata_vector_append(cur->winmean,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winstddev,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winfitmean,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winfitstddev,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winmextinctmean,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winmextinctstddev,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winiextinctmean,
		g_list_length(cur->sims), 0.0);
	kdata_vector_append(cur->winiextinctstddev,
		g_list_length(cur->sims), 0.0);

	/* Append our configuration. */
	outbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);

	g_snprintf(label, sizeof(label),
		""
		"    "
		"",
		(unsigned int)(solid[0] * 255),
		(unsigned int)(solid[1] * 255),
		(unsigned int)(solid[2] * 255));
	leftbox = gtk_label_new(NULL);
	gtk_misc_set_alignment(GTK_MISC(leftbox), 0.0, 0.0);
	gtk_label_set_markup(GTK_LABEL(leftbox), label);
	gtk_container_add(GTK_CONTAINER(outbox), leftbox);
	box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
	gtk_container_add(GTK_CONTAINER(outbox), box);
	window_add_config(box, "Name: %s", sim->name);
	window_add_configmarkup(box, "Payoffs: π = %s; "
		"T = %zu", sim->func, sim->stop);
	window_add_configmarkup(box, "Poisson offspring: "
		"λ = %g(1 + %g * π)",
		sim->alpha, sim->delta);
	window_add_config(box, "Incumbents: "
		"x = [%g, %g), %zu slices",
		sim->xmin, sim->xmax, sim->dims);
	if (MUTANTS_DISCRETE == sim->mutants)
		window_add_config(box, "Mutants: y = [%g, %g), "
			"%zu slices", sim->ymin, sim->ymax, sim->dims);
	else
		window_add_configmarkup(box, "Mutants: "
			"Gaussian σ = %g, Y = [%g, %g)",
			sim->mutantsigma, sim->ymin, sim->ymax);

	switch (sim->input) {
	case (INPUT_UNIFORM):
		window_add_config(box, "Population: uniform %zu "
			"islands, %zu islanders (%zu total), m = %g",
			sim->pop, sim->islands, sim->totalpop, sim->m);
		break;
	case (INPUT_VARIABLE):
		window_add_config(box, "Population: variable %zu "
			"islands (%zu total, %suniform), m = %g",
			sim->islands, sim->totalpop,
			NULL != sim->ms ? "non-" : "", sim->m);
		if (sim->ideathmean)
			window_add_config(box,
				"Island death Poisson mean: %zu, "
				"coefficient %g",
				sim->ideathmean, sim->ideathcoef);
		break;
	case (INPUT_MAPPED):
		window_add_config(box, "Population: mapped %zu "
			"islands (%zu total, %suniform), m = %g",
			sim->islands, sim->totalpop,
			NULL != sim->ms ? "non-" : "", sim->m);
		switch (sim->maptop) {
		case (MAPTOP_RECORD):
			window_add_config(box, "Map topology: "
				"KML records");
			break;
		case (MAPTOP_RAND):
			window_add_config(box, "Map topology: "
				"random records");
			break;
		case (MAPTOP_TORUS):
			window_add_config(box, "Map topology: "
				"toroidal records");
			break;
		default:
			abort();
		}
		switch (sim->migrant) {
		case (MAPMIGRANT_UNIFORM):
			window_add_config(box, "Map migration: "
				"uniform random");
			break;
		case (MAPMIGRANT_DISTANCE):
			window_add_config(box, "Map migration: "
				"inverse square distance");
			break;
		case (MAPMIGRANT_NEAREST):
			window_add_config(box, "Map migration: "
				"first nearest neighbour");
			break;
		case (MAPMIGRANT_TWONEAREST):
			window_add_config(box, "Map migration: "
				"two-nearest neighbours");
			break;
		default:
			abort();
		}
		break;
	default:
		abort();
	}

	if (0 == sim->fitpoly)
		window_add_config(box, "Polynomial fitting: disabled");
	else
		window_add_config(box, "Polynomial fitting: order "
			"%zu (%sweighted)", sim->fitpoly,
			sim->weighted ?  "" : "un");

	gtk_container_add(GTK_CONTAINER(cur->wins.boxconfig), outbox);
	gtk_widget_show_all(GTK_WIDGET(cur->wins.boxconfig));

	/* Configure our line and point style. */
	ksmthcfg_defaults(&smth);
	smth.movsamples = sim->smoothing;
	kdatacfg_defaults(&solidcfg);
	solidcfg.point.clr.type = KPLOTCTYPE_RGBA;
	memcpy(solidcfg.point.clr.rgba, solid, sizeof(solid));
	solidcfg.line.clr.type = KPLOTCTYPE_RGBA;
	memcpy(solidcfg.line.clr.rgba, solid, sizeof(solid));

	kdatacfg_defaults(&transcfg);
	transcfg.point.clr.type = KPLOTCTYPE_RGBA;
	memcpy(transcfg.point.clr.rgba, trans, sizeof(trans));
	transcfg.line.clr.type = KPLOTCTYPE_RGBA;
	memcpy(transcfg.line.clr.rgba, trans, sizeof(trans));

	/* Mean view. */
	kplot_attach_data(cur->views[VIEW_MEAN],
		sim->bufs.means->cold, KPLOT_LINES, &solidcfg);

	/* Mutant mean view. */
	kplot_attach_data(cur->views[VIEW_EXTM],
		sim->bufs.mextinct->cold, KPLOT_LINES, &solidcfg);

	/* Incumbent mean view. */
	kplot_attach_data(cur->views[VIEW_EXTI],
		sim->bufs.iextinct->cold, KPLOT_LINES, &solidcfg);

	/* Mean and poly-fitted line. */
	kplot_attach_data(cur->views[VIEW_POLY],
		sim->bufs.fitpolybuf, KPLOT_LINES, &solidcfg);
	kplot_attach_data(cur->views[VIEW_POLY],
		sim->bufs.means->cold, KPLOT_LINES, &transcfg);

	/* Mutant mean and smoothed line. */
	kplot_attach_smooth(cur->views[VIEW_SEXTM],
		sim->bufs.mextinct->cold, KPLOT_LINES, &solidcfg,
		KSMOOTH_MOVAVG, &smth);
	kplot_attach_data(cur->views[VIEW_SEXTM],
		sim->bufs.mextinct->cold, KPLOT_LINES, &transcfg);

	/* Mutant mean and smoothed line. */
	kplot_attach_smooth(cur->views[VIEW_SEXTI],
		sim->bufs.iextinct->cold, KPLOT_LINES, &solidcfg,
		KSMOOTH_MOVAVG, &smth);
	kplot_attach_data(cur->views[VIEW_SEXTI],
		sim->bufs.iextinct->cold, KPLOT_LINES, &transcfg);

	/* Mean and smoothed lines. */
	kplot_attach_smooth(cur->views[VIEW_SMEAN],
		sim->bufs.means->cold, KPLOT_LINES, &solidcfg,
		KSMOOTH_MOVAVG, &smth);
	kplot_attach_data(cur->views[VIEW_SMEAN],
		sim->bufs.means->cold, KPLOT_LINES, &transcfg);

	/* Mean and stddev.  */
	ts[0] = ts[1] = KPLOT_LINES;
	stats[0] = sim->bufs.means->cold;
	stats[1] = sim->bufs.stddevs->cold;
	cfgs[0] = &solidcfg;
	cfgs[1] = &transcfg;
	kplot_attach_datas(cur->views[VIEW_DEV], 2, stats, ts,
		(const struct kdatacfg *const *)cfgs,
		KPLOTS_YERRORLINE);

	/* Island mean and stddev. */
	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = sim->bufs.islandmeans->cold;
	stats[1] = sim->bufs.islandstddevs->cold;
	cfgs[0] = &solidcfg;
	cfgs[1] = &transcfg;
	kplot_attach_datas(cur->views[VIEW_ISLANDERMEAN], 2, stats, ts,
		(const struct kdatacfg *const *)cfgs,
		KPLOTS_YERRORBAR);

	/* Island mean and stddev. */
	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = sim->bufs.imeans->cold;
	stats[1] = sim->bufs.istddevs->cold;
	cfgs[0] = &solidcfg;
	cfgs[1] = &transcfg;
	kplot_attach_datas(cur->views[VIEW_ISLANDMEAN], 2, stats, ts,
		(const struct kdatacfg *const *)cfgs,
		KPLOTS_YERRORBAR);

	/* Time PMF views. */
	kplot_attach_smooth(cur->views[VIEW_TIMESPDF],
		sim->bufs.times->cold, KPLOT_LINES, &solidcfg,
		KSMOOTH_PMF, NULL);
	kplot_attach_smooth(cur->views[VIEW_TIMESCDF],
		sim->bufs.times->cold, KPLOT_LINES, &solidcfg,
		KSMOOTH_CDF, NULL);

	/* Mean PMF views. */
	kplot_attach_smooth(cur->views[VIEW_MEANMINPDF],
		sim->bufs.meanmins, KPLOT_LINES, &solidcfg,
		KSMOOTH_PMF, NULL);
	kplot_attach_smooth(cur->views[VIEW_MEANMINCDF],
		sim->bufs.meanmins, KPLOT_LINES, &solidcfg,
		KSMOOTH_CDF, NULL);

	/* Mutant PMF views. */
	kplot_attach_smooth(cur->views[VIEW_EXTMMAXPDF],
		sim->bufs.mextinctmaxs, KPLOT_LINES, &solidcfg,
		KSMOOTH_PMF, NULL);
	kplot_attach_smooth(cur->views[VIEW_EXTMMAXCDF],
		sim->bufs.mextinctmaxs, KPLOT_LINES, &solidcfg,
		KSMOOTH_CDF, NULL);

	/* Incumbent PMF views. */
	kplot_attach_smooth(cur->views[VIEW_EXTIMINPDF],
		sim->bufs.iextinctmins, KPLOT_LINES, &solidcfg,
		KSMOOTH_PMF, NULL);
	kplot_attach_smooth(cur->views[VIEW_EXTIMINCDF],
		sim->bufs.iextinctmins, KPLOT_LINES, &solidcfg,
		KSMOOTH_CDF, NULL);

	/* Fit poly PMF views. */
	kplot_attach_smooth(cur->views[VIEW_POLYMINPDF],
		sim->bufs.fitpolymins, KPLOT_LINES, &solidcfg,
		KSMOOTH_PMF, NULL);
	kplot_attach_smooth(cur->views[VIEW_POLYMINCDF],
		sim->bufs.fitpolymins, KPLOT_LINES, &solidcfg,
		KSMOOTH_CDF, NULL);

	kplot_attach_data(cur->views[VIEW_MEANMINQ],
		sim->bufs.meanminqbuf, KPLOT_LINES, &solidcfg);

	kplot_attach_data(cur->views[VIEW_POLYMINQ],
		sim->bufs.fitminqbuf, KPLOT_LINES, &solidcfg);
}

/*
 * Indicate that a given simulation is now being referenced by a new
 * window.
 */
static void
sim_ref(gpointer dat, gpointer unused)
{
	struct sim	*sim = dat;

	++sim->refs;
	g_debug("%p: Simulation ref (now %zu)", sim, sim->refs);
}

/*
 * Transfer the other widget's data into our own.
 */
void
on_drag_recv(GtkWidget *widget, GdkDragContext *ctx,
	gint x, gint y, GtkSelectionData *sel,
	guint target, guint time, gpointer dat)
{
	GObject		*srcptr, *dstptr;
	struct curwin	*cur;
	GList		*srcsims, *l, *ll;

	/* Get pointers to our and the other's window. */
	assert(NULL != sel);
	dstptr = G_OBJECT(gtk_widget_get_toplevel(widget));
	srcptr = G_OBJECT(*(void **)
		gtk_selection_data_get_data(sel));
	assert(NULL != srcptr);
	gtk_drag_finish(ctx, TRUE, FALSE, time);

	/* Don't copy into ourselves. */
	if (dstptr == srcptr)
		return;

	/* Get the simulation lists. */
	cur = g_object_get_data(srcptr, "cfg");
	srcsims = cur->sims;
	assert(NULL != srcsims);
	cur = g_object_get_data(dstptr, "cfg");

	/* Concatenate the simulation lists. */
	/* XXX: use g_list_concat? */
	for (l = srcsims; NULL != l; l = l->next) {
		g_debug("%p: Copying simulation", l->data);
		for (ll = cur->sims; NULL != ll; ll = ll->next)
			if (ll->data == l->data)
				break;

		if (NULL != ll) {
			g_debug("Simulation %p duplicate", l->data);
			continue;
		}

		sim_ref(l->data, NULL);
		cur->sims = g_list_append(cur->sims, l->data);
		window_add_sim(cur, l->data);
	}
}

/*
 * Signifity sight-unseen that we can transfer data.
 * We're only using DnD for one particular thing, so there's no need for
 * elaborate security measures.
 */
gboolean
on_drag_drop(GtkWidget *widget, GdkDragContext *ctx,
	gint x, gint y, guint time, gpointer dat)
{

	gtk_drag_get_data(widget, ctx, 0, time);
	return(TRUE);
}

/*
 * Send our identifier to the destination of the DnD.
 * They'll query our data separately.
 */
void
on_drag_get(GtkWidget *widget, GdkDragContext *ctx,
	GtkSelectionData *sel, guint targ, guint time, gpointer dat)
{
	void	*ptr;

	ptr = gtk_widget_get_toplevel(widget);
	gtk_selection_data_set(sel,
		gtk_selection_data_get_target(sel),
		sizeof(intptr_t) * 8,
		(const guchar *)&ptr, sizeof(intptr_t));
}

/*
 * Initialise a simulation (or simulations) window.
 * This is either called when we've just made a new simulation
 */
static void
window_init(struct bmigrate *b, struct curwin *cur, GList *sims)
{
	GtkBuilder	*builder;
	GtkTargetEntry   target;
	GList		*l;
	struct kdata	*stats[2];
	enum kplottype	 ts[2];
	size_t		 i;
	struct kplotcfg	 cfg;

	builder = builder_get("simwin.glade");
	g_assert(NULL != builder);
	swin_init(&cur->wins, cur->view, builder);
	gtk_builder_connect_signals(builder, cur);
	g_object_unref(G_OBJECT(builder));

	cur->winmean = kdata_vector_alloc(1);
	cur->winstddev = kdata_vector_alloc(1);
	cur->winfitmean = kdata_vector_alloc(1);
	cur->winfitstddev = kdata_vector_alloc(1);
	cur->winmextinctmean = kdata_vector_alloc(1);
	cur->winmextinctstddev = kdata_vector_alloc(1);
	cur->winiextinctmean = kdata_vector_alloc(1);
	cur->winiextinctstddev = kdata_vector_alloc(1);

	for (i = 0; i < VIEW__MAX; i++) {
		kplotcfg_defaults(&cfg);
		switch (i) {
		case (VIEW_DEV):
		case (VIEW_POLY):
		case (VIEW_EXTIMINS):
		case (VIEW_EXTMMAXS):
		case (VIEW_MEANMINS):
		case (VIEW_POLYMINS):
		case (VIEW_ISLANDMEAN):
		case (VIEW_ISLANDERMEAN):
			cfg.extrema_ymin = 0.0;
			cfg.extrema = EXTREMA_YMIN;
			break;
		default:
			break;
		}

		switch (i) {
		case (VIEW_DEV):
		case (VIEW_EXTI):
		case (VIEW_EXTIMINCDF):
		case (VIEW_EXTIMINPDF):
		case (VIEW_EXTM):
		case (VIEW_EXTMMAXCDF):
		case (VIEW_EXTMMAXPDF):
		case (VIEW_MEAN):
		case (VIEW_MEANMINCDF):
		case (VIEW_MEANMINPDF):
		case (VIEW_POLY):
		case (VIEW_POLYMINCDF):
		case (VIEW_POLYMINPDF):
		case (VIEW_SEXTM):
		case (VIEW_SEXTI):
		case (VIEW_SMEAN):
			cfg.xaxislabel = "incumbent";
			break;
		case (VIEW_ISLANDMEAN):
		case (VIEW_ISLANDERMEAN):
			cfg.xaxislabel = "island";
			break;
		case (VIEW_TIMESCDF):
		case (VIEW_TIMESPDF):
			cfg.xaxislabel = "exit time";
			break;
		case (VIEW_EXTIMINS):
		case (VIEW_EXTMMAXS):
		case (VIEW_MEANMINS):
		case (VIEW_POLYMINS):
			cfg.xaxislabel = "simulation";
			break;
		case (VIEW_MEANMINQ):
		case (VIEW_POLYMINQ):
			cfg.xaxislabel = "relative time";
			break;
		default:
			abort();
		}

		cfg.yaxislabelrot = M_PI / 2;

		switch (i) {
		case (VIEW_POLY):
		case (VIEW_DEV):
		case (VIEW_MEAN):
		case (VIEW_SMEAN):
			cfg.y2axislabel = "mutant fraction";
			break;
		case (VIEW_SEXTI):
		case (VIEW_EXTI):
			cfg.y2axislabel = "incumbent extinction";
			break;
		case (VIEW_SEXTM):
		case (VIEW_EXTM):
			cfg.y2axislabel = "mutant extinction";
			break;
		case (VIEW_EXTIMINCDF):
		case (VIEW_EXTIMINPDF):
		case (VIEW_EXTMMAXCDF):
		case (VIEW_EXTMMAXPDF):
		case (VIEW_MEANMINCDF):
		case (VIEW_MEANMINPDF):
		case (VIEW_POLYMINCDF):
		case (VIEW_POLYMINPDF):
		case (VIEW_TIMESCDF):
		case (VIEW_TIMESPDF):
			break;
		case (VIEW_ISLANDMEAN):
		case (VIEW_ISLANDERMEAN):
			cfg.y2axislabel = "mutant fraction mean";
			break;
		case (VIEW_EXTMMAXS):
		case (VIEW_EXTIMINS):
		case (VIEW_MEANMINS):
		case (VIEW_POLYMINS):
		case (VIEW_MEANMINQ):
		case (VIEW_POLYMINQ):
			cfg.y2axislabel = "incumbent";
			break;
		default:
			abort();
		}
		cur->views[i] = kplot_alloc(&cfg);
		g_assert(NULL != cur->views[i]);
	}

	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = cur->winmean;
	stats[1] = cur->winstddev;
	kplot_attach_datas(cur->views[VIEW_MEANMINS], 2,
		stats, ts, NULL, KPLOTS_YERRORBAR);

	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = cur->winfitmean;
	stats[1] = cur->winfitstddev;
	kplot_attach_datas(cur->views[VIEW_POLYMINS], 2,
		stats, ts, NULL, KPLOTS_YERRORBAR);

	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = cur->winmextinctmean;
	stats[1] = cur->winmextinctstddev;
	kplot_attach_datas(cur->views[VIEW_EXTMMAXS], 2,
		stats, ts, NULL, KPLOTS_YERRORBAR);

	ts[0] = ts[1] = KPLOT_POINTS;
	stats[0] = cur->winiextinctmean;
	stats[1] = cur->winiextinctstddev;
	kplot_attach_datas(cur->views[VIEW_EXTIMINS], 2,
		stats, ts, NULL, KPLOTS_YERRORBAR);

	cur->redraw = 1;
	cur->sims = sims;
	cur->b = b;

	g_object_set_data_full(G_OBJECT
		(cur->wins.window), "cfg", cur, curwin_free);
	b->windows = g_list_append(b->windows, cur);

	/*
	 * Coordinate drag-and-drop.
	 * We only allow drag-and-drop between us and other windows of
	 * this same simulation.
	 */
	target.target = g_strdup("integer");
	target.flags = GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET;
	target.info = 0;

	gtk_drag_dest_set(GTK_WIDGET(cur->wins.draw),
		GTK_DEST_DEFAULT_ALL, &target, 1, GDK_ACTION_COPY);
	gtk_drag_source_set(GTK_WIDGET(cur->wins.draw),
		GDK_BUTTON1_MASK, &target, 1, GDK_ACTION_COPY);
	g_free(target.target);

	for (l = cur->sims; NULL != l; l = g_list_next(l))
		window_add_sim(cur, l->data);
}

/*
 * Clone the current window.
 * We create a new window, intialised in the same way as for a new
 * single simulation, but give it an existing list of simulations.
 */
void
onclone(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*oldcur = dat, *newcur;
	GList		*oldsims, *newsims;

	oldsims = oldcur->sims;
	g_list_foreach(oldsims, sim_ref, NULL);
	newsims = g_list_copy(oldsims);
	newcur = g_malloc0(sizeof(struct curwin));
	newcur->view = oldcur->view;
	window_init(oldcur->b, newcur, newsims);
}

/*
 * Brute-force scan all possible pi values (and Poisson means) by
 * scanning through the strategy space.
 */
static gboolean
on_rangefind_idle(gpointer dat)
{

	return(rangefind(dat));
}

static int
entry2func(GtkEntry *entry, struct hnode ***exp, GtkLabel *error)
{
	const gchar	*txt;
	GdkRGBA	 	 bad = { 1.0, 0.0, 0.0, 0.5 };

	txt = gtk_entry_get_text(entry);
	*exp = hnode_parse((const gchar **)&txt);
	if (NULL != *exp) {
		gtk_widget_override_background_color
			(GTK_WIDGET(entry),
			 GTK_STATE_FLAG_NORMAL, NULL);
		return(1);
	}

	gtk_label_set_text(error, "Error: not a function.");
	gtk_widget_show_all(GTK_WIDGET(error));
	gtk_widget_override_background_color
		(GTK_WIDGET(entry), GTK_STATE_FLAG_NORMAL, &bad);
	return(0);
}

static int
entryworder(GtkEntry *mine, GtkEntry *maxe,
	double min, double max, GtkLabel *error)
{
	GdkRGBA	 bad = { 1.0, 0.0, 0.0, 0.5 };

	if (min < max) {
		gtk_widget_override_background_color
			(GTK_WIDGET(mine),
			 GTK_STATE_FLAG_NORMAL, NULL);
		gtk_widget_override_background_color
			(GTK_WIDGET(maxe),
			 GTK_STATE_FLAG_NORMAL, NULL);
		return(1);
	}

	gtk_label_set_text(error, "Error: bad weak ordering.");
	gtk_widget_show_all(GTK_WIDGET(error));
	gtk_widget_override_background_color
		(GTK_WIDGET(mine), GTK_STATE_FLAG_NORMAL, &bad);
	gtk_widget_override_background_color
		(GTK_WIDGET(maxe), GTK_STATE_FLAG_NORMAL, &bad);
	return(0);
}

static int
entryorder(GtkEntry *mine, GtkEntry *maxe,
	double min, double max, GtkLabel *error)
{
	GdkRGBA	 bad = { 1.0, 0.0, 0.0, 0.5 };

	if (min <= max) {
		gtk_widget_override_background_color
			(GTK_WIDGET(mine),
			 GTK_STATE_FLAG_NORMAL, NULL);
		gtk_widget_override_background_color
			(GTK_WIDGET(maxe),
			 GTK_STATE_FLAG_NORMAL, NULL);
		return(1);
	}

	gtk_label_set_text(error, "Error: bad ordering.");
	gtk_widget_show_all(GTK_WIDGET(error));
	gtk_widget_override_background_color
		(GTK_WIDGET(mine), GTK_STATE_FLAG_NORMAL, &bad);
	gtk_widget_override_background_color
		(GTK_WIDGET(maxe), GTK_STATE_FLAG_NORMAL, &bad);
	return(0);
}

static int
entry2double(GtkEntry *entry, gdouble *sz, GtkLabel *error)
{
	gchar	*ep;
	GdkRGBA	 bad = { 1.0, 0.0, 0.0, 0.5 };

	*sz = g_ascii_strtod(gtk_entry_get_text(entry), &ep);

	if (ERANGE != errno && '\0' == *ep) {
		gtk_widget_override_background_color
			(GTK_WIDGET(entry),
			 GTK_STATE_FLAG_NORMAL, NULL);
		return(1);
	}

	gtk_label_set_text(error, "Error: not a decimal number.");
	gtk_widget_show_all(GTK_WIDGET(error));
	gtk_widget_override_background_color
		(GTK_WIDGET(entry), GTK_STATE_FLAG_NORMAL, &bad);
	return(0);
}

static int
entry2size(GtkEntry *entry, size_t *sz, GtkLabel *error, size_t min)
{
	guint64	 v;
	gchar	*ep;
	GdkRGBA	 bad = { 1.0, 0.0, 0.0, 0.5 };

	v = g_ascii_strtoull(gtk_entry_get_text(entry), &ep, 10);
	if (ERANGE == errno || '\0' != *ep || v >= SIZE_MAX) {
		gtk_label_set_text
			(error, "Error: not a natural number.");
		gtk_widget_show_all(GTK_WIDGET(error));
		gtk_widget_override_background_color
			(GTK_WIDGET(entry),
			 GTK_STATE_FLAG_NORMAL, &bad);
		return(0);
	} else if (v < min) {
		gtk_label_set_text
			(error, "Error: number too small.");
		gtk_widget_show_all(GTK_WIDGET(error));
		gtk_widget_override_background_color
			(GTK_WIDGET(entry),
			 GTK_STATE_FLAG_NORMAL, &bad);
		return(0);
	}

	*sz = (size_t)v;
	gtk_widget_override_background_color
		(GTK_WIDGET(entry),
		 GTK_STATE_FLAG_NORMAL, NULL);
	return(1);
}

/*
 * We have various ways of auto-setting the name of the simulation.
 * By default, it's set to the current date-time.
 */
static void
donamefill(struct hwin *c)
{
	enum namefill	 v;
	GTimeVal	 gt;
	enum input	 input;
	gchar		 buf[1024];
	gchar		*bufp;
	enum mutants	 mutants;

	input = gtk_notebook_get_current_page(c->inputs);
	g_assert(input < INPUT__MAX);

	for (mutants = 0; mutants < MUTANTS__MAX; mutants++)
		if (gtk_toggle_button_get_active
			(GTK_TOGGLE_BUTTON(c->mutants[mutants])))
			break;

	for (v = 0; v < NAMEFILL__MAX; v++)
		if (gtk_toggle_button_get_active(c->namefill[v]))
			break;

	bufp = buf;
	switch (v) {
	case (NAMEFILL_M):
		g_snprintf(buf, sizeof(buf), "m=%s",
			gtk_entry_get_text(c->migrate[input]));
		break;
	case (NAMEFILL_T):
		g_snprintf(buf, sizeof(buf), "T=%s",
			gtk_entry_get_text(c->stop));
		break;
	case (NAMEFILL_MUTANTS):
		if (MUTANTS_DISCRETE == mutants) {
			g_snprintf(buf, sizeof(buf),
				"discrete [%s,%s)",
				gtk_entry_get_text(c->xmin),
				gtk_entry_get_text(c->xmax));
			break;
		}
		g_snprintf(buf, sizeof(buf),
			"Gaussian s=%s, [%s,%s)",
			gtk_entry_get_text(c->mutantsigma),
			gtk_entry_get_text(c->ymin),
			gtk_entry_get_text(c->ymax));
		break;
	case (NAMEFILL_DATE):
		g_get_current_time(&gt);
		bufp = g_time_val_to_iso8601(&gt);
		break;
	case (NAMEFILL_NONE):
		return;
	default:
		abort();
	}

	gtk_entry_set_text(c->name, bufp);
	if (bufp != buf)
		g_free(bufp);
}

void
onnametoggle(GtkToggleButton *editable, gpointer dat)
{
	struct bmigrate	*b = dat;

	donamefill(&b->wins);
}

void
onnameupdate(GtkEditable *editable, gpointer dat)
{
	struct bmigrate	*b = dat;

	donamefill(&b->wins);
}

/*
 * We want to run the given simulation.
 * First, verify all data; then, start the simulation; lastly, open a
 * window assigned specifically to that simulation.
 */
void
onactivate(GtkButton *button, gpointer dat)
{
	gint	 	  input;
	struct bmigrate	 *b = dat;
	struct hnode	**exp;
	enum mapmigrant	  migrants;
	GtkWidget	 *w;
	struct kml	 *kml;
	GList		 *l, *cl;
	GtkLabel	 *err = b->wins.error;
	const gchar	 *name, *func;
	gchar	  	 *file;
	gdouble		**ms;
	gdouble		  xmin, xmax, delta, alpha, m, sigma,
			  ymin, ymax, idcoef, strat;
	enum mutants	  mutants;
	size_t		  i, totalpop, islands, stop, ideathmean,
			  slices, islandpop, mapindexfix;
	size_t		 *islandpops;
	struct sim	 *sim;
	struct curwin	 *cur;
	struct kmlplace	 *kmlp;
	GError		 *er;
	enum maptop	  maptop;
	enum mapindex	  mapindex;

	sigma = idcoef = 0.0;
	islandpops = NULL;
	islandpop = 0;
	ms = NULL;
	kml = NULL;
	exp = NULL;

	if ( ! entry2size(b->wins.stop, &stop, err, 1))
		goto cleanup;

	for (migrants = 0; migrants < MAPMIGRANT__MAX; migrants++)
		if (gtk_toggle_button_get_active
			 (b->wins.mapmigrants[migrants]))
			break;
	g_assert(migrants < MAPMIGRANT__MAX);

	for (mapindex = 0; mapindex < MAPINDEX__MAX; mapindex++)
		if (gtk_toggle_button_get_active
			 (b->wins.mapindices[mapindex]))
			break;
	g_assert(mapindex < MAPINDEX__MAX);
	mapindexfix = MAPINDEX_FIXED != mapindex ? 0 :
		gtk_adjustment_get_value(b->wins.mapindexfix);

	for (maptop = 0; maptop < MAPTOP__MAX; maptop++)
		if (gtk_toggle_button_get_active
			 (b->wins.maptop[maptop]))
			break;
	g_assert(maptop < MAPTOP__MAX);

	input = gtk_notebook_get_current_page(b->wins.inputs);
	switch (input) {
	case (INPUT_UNIFORM):
		/*
		 * In the simplest possible case, we use uniform values
		 * for both migration (no inter-island migration) and
		 * populations.
		 */
		islands = gtk_adjustment_get_value(b->wins.islands);
		islandpop = gtk_adjustment_get_value(b->wins.pop);
		break;
	case (INPUT_VARIABLE):
		/*
		 * Variable number of islands.
		 * We also add a check to see if we're really running
		 * with different island sizes or not.
		 */
		if ( ! entry2double(b->wins.ideathcoef, &idcoef, err))
			goto cleanup;
		if (idcoef < 0.0 || idcoef > 1.0) {
			gtk_label_set_text(err, "Death coefficient "
				"must be in the unit interval.");
			gtk_widget_show_all(GTK_WIDGET(err));
			goto cleanup;
		}
		l = gtk_container_get_children
			(GTK_CONTAINER(b->wins.mapbox));
		islands = g_list_length(l);
		islandpops = g_malloc0_n(islands, sizeof(size_t));
		for (i = 0; i < islands; i++) {
			w = GTK_WIDGET(g_list_nth_data(l, i));
			cl = gtk_container_get_children(GTK_CONTAINER(w));
			islandpops[i] = gtk_spin_button_get_value_as_int
				(GTK_SPIN_BUTTON(g_list_next(cl)->data));
			g_list_free(cl);
		}
		g_list_free(l);
		break;
	case (INPUT_MAPPED):
		/*
		 * Possibly variable island sizes, possibly variable
		 * inter-island migration.
		 */
		switch (maptop) {
		case (MAPTOP_RECORD):
			/* Try to read KML from file. */
			file = gtk_file_chooser_get_filename
				(b->wins.mapfile);
			if (NULL == file) {
				gtk_label_set_text(err, "Error: "
					"map file not specified.");
				gtk_widget_show_all(GTK_WIDGET(err));
				goto cleanup;
			}
			er = NULL;
			kml = kml_parse(file, &er);
			g_free(file);
			if (NULL == kml) {
				file = g_strdup_printf("Error: "
					"bad map file: %s",
					NULL != er ? er->message :
					"cannot load file");
				gtk_label_set_text(err, file);
				gtk_widget_show_all(GTK_WIDGET(err));
				g_free(file);
				if (NULL != er)
					g_error_free(er);
				goto cleanup;
			}
			break;
		case (MAPTOP_RAND):
			islands = gtk_adjustment_get_value
				(b->wins.maprandislands);
			islandpop = gtk_adjustment_get_value
				(b->wins.maprandislanders);
			kml = kml_rand(islands, islandpop);
			islandpop = islands = 0;
			break;
		case (MAPTOP_TORUS):
			islands = gtk_adjustment_get_value
				(b->wins.maptorusislands);
			islandpop = gtk_adjustment_get_value
				(b->wins.maptorusislanders);
			kml = kml_torus(islands, islandpop);
			islandpop = islands = 0;
			break;
		default:
			abort();
		}

		/*
		 * Grok our island populations from the input file.
		 * This will have a reasonable default, but make sure
		 * anyway with some assertions.
		 */
		islands = (size_t)g_list_length(kml->kmls);
		islandpops = g_malloc0_n(islands, sizeof(size_t));
		for (i = 0; i < islands; i++) {
			kmlp = g_list_nth_data(kml->kmls, i);
			islandpops[i] = kmlp->pop;
		}

		/*
		 * If we're uniformly migrating, then stop processing
		 * right now.
		 * If we're distance-migrating, then have the kml file
		 * set the inter-island migration probabilities.
		 */
		switch (migrants) {
		case (MAPMIGRANT_UNIFORM):
			ms = kml_migration_distance(kml->kmls, maptop);
			break;
		case (MAPMIGRANT_NEAREST):
			ms = kml_migration_nearest(kml->kmls, maptop);
			break;
		case (MAPMIGRANT_TWONEAREST):
			ms = kml_migration_twonearest(kml->kmls, maptop);
			break;
		default:
			break;
		}
		break;
	default:
		abort();
	}

	/*
	 * Base check: we need at least two islands.
	 */
	if (islands < 2) {
		gtk_label_set_text(err,
			"Error: need at least two islands.");
		gtk_widget_show_all(GTK_WIDGET(err));
		goto cleanup;
	}

	ideathmean = gtk_adjustment_get_value(b->wins.ideathmean);

	/*
	 * If we have an array of island populations, make sure that
	 * each has more than two islanders.
	 * While here, revert to a single island population size if our
	 * sizes are, in fact, uniform.
	 * Calculate our total population size as well.
	 */
	if (NULL != islandpops) {
		g_assert(0 == islandpop);
		for (i = 0; i < islands; i++) {
			if (islandpops[i] > 1)
				continue;
			gtk_label_set_text(err, "Error: need at "
				"least two islanders per island.");
			gtk_widget_show_all(GTK_WIDGET(err));
			goto cleanup;
		}
		for (totalpop = i = 0; i < islands; i++)
			totalpop += islandpops[i];

		/* Check for uniformity. */
		for (i = 1; i < islands; i++)
			if (islandpops[i] != islandpops[0])
				break;
		if (i == islands && 0 == ideathmean) {
			g_debug("Reverting to uniform island "
				"populations: all islands have "
				"the same (with no death process): "
				"%zu", islandpops[0]);
			islandpop = islandpops[0];
			g_free(islandpops);
			islandpops = NULL;
		}
	}

	/*
	 * Handle uniform island sizes.
	 * (This isn't part of the conditional above because we might
	 * set ourselves to be uniform whilst processing.)
	 */
	if (NULL == islandpops && islandpop < 2) {
		gtk_label_set_text(err, "Error: need at "
			"least two islanders per island.");
		gtk_widget_show_all(GTK_WIDGET(err));
		goto cleanup;
	} else if (NULL == islandpops)
		totalpop = islands * islandpop;

	for (mutants = 0; mutants < MUTANTS__MAX; mutants++)
		if (gtk_toggle_button_get_active
			(GTK_TOGGLE_BUTTON(b->wins.mutants[mutants])))
			break;

	if ( ! entry2double(b->wins.xmin, &xmin, err))
		goto cleanup;
	if ( ! entry2double(b->wins.xmax, &xmax, err))
		goto cleanup;
	if ( ! entryworder(b->wins.xmin, b->wins.xmax, xmin, xmax, err))
		goto cleanup;
	ymin = xmin;
	ymax = xmax;
	if (MUTANTS_GAUSSIAN == mutants) {
		if ( ! entry2double(b->wins.ymin, &ymin, err))
			goto cleanup;
		if ( ! entry2double(b->wins.ymax, &ymax, err))
			goto cleanup;
		if ( ! entryorder(b->wins.ymin,
				b->wins.xmin, ymin, xmin, err))
			goto cleanup;
		if ( ! entryorder(b->wins.xmax,
				b->wins.ymax, xmax, ymax, err))
			goto cleanup;
		if ( ! entryworder(b->wins.ymin,
				b->wins.ymax, ymin, ymax, err))
			goto cleanup;
		if ( ! entry2double(b->wins.mutantsigma, &sigma, err))
			goto cleanup;
	}
	if ( ! entry2double(b->wins.alpha, &alpha, err))
		goto cleanup;
	if ( ! entry2double(b->wins.delta, &delta, err))
		goto cleanup;
	if ( ! entry2double(b->wins.migrate[input], &m, err))
		goto cleanup;
	if ( ! entry2size(b->wins.incumbents, &slices, err, 1))
		goto cleanup;
	if ( ! entry2func(b->wins.func, &exp, err))
		goto cleanup;

	func = gtk_entry_get_text(b->wins.func);
	name = gtk_entry_get_text(b->wins.name);

	if ('\0' == *name)
		name = "unnamed";

	gtk_widget_hide(GTK_WIDGET(err));

	/*
	 * All parameters check out!
	 * Allocate the simulation now.
	 */
	if (button == b->wins.buttonrange) {
		if (0 == b->rangeid) {
			g_debug("Starting rangefinder");
			b->rangeid = g_idle_add
				((GSourceFunc)on_rangefind_idle, b);
		} else
			g_debug("Re-using rangefinder");

		hnode_free(b->range.exp);
		if (NULL != islandpops) {
			b->range.n = 0;
			for (i = 0; i < islands; i++)
				b->range.n = islandpops[i] > b->range.n ?
					islandpops[i] : b->range.n;
		} else
			b->range.n = islandpop;

		b->range.exp = exp;
		b->range.alpha = alpha;
		b->range.delta = delta;
		b->range.slices = slices;
		b->range.slicex = b->range.slicey = 0;
		b->range.piaggr = 0.0;
		b->range.picount = 0;
		b->range.pimin = DBL_MAX;
		b->range.pimax = -DBL_MAX;
		b->range.xmin = b->range.ymin = xmin;
		b->range.xmax = b->range.ymax = xmax;
		if (MUTANTS_GAUSSIAN == mutants) {
			b->range.ymin = ymin;
			b->range.ymax = ymax;
		}
		exp = NULL;
		file = g_strdup_printf
			("%s, X=[%g, %g), Y=[%g, %g), n=%zu",
			func, b->range.xmin, b->range.xmax,
			b->range.ymin, b->range.ymax,
			b->range.n);
		gtk_label_set_text(b->wins.rangefunc, file);
		g_free(file);

		file = g_strdup_printf
			("%g(1 + %gπ)",
			 b->range.alpha, b->range.delta);
		gtk_label_set_markup(b->wins.rangeparms, file);
		g_free(file);

		gtk_widget_hide(GTK_WIDGET(b->wins.rangeerrorbox));
		gtk_widget_set_visible
			(GTK_WIDGET(b->wins.rangefind), TRUE);
		goto cleanup;
	}

	sim = g_malloc0(sizeof(struct sim));
	sim->dims = slices;
	sim->islands = islands;
	sim->mutants = mutants;
	sim->mutantsigma = sigma;
	sim->func = g_strdup(func);
	sim->name = g_strdup(name);
	sim->fitpoly = gtk_adjustment_get_value(b->wins.fitpoly);
	sim->weighted = gtk_toggle_button_get_active(b->wins.weighted);
	sim->smoothing = gtk_adjustment_get_value(b->wins.smoothing);
	sim->ideathmean = ideathmean;
	sim->ideathcoef = idcoef;
	sim->kml = kml;
	sim->migrant = migrants;
	sim->maptop = maptop;
	sim->mapindex = mapindex;
	if ((sim->mapindexfix = mapindexfix) > sim->islands) {
		g_debug("Clamping map index to %zu", sim->islands - 1);
		sim->mapindexfix = sim->islands - 1;
	}
	g_mutex_init(&sim->hot.mux);
	g_cond_init(&sim->hot.cond);

	/* Source for the fraction of mutants. */
	sim->bufs.fractions = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.fractions);
	sim->bufs.ifractions = kdata_array_alloc(NULL, islands);
	g_assert(NULL != sim->bufs.ifractions);
	sim->bufs.mutants = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.mutants);
	sim->bufs.incumbents = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.incumbents);
	sim->bufs.islands = kdata_array_alloc(NULL, islands);
	g_assert(NULL != sim->bufs.islands);
	sim->bufs.meanmins = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.meanmins);
	sim->bufs.mextinctmaxs = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.mextinctmaxs);
	sim->bufs.iextinctmins = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.iextinctmins);
	sim->bufs.fitpoly = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.fitpoly);
	sim->bufs.fitpolybuf = kdata_buffer_alloc(slices);
	g_assert(NULL != sim->bufs.fitpolybuf);
	sim->bufs.fitpolymins = kdata_array_alloc(NULL, slices);
	g_assert(NULL != sim->bufs.fitpolymins);
	sim->bufs.meanminqbuf = kdata_array_alloc(NULL, CQUEUESZ);
	g_assert(NULL != sim->bufs.meanminqbuf);
	sim->bufs.fitminqbuf = kdata_array_alloc(NULL, CQUEUESZ);
	g_assert(NULL != sim->bufs.fitminqbuf);

	for (i = 0; i < slices; i++) {
		strat = xmin + (xmax - xmin) * (i / (double)slices);
		kdata_array_set(sim->bufs.fractions, i, strat, 0);
		kdata_array_set(sim->bufs.mutants, i, strat, 0);
		kdata_array_set(sim->bufs.incumbents, i, strat, 0);
		kdata_array_set(sim->bufs.meanmins, i, strat, 0);
		kdata_array_set(sim->bufs.mextinctmaxs, i, strat, 0);
		kdata_array_set(sim->bufs.iextinctmins, i, strat, 0);
		kdata_array_set(sim->bufs.fitpoly, i, strat, 0);
		kdata_array_set(sim->bufs.fitpolymins, i, strat, 0);
	}

	sim->bufs.times = simbuf_alloc
		(kdata_array_alloc(NULL, stop + 1), stop + 1);
	sim->bufs.islandmeans = simbuf_alloc
		(kdata_mean_alloc(sim->bufs.islands), islands);
	sim->bufs.islandstddevs = simbuf_alloc
		(kdata_stddev_alloc(sim->bufs.islands), islands);
	sim->bufs.imeans = simbuf_alloc
		(kdata_mean_alloc(sim->bufs.ifractions), islands);
	sim->bufs.istddevs = simbuf_alloc
		(kdata_stddev_alloc(sim->bufs.ifractions), islands);
	sim->bufs.means = simbuf_alloc
		(kdata_mean_alloc(sim->bufs.fractions), slices);
	sim->bufs.stddevs = simbuf_alloc
		(kdata_stddev_alloc(sim->bufs.fractions), slices);
	sim->bufs.mextinct = simbuf_alloc
		(kdata_mean_alloc(sim->bufs.mutants), slices);
	sim->bufs.iextinct = simbuf_alloc
		(kdata_mean_alloc(sim->bufs.incumbents), slices);

	/*
	 * Conditionally allocate our fitness polynomial structures.
	 * These are per-simulation as they're only run by one thread at
	 * any one time during the simulation.
	 */
	if (sim->fitpoly) {
		sim->work.X = gsl_matrix_alloc
			(sim->dims, sim->fitpoly + 1);
		sim->work.y = gsl_vector_alloc(sim->dims);
		sim->work.w = gsl_vector_alloc(sim->dims);
		sim->work.c = gsl_vector_alloc(sim->fitpoly + 1);
		sim->work.cov = gsl_matrix_alloc
			(sim->fitpoly + 1, sim->fitpoly + 1);
		sim->work.work = gsl_multifit_linear_alloc
			(sim->dims, sim->fitpoly + 1);
		sim->work.coeffs = g_malloc0_n
			(sim->fitpoly + 1, sizeof(double));
	}

	sim->nprocs = gtk_adjustment_get_value(b->wins.nthreads);
	sim->totalpop = totalpop;
	sim->stop = stop;
	sim->alpha = alpha;
	sim->colour = b->nextcolour++;
	sim->delta = delta;
	sim->m = m;
	sim->ms = ms;
	sim->pop = islandpop;
	sim->pops = islandpops;
	sim->input = input;
	sim->exp = exp;
	sim->xmin = xmin;
	sim->xmax = xmax;
	sim->ymin = ymin;
	sim->ymax = ymax;
	b->sims = g_list_append(b->sims, sim);
	sim_ref(sim, NULL);
	sim->threads = g_malloc0_n(sim->nprocs, sizeof(struct simthr));
	g_debug("%p: Simulation created", sim);

	/* Create the simulation threads. */
	for (i = 0; i < sim->nprocs; i++) {
		sim->threads[i].rank = i;
		sim->threads[i].sim = sim;
		sim->threads[i].thread = g_thread_new
			(NULL, simulation, &sim->threads[i]);
	}

	/* Create the simulation window. */
	cur = g_malloc0(sizeof(struct curwin));
	cur->view = VIEW_MEAN;
	window_init(b, cur, g_list_append(NULL, sim));

	/* Initialise the name of our simulation. */
	donamefill(&b->wins);
	return;
cleanup:
	if (NULL != ms)
		for (i = 0; i < islands; i++)
			g_free(ms[i]);
	g_free(islandpops);
	g_free(ms);
	hnode_free(exp);
	kml_free(kml);
}


// Copyright 2014 The Chromium Authors. All rights reserved.
// Inspired by Chromium video capture interface
// Simplified and stripped from internal base code

#ifndef S3D_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_DECKLINK_H
#define S3D_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_DECKLINK_H

#include <s3d/video/capture/video_capture_device.h>
#include <s3d/video/capture/video_capture_device_factory.h>

#include <gsl/gsl>

#include <chrono>

namespace s3d {

class DecklinkCaptureDelegate;

class VideoCaptureDeviceDecklink : public VideoCaptureDevice {
 public:
  explicit VideoCaptureDeviceDecklink(const VideoCaptureDeviceDescriptor& deviceDescriptor);

  gsl::owner<VideoCaptureDevice*> clone() const override;

  ~VideoCaptureDeviceDecklink() override;

  void AllocateAndStart(const VideoCaptureFormat& format,
                        VideoCaptureDevice::Client* client) override;

  void StopAndDeAllocate() override;

  // called from delegate
  void OnIncomingCapturedData(const VideoCaptureDevice::Client::Images& images,
                              const VideoCaptureFormat& frameFormat,
                              std::chrono::microseconds /*timestamp*/);

  VideoCaptureFormat DefaultFormat() override;

 private:
  VideoCaptureDevice::Client* client_{};
  std::unique_ptr<DecklinkCaptureDelegate> captureDelegate_;
};

}  // namespace s3d

#endif  // S3D_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_DECKLINK_H


//
//  Author
//      luncliff@gmail.com
//
#pragma once
#ifndef _NDCAM_INCLUDE_H_
#define _NDCAM_INCLUDE_H_

#include <gsl/gsl>

#include <android/hardware_buffer.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>

#include <media/NdkImage.h>
#include <media/NdkImageReader.h>

#include <camera/NdkCameraCaptureSession.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraError.h>
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraMetadata.h>
#include <camera/NdkCameraMetadataTags.h>
#include <camera/NdkCaptureRequest.h>

using native_window_ptr =
    std::unique_ptr<ANativeWindow, void (*)(ANativeWindow*)>;
using capture_session_output_container_ptr =
    std::unique_ptr<ACaptureSessionOutputContainer,
                    void (*)(ACaptureSessionOutputContainer*)>;
using capture_session_output_ptr =
    std::unique_ptr<ACaptureSessionOutput, void (*)(ACaptureSessionOutput*)>;
using capture_request_ptr =
    std::unique_ptr<ACaptureRequest, void (*)(ACaptureRequest*)>;

using camera_output_target_ptr =
    std::unique_ptr<ACameraOutputTarget, void (*)(ACameraOutputTarget*)>;

/**
 * Library context. Supports auto releasing and facade for features
 *
 * !!! All NDK type members must be public. There will be no encapsulation !!!
 */
struct camera_group_t final {
    // without external camera, 2 is enough(back + front).
    // But we will use more since there might be multiple(for now, 2) external
    // camera...
    static constexpr auto max_camera_count = 4;

  public:
    // Android camera manager. After the context is initialized, this must be
    // non-null if this variable is null, then the context is considered as 'not
    // initailized' and all operation *can* be ignored
    ACameraManager* manager = nullptr;
    ACameraIdList* id_list = nullptr;

    // cached metadata
    std::array<ACameraMetadata*, max_camera_count> metadata_set{};

    //
    // even though android system limits the number of maximum open camera
    // device, we will consider multiple camera are working concurrently.
    //
    // if element is nullptr, it means the device is not open.
    std::array<ACameraDevice*, max_camera_count> device_set{};

    // if there is no session, session pointer will be null
    std::array<ACameraCaptureSession*, max_camera_count> session_set{};

    // sequence number from capture session
    std::array<int, max_camera_count> seq_id_set{};

  public:
    camera_group_t() noexcept = default;
    // copy-move is disabled
    camera_group_t(const camera_group_t&) = delete;
    camera_group_t(camera_group_t&&) = delete;
    camera_group_t& operator=(const camera_group_t&) = delete;
    camera_group_t& operator=(camera_group_t&&) = delete;
    ~camera_group_t() noexcept {
        this->release(); // ensure release precedure
    }

  public:
    void release() noexcept;

  public:
    auto open_device(uint16_t id,
                     ACameraDevice_StateCallbacks& callbacks) noexcept
        -> camera_status_t;

    // Notice that this routine doesn't free metadata
    void close_device(uint16_t id) noexcept;

    auto start_repeat(
        uint16_t id, ANativeWindow* window,
        ACameraCaptureSession_stateCallbacks& on_session_changed,
        ACameraCaptureSession_captureCallbacks& on_capture_event) noexcept
        -> camera_status_t;
    void stop_repeat(uint16_t id) noexcept;

    auto start_capture(
        uint16_t id, ANativeWindow* window,
        ACameraCaptureSession_stateCallbacks& on_session_changed,
        ACameraCaptureSession_captureCallbacks& on_capture_event) noexcept
        -> camera_status_t;
    void stop_capture(uint16_t id) noexcept;

    // ACAMERA_LENS_FACING_FRONT
    // ACAMERA_LENS_FACING_BACK
    // ACAMERA_LENS_FACING_EXTERNAL
    uint16_t get_facing(uint16_t id) noexcept;
};

// device callbacks

void context_on_device_disconnected(camera_group_t& context,
                                    ACameraDevice* device) noexcept;

void context_on_device_error(camera_group_t& context, ACameraDevice* device,
                             int error) noexcept;

// session state callbacks

void context_on_session_active(camera_group_t& context,
                               ACameraCaptureSession* session) noexcept;

void context_on_session_closed(camera_group_t& context,
                               ACameraCaptureSession* session) noexcept;

void context_on_session_ready(camera_group_t& context,
                              ACameraCaptureSession* session) noexcept;

// capture callbacks

void context_on_capture_started(camera_group_t& context,
                                ACameraCaptureSession* session,
                                const ACaptureRequest* request,
                                uint64_t time_point) noexcept;

void context_on_capture_progressed(camera_group_t& context,
                                   ACameraCaptureSession* session,
                                   ACaptureRequest* request,
                                   const ACameraMetadata* result) noexcept;

void context_on_capture_completed(camera_group_t& context,
                                  ACameraCaptureSession* session,
                                  ACaptureRequest* request,
                                  const ACameraMetadata* result) noexcept;

void context_on_capture_failed(camera_group_t& context,
                               ACameraCaptureSession* session,
                               ACaptureRequest* request,
                               ACameraCaptureFailure* failure) noexcept;

void context_on_capture_buffer_lost(camera_group_t& context,
                                    ACameraCaptureSession* session,
                                    ACaptureRequest* request,
                                    ANativeWindow* window,
                                    int64_t frame_number) noexcept;

void context_on_capture_sequence_abort(camera_group_t& context,
                                       ACameraCaptureSession* session,
                                       int sequence_id) noexcept;

void context_on_capture_sequence_complete(camera_group_t& context,
                                          ACameraCaptureSession* session,
                                          int sequence_id,
                                          int64_t frame_number) noexcept;

// status - error code to string

auto camera_error_message(camera_status_t status) noexcept -> const char*;

#endif


#ifndef TTT_GFX_TEXT_H
#define TTT_GFX_TEXT_H

#include <SDL_render.h>
#include <SDL_ttf.h>

#include <gsl/pointers>
#include <gsl/util>

#include <memory>
#include <string_view>
#include <string>
#include <stdexcept>
#include <cstdint>

namespace ttt::gfx
{
	class TextError final : public std::runtime_error
	{
	public:
		TextError(const std::string &msg) noexcept;
	};

	inline TextError::TextError(const std::string &msg) noexcept
		: runtime_error{msg}
	{
	}

	// A very simple class for rendering text.
	class Text final
	{
	public:
		Text() = default;
		Text(std::string_view str, gsl::not_null<TTF_Font *> font, const SDL_Colour &colour, bool shadow, gsl::not_null<SDL_Renderer *> renderer);

		void setText(std::string_view str, gsl::not_null<TTF_Font *> font, const SDL_Colour &colour, bool shadow, gsl::not_null<SDL_Renderer *> renderer);
		void clear() noexcept;

		void render(gsl::not_null<SDL_Renderer *> renderer, const SDL_FPoint &pos, double angle = 0.0, const SDL_FPoint *centre = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE) const noexcept;

		[[nodiscard]] SDL_Point getSize() const noexcept { return size; }

	private:
		using TexturePointer = std::unique_ptr<SDL_Texture, decltype(&SDL_DestroyTexture)>;

		TexturePointer primary{nullptr, SDL_DestroyTexture};
		TexturePointer secondary{nullptr, SDL_DestroyTexture};
		SDL_Point size{0, 0};

		[[nodiscard]] TexturePointer createTexture(std::string_view str, gsl::not_null<TTF_Font *> font, const SDL_Colour &colour, gsl::not_null<SDL_Renderer *> renderer);
		[[nodiscard]] SDL_Colour createShadowColour(const SDL_Colour &colour) const noexcept;
	};

	inline SDL_Colour Text::createShadowColour(const SDL_Colour &colour) const noexcept
	{
		return {
			gsl::narrow_cast<std::uint8_t>(colour.r % 5),
			gsl::narrow_cast<std::uint8_t>(colour.g % 5),
			gsl::narrow_cast<std::uint8_t>(colour.b % 5),
			colour.a
		};
	}
}

#endif


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef xml_f13c2b07_0230_4967_8a16_0c276e19b8a2_h
#define xml_f13c2b07_0230_4967_8a16_0c276e19b8a2_h

#include <gslib/std.h>
#include <gslib/tree.h>

__gslib_begin__

struct xml_kvpair { string key, value; };
typedef xml_kvpair xml_attr;

struct xml_key:
    public xml_kvpair
{
    xml_key(const string& k) { key = k; }
    xml_key(const gchar* c) { key.assign(c); }
};

struct xml_kvp_hash
{
    size_t operator()(const xml_kvpair& kvp) const
    { return string_hash(kvp.key); }
};

struct xml_kvp_equalto
{
    bool operator()(const xml_kvpair& p1, const xml_kvpair& p2) const
    { return p1.key == p2.key; }
};

template<class _kvpvsl>
struct xml_kvp_search
{
    static string* find(_kvpvsl& v, const string& k)
    {
        auto i = v.find(xml_key(k));
        return i != v.end() ? &(const_cast<string&>(i->value)) : nullptr;
    }
    static const string* find(const _kvpvsl& v, const string& k)
    {
        auto i = v.find(xml_key(k));
        return i != v.end() ? &(i->value) : nullptr;
    }
};

struct __gs_novtable xml_node abstract
{
    typedef unordered_set<xml_kvpair, xml_kvp_hash, xml_kvp_equalto> kvplist;
    typedef kvplist::iterator iterator;
    typedef kvplist::const_iterator const_iterator;

public:
    virtual bool is_value() const = 0;
    virtual void add_attribute(const xml_attr& attr) = 0;
    virtual string* get_attribute(const string& k) = 0;
    virtual const string* get_attribute(const string& k) const = 0;
    virtual void set_attribute(const gchar* k, int len1, const gchar* v, int len2) = 0;
    virtual void set_attribute(const string& k, const string& v) = 0;
    virtual int get_attribute_count() const = 0;
    virtual const string& get_name() const = 0;
    virtual void set_name(const gchar* str, int len) = 0;
    virtual string to_string() const = 0;   /* for debug */

public:
    string* get_attribute(const gchar* k) { return get_attribute(string(k)); }
    const string* get_attribute(const gchar* k) const { return get_attribute(string(k)); }
    string* get_attribute(const gchar* k, int len) { return get_attribute(string(k, len)); }
    const string* get_attribute(const gchar* k, int len) const { return get_attribute(string(k, len)); }
    bool has_attributes() const { return get_attribute_count() != 0; }
    void set_name(const string& str) { set_name(str.c_str(), str.length()); }
};

struct xml_element:
    public xml_node
{
    string      key;
    kvplist     attrlist;

public:
    bool is_value() const override { return false; }
    void add_attribute(const xml_attr& attr) override { attrlist.insert(attr); }
    string* get_attribute(const string& k) override { return xml_kvp_search<kvplist>::find(attrlist, k); }
    const string* get_attribute(const string& k) const override { return xml_kvp_search<kvplist>::find(attrlist, k); }
    void set_attribute(const gchar* k, int len1, const gchar* v, int len2) override
    {
        assert(k && v);
        string* vstr = get_attribute(string(k, len1));
        if(vstr) {
            vstr->assign(v, len2);
            return;
        }
        xml_attr attr;
        attr.key.assign(k, len1);
        attr.value.assign(v, len2);
        add_attribute(attr);
    }
    void set_attribute(const string& k, const string& v) override
    {
        string* vstr = get_attribute(k);
        if(vstr) {
            vstr->assign(v);
            return;
        }
        xml_attr attr;
        attr.key = k;
        attr.value = v;
        add_attribute(attr);
    }
    int get_attribute_count() const override { return (int)attrlist.size(); }
    const string& get_name() const override { return key; }
    void set_name(const gchar* str, int len) override { key.assign(str, len); }
    void set_value(const string& v) { __super::set_name(v); }
    kvplist& get_attributes() { return attrlist; }
    const kvplist& const_attributes() const { return attrlist; }
    string to_string() const override;
};

struct xml_value:
    public xml_node
{
    string      value;

public:
    bool is_value() const override { return true; }
    void add_attribute(const xml_attr& attr) override { assert(!"error!"); }
    string* get_attribute(const string& k) override { return nullptr; }
    const string* get_attribute(const string& k) const override { return nullptr; }
    void set_attribute(const gchar* k, int len1, const gchar* v, int len2) override { assert(!"error!"); }
    void set_attribute(const string& k, const string& v) override { assert(!"error!"); }
    int get_attribute_count() const override { return 0; }
    const string& get_name() const override { return value; }
    void set_name(const gchar* str, int len) override { value.assign(str, len); }
    string to_string() const override;
};

struct xml_enum
{
    typedef xml_node::iterator iterator;
    typedef xml_node::const_iterator const_iterator;

    xml_element*  node;
    iterator    iter;

    xml_enum(xml_node* n)
    {
        assert(n && !n->is_value());
        node = static_cast<xml_element*>(n);
        iter = node->attrlist.begin();
    }
    xml_enum(xml_node* n, iterator i)
    {
        assert(n && !n->is_value());
        node = static_cast<xml_element*>(n);
        iter = i;
    }
    void rewind(iterator i) { iter = i; }
    const xml_attr& get_attribute() const { return *iter; }
    bool next()
    {
        if(iter == node->attrlist.end())
            return false;
        return ++ iter != node->attrlist.end();
    }
    template<class _lambda>
    void for_each(_lambda lam) { for_each(node->attrlist.end(), lam); }
    template<class _lambda>
    void for_each(iterator end, _lambda lam)
    {
        iterator i = iter;
        for( ; i != end; ++ i)
            lam(*i);
    }
    template<class _lambda>
    void const_for_each(_lambda lam) const { const_for_each(node->attrlist.end(), lam); }
    template<class _lambda>
    void const_for_each(const_iterator end, _lambda lam) const
    {
        const_iterator i = iter;
        for( ; i != end; ++ i)
            lam(*i);
    }
};

struct xml_const_enum
{
    typedef xml_node::iterator iterator;
    typedef xml_node::const_iterator const_iterator;

    const xml_element* node;
    const_iterator  iter;

    xml_const_enum(const xml_node* n)
    {
        assert(n && !n->is_value());
        node = static_cast<const xml_element*>(n);
        iter = node->attrlist.begin();
    }
    xml_const_enum(const xml_node* n, const_iterator i)
    {
        assert(n && !n->is_value());
        node = static_cast<const xml_element*>(n);
        iter = i;
    }
    void rewind(const_iterator i) { iter = i; }
    const xml_attr& get_attribute() const { return *iter; }
    bool next()
    {
        if(iter == node->attrlist.end())
            return false;
        return ++ iter != node->attrlist.end();
    }
    template<class _lambda>
    void const_for_each(_lambda lam) const { const_for_each(node->attrlist.end(), lam); }
    template<class _lambda>
    void const_for_each(const_iterator end, _lambda lam) const
    {
        const_iterator i = iter;
        for( ; i != end; ++ i)
            lam(*i);
    }
};

typedef _treenode_wrapper<xml_node> xml_wrapper;

class xmltree:
    public tree<xml_node, xml_wrapper>
{
public:
    enum encodesys
    {
        encodesys_unknown,
        encodesys_ascii,
        encodesys_mbc,          /* multi-byte compatible */
        encodesys_wstr,         /* wide string */
    };
    enum encode
    {
        encode_unknown,
        encode_ansi,
        encode_gb2312,
        encode_utf8,
        encode_utf16,
    };
    encodesys       _encodesys;
    encode          _encode;

protected:
    static const gchar* skip(const gchar* str);
    static const gchar* skip(const gchar* str, const gchar* skp);
    static void replace(string& str, const gchar* s1, const gchar* s2);
    static void filter_entity_reference(string& s);
    static void recover_entity_reference(string& s);
    static void write_attribute(string& str, const gchar* prefix, const xml_attr& attr, const gchar* postfix);
    static void close_write_item(string& str, const gchar* prefix, const xml_node* node, const gchar* postfix);
    static void begin_write_item(string& str, const gchar* prefix, const xml_node* node, const gchar* postfix);
    static void end_write_item(string& str, const gchar* prefix, const xml_node* node, const gchar* postfix);

protected:
    const gchar* check_header(const gchar* str, string& version, string& encoding);
    const gchar* read_attribute(const gchar* str, string& k, string& v);
    const gchar* read_item(const gchar* str, iterator p);
    void setup_encoding_info(const string& enc);
    void write_header(string& str) const;
    void write_item(string& str, const_iterator i) const;

public:
    xmltree();
    bool load(const gchar* filename);
    bool parse(const gchar* src);
    void output(string& str) const;
    void save(const gchar* filename) const;
    void set_encode(encode enc) { _encode = enc; }

public:
    /*
     * Unique path locater
     * It was designed to access the DOM easier by a specified string path.
     * Usage:
     * 1.basically, we use the string "xx/yy/zz" to specify a path, which means in current position, find a sub node named "xx",
     *   go to the sub node; then find "yy", then "zz", and so on.
     *   the separator could be "/", or backslash "\", as well.
     * 2.about the name selector "$".
     *   you might also write the path as "$xx/$yy/$zz", it was the same as above; the selector "$" was skippable.
     * 3.about the counter "@"
     *   you could add a counter into the path, like "$xx@3/yy/zz", in this case, the selector "$" was unskippable.
     *   you could make a nameless selection, like "$@3/yy/zz", which means in the first jump, it would always choose the third node.
     *   or simply wrote "$/yy/zz", which means it would always choose the first node in the first jump.
     * 4.about the condition specifier "#"
     *   you could add a condition content into the selector;
     *   a condition was defined like: "$xx#aa,bb=cc,!dd|!ee,ff~=gg",
     *   there were two major binary connectors of conditions, "," refer to logic "AND", and "|" refer to logic "OR";
     *   the priority of "OR" was higher than "AND".
     *   if you need a negative condition, use "!" to reverse the logic, and the "!" should always follow "|"
     *   if a condition content was like the above "aa", which means the selected node must have the attribute named "aa"
     *   if a condition content was wrote like "bb=cc", which means the selected node must have an attribute named "bb" and valued "cc"
     *   if a condition content was wrote like "ff~=gg", which means the selected node must have an attribute named "ff" and valued "gg",
     *   the comparison was case insensitive.
     * 5.the "$", "@", "#" could be used as a combo, and the announcement should always be in the specified order, $#@
     */
    iterator unique_path_locater(const gchar* ctlstr) { return unique_path_locater(get_root(), ctlstr); }
    static iterator unique_path_locater(iterator i, const gchar* ctlstr)
    {
        assert(ctlstr);
        string cpyctlstr(ctlstr);
        ctlstr = cpyctlstr.c_str(); /* for trap */
        ctlstr = upl_run_over(i, ctlstr);
        if(ctlstr && !ctlstr[0])
            return i;
        return iterator(nullptr);
    }

public:
    static const gchar* upl_run_once(iterator& i, const gchar* ctlstr);
    static const gchar* upl_run_over(iterator& i, const gchar* ctlstr)
    {
        while(ctlstr && ctlstr[0]) {
            const gchar* ctlret = upl_run_once(i, ctlstr);
            if(!ctlret)
                return ctlstr;
            ctlstr = ctlret;
        }
        return ctlstr;
    }
};

__gslib_end__

#endif


#pragma once

#include "Core/Assert.h"
#include "Core/Delegate.h"
#include "Core/Pointers.h"
#include "Scene/Components/Component.h"

#include <gsl/span>

#include <string>
#include <utility>
#include <vector>

class Scene;

class Entity
{
public:
   using OnDestroyDelegate = MulticastDelegate<void, Entity*>;

   ~Entity();

   void destroy();

   void tick(float dt);

   template<typename T>
   T* createComponent();

   Component* createComponentByName(const std::string& className);
   bool destroyComponent(Component* componentToDestroy);

   template<typename T>
   T* getComponentByClass();

   template<typename T>
   const T* getComponentByClass() const;

   template<typename T>
   std::vector<T*> getComponentsByClass();

   template<typename T>
   std::vector<const T*> getComponentsByClass() const;

   DelegateHandle addOnDestroyDelegate(OnDestroyDelegate::FuncType&& function);
   void removeOnDestroyDelegate(const DelegateHandle& handle);

   Scene& getScene()
   {
      return scene;
   }

   const Scene& getScene() const
   {
      return scene;
   }

private:
   friend class Scene;

   template<typename... ComponentTypes>
   static UPtr<Entity> create(Scene& scene);

   static UPtr<Entity> create(gsl::span<std::string> componentClassNames, Scene& scene);

   Entity(Scene& owningScene)
      : scene(owningScene)
   {
   }

   template<typename First, typename... Rest>
   void constructComponentsHelper();

   template<typename... ComponentTypes>
   void constructComponents();

   void onInitialized();
   void onComponentCreated(Component* component);

   std::vector<UPtr<Component>> components;
   OnDestroyDelegate onDestroyDelegate;
   Scene& scene;
};

template<typename T>
T* Entity::createComponent()
{
   UPtr<T> newComponent = ComponentRegistry::instance().createComponent<T>(*this);
   ASSERT(newComponent, "Failed to create a typed component");

   T* newComponentRaw = newComponent.get();
   components.push_back(std::move(newComponent));
   onComponentCreated(newComponentRaw);
   return newComponentRaw;
}

template<typename T>
T* Entity::getComponentByClass()
{
   for (const UPtr<Component>& component : components)
   {
      if (T* castedComponent = dynamic_cast<T*>(component.get()))
      {
         return castedComponent;
      }
   }

   return nullptr;
}

template<typename T>
const T* Entity::getComponentByClass() const
{
   for (const UPtr<Component>& component : components)
   {
      if (const T* castedComponent = dynamic_cast<const T*>(component.get()))
      {
         return castedComponent;
      }
   }

   return nullptr;
}

template<typename T>
std::vector<T*> Entity::getComponentsByClass()
{
   std::vector<T*> componentsOfClass;

   for (const UPtr<Component>& component : components)
   {
      if (T* castedComponent = dynamic_cast<T*>(component.get()))
      {
         componentsOfClass.push_back(castedComponent);
      }
   }

   return componentsOfClass;
}

template<typename T>
std::vector<const T*> Entity::getComponentsByClass() const
{
   std::vector<const T*> componentsOfClass;

   for (const UPtr<Component>& component : components)
   {
      if (const T* castedComponent = dynamic_cast<const T*>(component.get()))
      {
         componentsOfClass.push_back(castedComponent);
      }
   }

   return componentsOfClass;
}

// static
template<typename... ComponentTypes>
UPtr<Entity> Entity::create(Scene& scene)
{
   UPtr<Entity> entity(new Entity(scene));

   entity->constructComponents<ComponentTypes...>();
   entity->onInitialized();

   return entity;
}

template<typename First, typename... Rest>
void Entity::constructComponentsHelper()
{
   components.push_back(ComponentRegistry::instance().createComponent<First>(*this));
   constructComponents<Rest...>();
}

template<typename... ComponentTypes>
void Entity::constructComponents()
{
   constructComponentsHelper<ComponentTypes...>();
}

template<>
inline void Entity::constructComponents()
{
}


/*
  Copyright [2020] [IBM Corporation]
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#ifndef MCAS_CCPM_LOG_H__
#define MCAS_CCPM_LOG_H__

#include <ccpm/interfaces.h>
#include <gsl/pointers>
#include <cstring>
#include <cstddef>

namespace ccpm
{
struct block_header;

struct log
  : public ILog
{
  using heap_type = gsl::not_null<IHeap_expandable *>;
  using persister_type = gsl::not_null<ccpm::persister *>;
private:
  /*
   * The log needs to be stored persistently, and to use persistent storage.
   * Use persister_type for the former and heap_type for the latter.
   */
  persister_type _persister; // not owned
  heap_type _mr; // not owned
  /* The log needs a root */
  block_header *_root; // owned
  void clear_top();

  static constexpr size_t min_log_extend = std::size_t(65536U);

  void extend(std::size_t size);
public:
  explicit log(persister_type, heap_type mr_);

  log(const log &) = delete;
  log &operator=(const log &) = delete;
  /*
   * Restoration of a log from persistent memory is done by moving the
   * log to itself with a move constructor. The persisted log has a vft
   * pointer, which is invalid when the log is rediscovered in persistent
   * memory. The move constructor creates valid vft pointer and (we hope)
   * preserves the rest of the log member data.
   */
  log(log &&) = default;
  log(log &&other, persister_type, heap_type);

  ~log();
  /*
   * Make a record of an old value
   */
  void add(void *begin, std::size_t size) override;
  /*
   * Make a record of an allocate
   */
  void allocated(void *&p, std::size_t size) override;
  /*
   * Make a record of a free
   */
  void freed(void *&p, std::size_t size) override;
  /*
   * commit and discard all log entries
   */
  void commit() override;
  /*
   * Restore all data areas added after initialization (or the most recent clear)
   * to the values present at their last add command.
   */
  void rollback() override;

  bool includes(const void *addr) const { return _mr->includes(addr); }
};

struct bad_alloc_log
	: public std::bad_alloc
{
	bad_alloc_log()
	{}
	const char *what() const noexcept override
	{
		return "bad undo log allocate";
	}
};

}
#endif


#if !defined(REGEXMOVE_H_INCLUDED)
#define REGEXMOVE_H_INCLUDED

#include "Utils.h"

#include <filesystem>
#include <gsl/gsl>
#include <iosfwd>
#include <regex>
#include <string>

class RegExMove
{
public:
	static int usage(::std::ostream& strm, const ::std::string& progName,
		const char* pMsg);

	RegExMove(::gsl::span<const char*const> args);
	int run() const;

	RegExMove(const RegExMove&) = delete;
	RegExMove& operator=(const RegExMove&) = delete;
	RegExMove(RegExMove&&) = delete;
	RegExMove& operator=(RegExMove&&) = delete;

PRIVATE_EXCEPT_IN_TEST:
	using Path = ::std::filesystem::path;
	using DirEntry = ::std::filesystem::directory_entry;

	template <typename DirIter> void processDirectoryEntries() const;
	void processDirectoryEntry(DirEntry const& dirEntry) const;
	void renamePath(Path const& p) const;

	bool				m_caseSensitive;
	bool				m_renameDirectories;
	bool				m_renameFiles;
	bool				m_recursiveSearch;
	bool				m_verboseOutput;
	bool				m_allowOverwriteOnNameCollision;
	Path				m_rootDir;
	::std::string	m_patternStr;
	::std::regex	m_pattern;
	::std::string	m_replacement;
};

#endif // REGEXMOVE_H_INCLUDED


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef scenegraph_979fc7df_3f68_4a38_9dea_5eed6d400c98_h
#define scenegraph_979fc7df_3f68_4a38_9dea_5eed6d400c98_h

#include <gslib/type.h>
#include <gslib/tree.h>
#include <ariel/config.h>

__ariel_begin__

class scene_node
{
public:
    //typedef gs::string string;
    typedef const string* sntype;
    typedef void* snptr;
    typedef snptr (*fnaccess)(int);
    typedef void (*fndestroy)(snptr);

protected:
    sntype          _tag;
    snptr           _ptr;
    string          _key;
    fndestroy       _del;

public:
    /* sntype declaration */
#define declare_sntype(snt) \
    static sntype snt;
#include <ariel\snt.h>
#undef declare_sntype

    static void register_sntypes();

public:
    scene_node() { reset(); }
    scene_node(const gchar* name)
    {
        reset();
        set_key(name);
    }
    scene_node(const gchar* name, sntype tag, snptr ptr, fndestroy del = 0)
    {
        set_key(name);
        bind(tag, ptr, del);
    }
    ~scene_node()
    {
        destroy();
        reset();
    }
    void reset()
    {
        _tag = sn_void;
        _ptr = 0;
        _del = 0;
    }
    void destroy() { if(_ptr && _del) _del(_ptr); }
    sntype get_type() const { return _tag; }
    snptr get_ptr() const { return _ptr; }
    const gchar* get_name() const { return _key.c_str(); }
    const string& get_key() const { return _key; }
    void set_destroy(fndestroy del) { _del = del; }
    void set_nodestroy() { set_destroy(0); }
    void set_key(const gchar* key) { _key.assign(key); }
    void bind(sntype tag, snptr ptr, fndestroy del = 0)
    {
        _tag = tag;
        _ptr = ptr;
        set_destroy(del);
    }
    void rebind(sntype tag, snptr ptr, fndestroy del = 0)
    {
        destroy();
        bind(tag, ptr, del);
    }
    scene_node& operator=(const scene_node& that)
    {
        _tag = that._tag;
        _ptr = that._ptr;
        _key = that._key;
        _del = that._del;
        const_cast<scene_node&>(that)._del = 0;
        return *this;
    }
};

struct scene_key
{
    const gchar*    _key;
    scene_key() { _key = 0; }
    scene_key(const gchar* k) { _key = k; }
    scene_key(const scene_node* n) { _key = n->get_name(); }
};

class scene_graph
{
public:
    struct snkey_hash
    {
        size_t operator()(const scene_key& key) const
        { return string_hash(key._key); }
    };
    struct snkey_equal
    {
        bool operator()(const scene_key& k1, const scene_key& k2) const
        { return string_hash(k1._key) == string_hash(k2._key); }
    };

    typedef tree<scene_node> sntree;
    typedef sntree::wrapper wrapper;
    typedef sntree::iterator iterator;
    typedef sntree::const_iterator const_iterator;
    typedef unordered_map<scene_key, scene_node*, snkey_hash, snkey_equal> sntable;
    typedef scene_node::sntype sntype;
    typedef scene_node::snptr snptr;
    typedef scene_node::fndestroy fndestroy;

public:
    template<class castop, int bias, class castp>
    static castop fbop_cast(castp ptr) { return ptr ? reinterpret_cast<castop>(((int)ptr) + bias) : 0; }
    template<class castop, class castp>
    static castop fbop_cast(castp ptr, int bias) { return ptr ? reinterpret_cast<castop>(((int)ptr) + bias) : 0; }
    static int wrapper_node_bias()
    {
        static wrapper inst;
        static const int bias = (int)&inst - ((int)inst.get_ptr());
        return bias;
    }
    static wrapper* node_to_wrapper(scene_node* ptr) { return fbop_cast<wrapper*>(ptr, -wrapper_node_bias()); }
    static const wrapper* node_to_wrapper(const scene_node* ptr) { return fbop_cast<const wrapper*>(ptr, -wrapper_node_bias()); }
    static scene_node* iter_to_node(iterator i) { return i.get_ptr(); }
    static const scene_node* iter_to_node(const_iterator i) { return i.get_ptr(); }
    static iterator node_to_iter(scene_node* ptr) { return iterator(node_to_wrapper(ptr)); }
    static const_iterator node_to_iter(const scene_node* ptr) { return const_iterator(node_to_wrapper(ptr)); }

public:
    scene_graph();
    virtual ~scene_graph() {}

public:
    scene_node* get_root_node() { return iter_to_node(_sntree.get_root()); }
    const scene_node* get_root_node() const { return iter_to_node(_sntree.get_root()); }
    bool is_node_attached(const scene_node* node) const { return node && _sntree.is_mine(node_to_iter(node)); }
    sntree* get_node_tree(const scene_node* node);
    const sntree* get_node_tree(const scene_node* node) const;
    void fix_node(scene_node* old, scene_node* new1);
    void map_node(scene_node* node, const gchar* name);
    void unmap_node(scene_node* node);

public:
    virtual scene_node* create_node(sntype tag = scene_node::sn_void, snptr ptr = 0, fndestroy del = 0);
    virtual scene_node* create_node(const gchar* name, sntype tag = scene_node::sn_void, snptr ptr = 0, fndestroy del = 0);
    virtual scene_node* attach_node(scene_node* parent, scene_node* node);
    virtual scene_node* detach_node(scene_node* node);
    virtual void destroy_node(scene_node* node);
    virtual void clear_miscs();

protected:
    sntree          _sntree;
    sntree          _miscs;
    sntable         _sntable;
};

__ariel_end__

#endif


#ifndef SQ_INCLUDE_GUARD_core_SharedRange_h_
#define SQ_INCLUDE_GUARD_core_SharedRange_h_

#include <gsl/gsl>
#include <memory>
#include <range/v3/range/concepts.hpp>
#include <vector>

namespace sq {

template <typename R> requires ranges::cpp20::range<R> struct SharedRange {
public:
  explicit SharedRange(const std::shared_ptr<R> &base) : base_{base} {}

  explicit SharedRange(std::shared_ptr<R> &&base) : base_{std::move(base)} {}

  constexpr SharedRange() noexcept = default;
  SharedRange(const SharedRange &) noexcept = default;
  SharedRange(SharedRange &&) noexcept = default;
  SharedRange &operator=(const SharedRange &) noexcept = default;
  SharedRange &operator=(SharedRange &&) noexcept = default;
  ~SharedRange() noexcept = default;

  auto begin() {
    Expects(base_ != nullptr);
    return ranges::begin(*base_);
  }
  auto end() {
    Expects(base_ != nullptr);
    return ranges::end(*base_);
  }

private:
  // views must be semiregular, which means that SharedRange must have a
  // default constructor, which, unfortunately, means that we can't use
  // gsl::not_null here.
  std::shared_ptr<R> base_ = nullptr;
};

} // namespace sq

namespace ranges {

template <typename R>
inline constexpr bool enable_view<sq::SharedRange<R>> = true;

} // namespace ranges

namespace sq {

static_assert(ranges::cpp20::range<SharedRange<std::vector<int>>>);
static_assert(ranges::cpp20::view<SharedRange<std::vector<int>>>);
static_assert(ranges::viewable_range<SharedRange<std::vector<int>>>);

} // namespace sq

#endif // SQ_INCLUDE_GUARD_core_SharedRange_h_


#pragma once

#include "Cesium3DTiles/BoundingVolume.h"
#include "Cesium3DTiles/Library.h"
#include "Cesium3DTiles/Tile.h"
#include "Cesium3DTiles/TileContext.h"
#include "Cesium3DTiles/TileID.h"
#include "Cesium3DTiles/TileRefine.h"

#include <gsl/span>
#include <spdlog/fwd.h>

#include <cstddef>
#include <memory>

namespace Cesium3DTiles {
/**
 * @brief The information that is passed to a {@link TileContentLoader} to
 * create a {@link TileContentLoadResult}.
 *
 * For many types of tile content, only the `data` field is required. The other
 * members are used for content that can generate child tiles, like external
 * tilesets or composite tiles. These members are usually initialized from
 * the corresponding members of the {@link Tile} that the content belongs to.
 */
struct CESIUM3DTILES_API TileContentLoadInput {

  /**
   * @brief Creates a new, uninitialized instance for the given tile.
   *
   * The `data`, `contentType` and `url` will have default values,
   * and have to be initialized before this instance is passed to
   * one of the loader functions.
   *
   * @param pLogger_ The logger that will be used
   * @param tile_ The {@link Tile} that the content belongs to
   */
  TileContentLoadInput(
      const std::shared_ptr<spdlog::logger> pLogger_,
      const Tile& tile_)
      : pLogger(pLogger_),
        data(),
        contentType(),
        url(),
        tileID(tile_.getTileID()),
        tileBoundingVolume(tile_.getBoundingVolume()),
        tileContentBoundingVolume(tile_.getContentBoundingVolume()),
        tileRefine(tile_.getRefine()),
        tileGeometricError(tile_.getGeometricError()),
        tileTransform(tile_.getTransform()) {}

  /**
   * @brief Creates a new instance for the given tile.
   *
   * @param pLogger_ The logger that will be used
   * @param data_ The actual data that the tile content will be created from
   * @param contentType_ The content type, if the data was received via a
   * network response
   * @param url_ The URL that the data was loaded from
   * @param tile_ The {@link Tile} that the content belongs to
   */
  TileContentLoadInput(
      const std::shared_ptr<spdlog::logger> pLogger_,
      const gsl::span<const std::byte>& data_,
      const std::string& contentType_,
      const std::string& url_,
      const Tile& tile_)
      : pLogger(pLogger_),
        data(data_),
        contentType(contentType_),
        url(url_),
        tileID(tile_.getTileID()),
        tileBoundingVolume(tile_.getBoundingVolume()),
        tileContentBoundingVolume(tile_.getContentBoundingVolume()),
        tileRefine(tile_.getRefine()),
        tileGeometricError(tile_.getGeometricError()),
        tileTransform(tile_.getTransform()) {}

  /**
   * @brief Creates a new instance.
   *
   * For many types of tile content, only the `data` field is required. The
   * other parameters are used for content that can generate child tiles, like
   * external tilesets or composite tiles.
   *
   * @param pLogger_ The logger that will be used
   * @param data_ The actual data that the tile content will be created from
   * @param contentType_ The content type, if the data was received via a
   * network response
   * @param url_ The URL that the data was loaded from
   * @param context_ The {@link TileContext}
   * @param tileID_ The {@link TileID}
   * @param tileBoundingVolume_ The tile {@link BoundingVolume}
   * @param tileContentBoundingVolume_ The tile content {@link BoundingVolume}
   * @param tileRefine_ The {@link TileRefine} strategy
   * @param tileGeometricError_ The geometric error of the tile
   * @param tileTransform_ The tile transform
   */
  TileContentLoadInput(
      const std::shared_ptr<spdlog::logger> pLogger_,
      const gsl::span<const std::byte>& data_,
      const std::string& contentType_,
      const std::string& url_,
      const TileID& tileID_,
      const BoundingVolume& tileBoundingVolume_,
      const std::optional<BoundingVolume>& tileContentBoundingVolume_,
      TileRefine tileRefine_,
      double tileGeometricError_,
      const glm::dmat4& tileTransform_)
      : pLogger(pLogger_),
        data(data_),
        contentType(contentType_),
        url(url_),
        tileID(tileID_),
        tileBoundingVolume(tileBoundingVolume_),
        tileContentBoundingVolume(tileContentBoundingVolume_),
        tileRefine(tileRefine_),
        tileGeometricError(tileGeometricError_),
        tileTransform(tileTransform_) {}

  /**
   * @brief The logger that receives details of loading errors and warnings.
   */
  std::shared_ptr<spdlog::logger> pLogger;

  /**
   * @brief The raw input data.
   *
   * The {@link TileContentFactory} will try to determine the type of the
   * data using the first four bytes (i.e. the "magic header"). If this
   * does not succeed, it will try to determine the type based on the
   * `contentType` field.
   */
  gsl::span<const std::byte> data;

  /**
   * @brief The content type.
   *
   * If the data was obtained via a HTTP response, then this will be
   * the `Content-Type` of that response. The {@link TileContentFactory}
   * will try to interpret the data based on this content type.
   *
   * If the data was not directly obtained from an HTTP response, then
   * this may be the empty string.
   */
  std::string contentType;

  /**
   * @brief The source URL.
   */
  std::string url;

  /**
   * @brief The {@link TileID}.
   */
  TileID tileID;

  /**
   * @brief The tile {@link BoundingVolume}.
   */
  BoundingVolume tileBoundingVolume;

  /**
   * @brief Tile content {@link BoundingVolume}.
   */
  std::optional<BoundingVolume> tileContentBoundingVolume;

  /**
   * @brief The {@link TileRefine}.
   */
  TileRefine tileRefine;

  /**
   * @brief The geometric error.
   */
  double tileGeometricError;

  /**
   * @brief The tile transform
   */
  glm::dmat4 tileTransform;
};
} // namespace Cesium3DTiles


#pragma once
#include "iarg.h"
#include "optioninfo.h"
#include "configaccess.h"
#include "format.h"
#include "streamreader.h"
#include <gsl/gsl>
#include <cmdlime/errors.h>
#include <cmdlime/customnames.h>
#include <sstream>
#include <functional>
#include <memory>

namespace cmdlime::detail{

template <typename T>
class Arg : public IArg{
public:
    Arg(std::string name,
        std::string type,
        std::function<T&()> argGetter)
        : info_(std::move(name), {}, std::move(type))
        , argGetter_(std::move(argGetter))
    {
    }

    OptionInfo& info() override
    {
        return info_;
    }

    const OptionInfo& info() const override
    {
        return info_;
    }

private:
    bool read(const std::string& data) override
    {
        auto stream = std::stringstream{data};
        return readFromStream(stream, argGetter_());
    }

private:
    OptionInfo info_;
    std::function<T&()> argGetter_;
};

template <>
inline bool Arg<std::string>::read(const std::string& data)
{
    argGetter_() = data;
    return true;
}

template<typename T, typename TConfig>
class ArgCreator{
    using NameProvider = typename Format<ConfigAccess<TConfig>::format()>::nameProvider;
public:
    ArgCreator(TConfig& cfg,
               const std::string& varName,
               const std::string& type,
               std::function<T&()> argGetter)
        : cfg_(cfg)
    {
        Expects(!varName.empty());
        Expects(!type.empty());
        arg_ = std::make_unique<Arg<T>>(NameProvider::fullName(varName),
                                        NameProvider::valueName(type),
                                        std::move(argGetter));
    }

    ArgCreator<T, TConfig>& operator<<(const std::string& info)
    {
        arg_->info().addDescription(info);
        return *this;
    }

    ArgCreator<T, TConfig>& operator<<(const Name& customName)
    {
        arg_->info().resetName(customName.value());
        return *this;
    }

    ArgCreator<T, TConfig>& operator<<(const ValueName& valueName)
    {
        arg_->info().resetValueName(valueName.value());
        return *this;
    }

    operator T()
    {
        ConfigAccess<TConfig>{cfg_}.addArg(std::move(arg_));
        return T{};
    }

private:
    std::unique_ptr<Arg<T>> arg_;
    TConfig& cfg_;
};

template <typename T, typename TConfig>
ArgCreator<T, TConfig> makeArgCreator(TConfig& cfg,
                                       const std::string& varName,
                                       const std::string& type,
                                       std::function<T&()> argGetter)
{
    return ArgCreator<T, TConfig>{cfg, varName, type, std::move(argGetter)};
}

}


#pragma once

#include <gsl.h>
#include <memory>

namespace crimson {

class ISolverSetupManager;

/*! \brief   A solver setup service interface. */
class ISolverSetupService
{
public:

    /*!
     * \brief   Gets solver setup manager.
     */
    virtual gsl::not_null<ISolverSetupManager*> getSolverSetupManager() const = 0;
    virtual ~ISolverSetupService() {}
};

}


#pragma once

#include <csignal>
#include <iostream>
#include <sstream>
#include <cassert>
#include <vector>
#include <algorithm>
#include <array>
#include <memory>
#include <iomanip>
#include <fstream>
#include <filesystem>
#include <cstdarg>
#include <unordered_set>
#include <unordered_map>
#include <bitset>
#include <numeric>
#include <cmath>

#ifdef _MSC_VER
#	pragma  warning(disable:4267)
#endif

#include <GLTFAsset.h>
#include <GLTFScene.h>
#include <GLTFBuffer.h>
#include <GLTFBufferView.h>
#include <GLTFAccessor.h>
#include <GLTFMesh.h>
#include <GLTFPrimitive.h>

#ifdef _MSC_VER
#	pragma  warning(default:4267)
#endif

#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

#include <gsl/span>

#include <coveo/linq.h>
#include <coveo/enumerable.h>

#include <maya/MPxCommand.h>
#include <maya/MFnPlugin.h>
#include <maya/MIOStream.h>
#include <maya/MGlobal.h>
#include <maya/MArgList.h>
#include <maya/MArgDatabase.h>
#include <maya/MSyntax.h>
#include <maya/MStreamUtils.h>
#include <maya/MFileObject.h>
#include <maya/MFileIO.h>
#include <maya/MSelectionList.h>
#include <maya/MFnMesh.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MFloatPointArray.h>
#include <maya/MFloatVectorArray.h>
#include <maya/MDagPath.h>
#include <maya/MPointArray.h>
#include <maya/MFnAttribute.h>
#include <maya/MFnStringArrayData.h>
#include <maya/MFnMessageAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MDagPathArray.h>
#include <maya/MFnSet.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnComponentListData.h>
#include <maya/MDagModifier.h>
#include <maya/MMatrix.h>
#include <maya/MFnMatrixData.h>
#include <maya/MFnTransform.h>
#include <maya/MItDependencyGraph.h>
#include <maya/MFnBlendShapeDeformer.h>
#include <maya/MFnPhongShader.h>
#include <maya/MFnLambertShader.h>
#include <maya/MFnBlinnShader.h>
#include <maya/MUuid.h>
#include <maya/MImage.h>
#include <maya/MFloatMatrix.h>
#include <maya/MTime.h>
#include <maya/MAnimControl.h>
#include <maya/M3dView.h>
#include <maya/MFnSkinCluster.h>
#include <maya/MItGeometry.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MItMeshFaceVertex.h>


/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2018, mtezych
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CL_CONTEXT
#define CL_CONTEXT

#ifdef __APPLE__
	#include <OpenCL/cl.h>
#else
	#include <CL/cl.h>
#endif

#include <cl/Device.h>

#include <util/util.h>

#include <gsl/span>

#include <vector>
#include <cstddef>

namespace cl
{
	struct Platform;

	struct Context
	{
		cl_context clContext;

		//
		// @note: The NotifyCallback must be thread-safe.
		//
		using NotifyCallback = void (CL_CALLBACK *)
		(
			const char* errorInfo,
			const void* privateInfo, size_t cb,
			void* userData
		);

		Context
		(
			const Platform&         platform,
			gsl::span<const Device> devices
		);

		Context
		(
			const Platform& platform,
			cl_device_type  deviceType
		);

		~Context ();

		Context (Context&& context);
		Context (const Context& context) = delete;

		Context& operator = (Context&& context);
		Context& operator = (const Context& context) = delete;

		enum class Info : cl_context_info
		{
			NumDevices     = CL_CONTEXT_NUM_DEVICES,
			ReferenceCount = CL_CONTEXT_REFERENCE_COUNT,
		};

		template <Info info>
		auto GetInfo() const
		{
			auto infoSize = std::size_t { 0 };
			auto result = clGetContextInfo
			(
				clContext, util::enum_cast(info),
				0, nullptr, &infoSize
			);
			assert(result == CL_SUCCESS);

			auto infoBytes = std::vector<std::byte>
			{
				infoSize, std::byte { 0x00 }
			};
			result = clGetContextInfo
			(
				clContext, util::enum_cast(info),
				infoBytes.size(), infoBytes.data(), nullptr
			);
			assert(result == CL_SUCCESS);

			return InfoResult<info>::FromBytes(infoBytes);
		}

	private:

		template <Info info>
		struct InfoResult;
	};

	template <>
	struct Context::InfoResult<Context::Info::NumDevices>
	{
		static cl_uint
		FromBytes (const std::vector<std::byte>& infoBytes);
	};

	template <>
	struct Context::InfoResult<Context::Info::ReferenceCount>
	{
		static cl_uint
		FromBytes (const std::vector<std::byte>& infoBytes);
	};
}

#endif