/*-
 * redshift-ng - Automatically adjust display colour temperature according the Sun
 * 
 * Copyright (c) 2009-2018        Jon Lund Steffensen <jonlst@gmail.com>
 * Copyright (c) 2014-2016, 2025  Mattias Andrée <m@maandree.se>
 * 
 * redshift-ng is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * redshift-ng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with redshift-ng.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "common.h"


const struct gamma_method *gamma_methods[] = {
#ifdef ENABLE_COOPGAMMA
	&coopgamma_gamma_method,
#endif
	&drm_gamma_method,
	&randr_gamma_method,
	&vidmode_gamma_method,
	&quartz_gamma_method,
	&w32gdi_gamma_method,
	&dummy_gamma_method,
	NULL
};


/**
 * Attempt to start a specific adjustment method
 * 
 * @param   method     The adjustment method
 * @param   state_out  Output parameter for the adjustment method state
 * @param   config     Loaded information file
 * @param   args       `NULL` or option part of the command line argument for the adjustment method
 * @return             0 on success, -1 on failure
 */
static int
try_start(const struct gamma_method *method, GAMMA_STATE **state_out, struct config_ini_state *config, char *args)
{
	struct config_ini_section *section;
	struct config_ini_setting *setting;
	char *next_arg, *value;
	const char *key;

	if (method->create(state_out) < 0) {
		weprintf(_("Initialization of %s failed."), method->name);
		goto fail;
	}

	/* Set method options from config file */
	if ((section = config_ini_get_section(config, method->name)))
		for (setting = section->settings; setting; setting = setting->next)
			if (method->set_option(*state_out, setting->name, setting->value) < 0)
				goto set_option_fail;

	/* Set method options from command line */
	for (; args && *args; args = next_arg) {
		if (!strncasecmp(args, "display=", sizeof("display=") - 1U))
			next_arg = &args[strcspn(args, ";")];
		else
			next_arg = &args[strcspn(args, ";:")];
		if (*next_arg)
			*next_arg++ = '\0';

		key = args;
		value = strchr(args, '=');
		if (!value) {
			weprintf(_("Failed to parse option `%s'."), args);
			goto fail;
		}
		*value++ = '\0';

		if (method->set_option(*state_out, key, value) < 0)
			goto set_option_fail;
	}

	/* Start method */
	if (method->start(*state_out) < 0) {
		weprintf(_("Failed to start adjustment method %s."), method->name);
		goto fail;
	}

	return 0;

set_option_fail:
	weprintf(_("Failed to set %s option."), method->name);
	/* TRANSLATORS: `help' must not be translated. */
	weprintf(_("Try `-m %s:help' for more information."), method->name);
fail:
	if (*state_out) {
		method->free(*state_out);
		*state_out = NULL;
	}
	return -1;
}


void
acquire_adjustment_method(struct settings *settings, GAMMA_STATE **method_state_out)
{
	size_t i;

	if (settings->method) {
		/* Use method specified on command line */
		if (try_start(settings->method, method_state_out, &settings->config, settings->method_args) < 0)
			exit(1);
	} else {
		/* Try all methods, use the first that works */
		for (i = 0; gamma_methods[i]; i++) {
			if (!gamma_methods[i]->autostart)
				continue;
			if (!gamma_methods[i]->is_available())
				continue;

			if (try_start(gamma_methods[i], method_state_out, &settings->config, NULL) < 0) {
				weprintf(_("Trying next method..."));
				continue;
			}

			/* Found method that works */
			printf(_("Using method `%s'.\n"), gamma_methods[i]->name);
			settings->method = gamma_methods[i];
			break;
		}

		/* Failure if no methods were successful at this point */
		if (!settings->method)
			eprintf(_("No more methods to try."));
	}
}