/* common.h -- Common header file for Redshift source files
 * This file is part of redshift-ng.
 * 
 * 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/>.
 * 
 * Copyright (c) 2009-2017               Jon Lund Steffensen <jonlst@gmail.com>
 * Copyright (c) 2014, 2015, 2016, 2025  Mattias Andrée <m@maandree.se>
 */
#ifndef REDSHIFT_COMMON_H
#define REDSHIFT_COMMON_H

#include <libsimple.h>
#include <libsimple-arg.h>

#ifndef WINDOWS
# if defined(__WIN32__) || defined(_WIN32)
#  define WINDOWS
# endif
#endif

#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <locale.h>
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef _POSIX_TIMERS
# include <sys/time.h>
#endif
#ifdef WINDOWS
# include <windows.h>
# define localtime_r(T, TM) localtime_s((TM), (T))
#else
# include <pwd.h>
# include <time.h>
#endif

#include <libred.h>

#ifdef ENABLE_NLS
# include <libintl.h>
#else
# define gettext(s) s
#endif
#define _(s) gettext(s)
#define N_(s) s

#if defined(__GNUC__)
# define GCC_ONLY(...) __VA_ARGS__
#else
# define GCC_ONLY(...)
#endif


/**
 * The colour temperature corresponding to no effect
 */
#define NEUTRAL_TEMP  6500


#define SOLAR_CIVIL_TWILIGHT_ELEV    -6.0


/**
 * Truncate a value into a bounded range
 * 
 * @param   LO  The lower bound
 * @param   X   The value to truncated
 * @param   UP  The upper bound
 * @return      `X` truncated such that it is at least `LO` and at most `UP`
 */
#define CLAMP(LO, X, UP)  (((LO) > (X)) ? (LO) : (((X) < (UP)) ? (X) : (UP)))



enum period {
	PERIOD_NONE = 0,
	PERIOD_DAYTIME,
	PERIOD_NIGHT,
	PERIOD_TRANSITION
} ;

enum program_mode {
	PROGRAM_MODE_CONTINUAL,
	PROGRAM_MODE_ONE_SHOT,
	PROGRAM_MODE_PRINT,
	PROGRAM_MODE_RESET,
	PROGRAM_MODE_MANUAL
};

struct location {
	double lat, lon;
};

struct color_setting {
	int temperature;
	double gamma[3];
	double brightness;
};

/* Time range.
   Fields are offsets from midnight in seconds. */
struct time_range {
	int start;
	int end;
};

/* Transition scheme.
   The solar elevations at which the transition begins/ends,
   and the association color settings. */
struct transition_scheme {
	double high;
	double low;
	int use_time; /* When enabled, ignore elevation and use time ranges. */
	struct time_range dawn;
	struct time_range dusk;
	struct color_setting day;
	struct color_setting night;
};

struct config_ini_setting {
	struct config_ini_setting *next;
	char *name;
	char *value;
};

struct config_ini_section {
	struct config_ini_section *next;
	char *name;
	struct config_ini_setting *settings;
};

struct config_ini_state {
	struct config_ini_section *sections;
};


struct options {
	/* Path to config file */
	char *config_filepath;

	struct transition_scheme scheme;
	enum program_mode mode;
	int verbose;

	/* Temperature to set in manual mode. */
	int temp_set;
	/* Whether to fade between large skips in color temperature. */
	int use_fade;
	/* Whether to preserve gamma ramps if supported by gamma method. */
	int preserve_gamma;

	/* Selected gamma method. */
	const struct gamma_method *method;
	/* Arguments for gamma method. */
	char *method_args;

	/* Selected location provider. */
	const struct location_provider *provider;
	/* Arguments for location provider. */
	char *provider_args;
};


/* Gamma adjustment method */
typedef struct gamma_state GAMMA_STATE;

struct gamma_method {
	const char *name;

	/* If true, this method will be tried if none is explicitly chosen. */
	int autostart;

	/* Initialize state. Options can be set between init and start. */
	int (*init)(GAMMA_STATE **state);
	/* Allocate storage and make connections that depend on options. */
	int (*start)(GAMMA_STATE *state, enum program_mode mode);
	/* Free all allocated storage and close connections. */
	void (*free)(GAMMA_STATE *state);

	/* Print help on options for this adjustment method. */
	void (*print_help)(FILE *f);
	/* Set an option key, value-pair */
	int (*set_option)(GAMMA_STATE *state, const char *key, const char *value);

	/* Restore the adjustment to the state before start was called. */
	void (*restore)(GAMMA_STATE *state);
	/* Set a specific color temperature. */
	int (*set_temperature)(GAMMA_STATE *state, const struct color_setting *setting, int preserve);
};


/* Location provider */
typedef struct location_state LOCATION_STATE;

struct location_provider {
	const char *name;

	/* Initialize state. Options can be set between init and start. */
	int (*init)(LOCATION_STATE **state);
	/* Allocate storage and make connections that depend on options. */
	int (*start)(LOCATION_STATE *state);
	/* Free all allocated storage and close connections. */
	void (*free)(LOCATION_STATE *state);

	/* Print help on options for this location provider. */
	void (*print_help)(FILE *f);
	/* Set an option key, value-pair. */
	int (*set_option)(LOCATION_STATE *state, const char *key, const char *value);

	/* Listen and handle location updates. */
	int (*get_fd)(LOCATION_STATE *state);
	int (*handle)(LOCATION_STATE *state, struct location *location, int *available);
};


#define LIST_RAMPS_STOP_VALUE_TYPES(X, D)\
	X(u8, uint8_t, UINT8_MAX, 8) D\
	X(u16, uint16_t, UINT16_MAX, 16) D\
	X(u32, uint32_t, UINT32_MAX, 32) D\
	X(u64, uint64_t, UINT64_MAX, 64) D\
	X(float, float, 1, -1) D\
	X(double, double, 1, -2)

#define X(SUFFIX, TYPE, MAX, DEPTH)\
	/**
	 * Fill the gamma ramps
	 * 
	 * @param  gamma_r   The gamma ramp for the red channel
	 * @param  gamma_g   The gamma ramp for the green channel
	 * @param  gamma_b   The gamma ramp for the blue channel
	 * @param  size_r    The number of stops in `gamma_r`
	 * @param  size_g    The number of stops in `gamma_g`
	 * @param  size_b    The number of stops in `gamma_b`
	 * @param  settings  The colour settings to apply (temperature, brightness, gamma)
	 */\
	void colorramp_fill_##SUFFIX(TYPE *gamma_r, TYPE *gamma_g, TYPE *gamma_b,\
	                             size_t size_r, size_t size_g, size_t size_b,\
	                             const struct color_setting *setting)
LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
#undef X


/**
 * Load the configuration file
 * 
 * @param  state  Output parameter for the configurations
 * @param  path   The path to the configuration file, or `NULL` if the
 *                application should look for it in the default paths
 */
void config_ini_init(struct config_ini_state *state, const char *path);

/**
 * Deallocate the settings loaded
 * from the configurations file
 * 
 * @param  state  The configurations
 */
void config_ini_free(struct config_ini_state *state);

/**
 * Get a section from the configuration file
 * 
 * @param   state  The configurations
 * @param   name   The name of the section
 * @return         The section; `NULL` if missing
 */
GCC_ONLY(__attribute__((__pure__)))
struct config_ini_section *config_ini_get_section(struct config_ini_state *state, const char *name);


void options_init(struct options *options);
void options_parse_args(struct options *options, int argc, char *argv[],
                        const struct gamma_method *gamma_methods,
                        const struct location_provider *location_providers);
void options_parse_config_file(struct options *options, struct config_ini_state *config_state,
                               const struct gamma_method *gamma_methods,
                               const struct location_provider *location_providers);
void options_set_defaults(struct options *options);


void hooks_signal_period_change(enum period prev_period, enum period period);


/**
 * Create a pipe(7) where both ends have O_NONBLOCK applied
 * 
 * @param   Output parameter for the pipe's file descriptors:
 *          0) reading file descriptor, and
 *          1) writing file descriptor.
 * @return  0 on success, -1 on failure
 */
int pipeutils_create_nonblocking(int pipefds[2]);

void pipeutils_signal(int write_fd);
void pipeutils_handle_signal(int read_fd);


/**
 * Set to 1 once the process has received a signal to terminate
 */
extern volatile sig_atomic_t exiting;

/**
 * Set to 1 once the process has received a signal to remove its effect
 */
extern volatile sig_atomic_t disable;


/**
 * Install signal handlers for the process
 */
void signals_install_handlers(void);


/**
 * Get the current time in seconds since the Unix epoch
 *
 * @return  The current time
 */
double systemtime_get_time(void);

/**
 * Suspend the process for a short time
 *
 * The process may be resumed earily, specifically
 * if it receives a signal
 * 
 * @param  msecs  The number of milliseconds to sleep
 */
void systemtime_msleep(unsigned int msecs);


extern const struct gamma_method dummy_gamma_method;

#ifdef ENABLE_COOPGAMMA
extern const struct gamma_method coopgamma_gamma_method;
#endif

#ifdef ENABLE_RANDR
extern const struct gamma_method randr_gamma_method;
#endif

#ifdef ENABLE_VIDMODE
extern const struct gamma_method vidmode_gamma_method;
#endif

#ifdef ENABLE_DRM
extern const struct gamma_method drm_gamma_method;
#endif

#ifdef ENABLE_QUARTZ
extern const struct gamma_method quartz_gamma_method;
#endif

#ifdef ENABLE_W32GDI
extern const struct gamma_method w32gdi_gamma_method;
#endif


extern const struct location_provider manual_location_provider;

#ifdef ENABLE_GEOCLUE2
extern const struct location_provider geoclue2_location_provider;
#endif

#ifdef ENABLE_CORELOCATION
extern const struct location_provider corelocation_location_provider;
#endif


#endif