#ifndef NEARSMTH_H
#define NEARSMTH_H

/* 
 * nearsmth
 *
 * Gamut mapping support routine that creates a list of
 * guide vectors from the source to destination
 * gamut, smoothed to retain reasonably even spacing.
 *
 * Author:  Graeme W. Gill
 * Date:    17/1/2002
 * Version: 1.00
 *
 * Copyright 2002 - 2006 Graeme W. Gill
 * All rights reserved.
 *
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */


/* Settings for nearsmth & gammap */

#define GAMMAP_RSPLFLAGS (0)		/* Default rspl flags */
#define GAMMAP_RSPLAVGDEV 0.005		/* Default average deviation */

/* Structures to hold weightings */

/* Color x light & dark = 14 combinations */
typedef enum {
	/* Combinations in prioroty order */
	gmm_light_red     = 0x101,
	gmm_light_yellow  = 0x102,
	gmm_light_green   = 0x104,
	gmm_light_cyan    = 0x108,
	gmm_light_blue    = 0x110,
	gmm_light_magenta = 0x120,
	gmm_light_neutral = 0x140,

	gmm_dark_red      = 0x201,
	gmm_dark_yellow   = 0x202,
	gmm_dark_green    = 0x204,
	gmm_dark_cyan     = 0x208,
	gmm_dark_blue     = 0x210,
	gmm_dark_magenta  = 0x220,
	gmm_dark_neutral  = 0x240,

	gmm_l_d_red       = 0x301,
	gmm_l_d_yellow    = 0x302,
	gmm_l_d_green     = 0x304,
	gmm_l_d_cyan      = 0x308,
	gmm_l_d_blue      = 0x310,
	gmm_l_d_magenta   = 0x320,
	gmm_l_d_neutral   = 0x340,

	gmm_light_colors  = 0x17f,	/* All light colors */
	gmm_dark_colors   = 0x27f,	/* All dark colors */

	gmm_default       = 0x37f,	/* All light and dark colors */ 

	gmm_end     = 0x0000,		/* Mark the end of the list */
	gmm_ignore  = 0x1001,		/* Ignore this entry */

	/* Components */
	gmc_light   = 0x100,
	gmc_dark    = 0x200,
	gmc_l_d     = 0x300,	/* Both Light and dark */
	gmc_red     = 0x001,
	gmc_yellow  = 0x002,
	gmc_green   = 0x004,
	gmc_cyan    = 0x008,
	gmc_blue    = 0x010,
	gmc_magenta = 0x020,
	gmc_neutral = 0x040,
	gmc_colors  = 0x07f		/* All colors */

} gmm_chex;

/* Single weight */
typedef struct {
	double l,c,h;
} iweight;

/* Group of complete weights */
/* (Remeber to alter near_wcopy() and near_wblend() !!) */
typedef struct {

	gmm_chex ch;			/* Color hextant this applies to */

	/* Cusp alignment control */
	struct {
		iweight w;			/* Component alignment weights, 0 - 1 */
		double tw;			/* Alignment twist power, 0 = linear, 1 = curve, 2+ late curve */ 
							/* Use > 0 to make cusp alignment only affect surface colors. */
		double cx;			/* Chroma expansion, 1 = none, > 1 = more */
	} c;

	/* Radial weighting (This seems to be not working at the moment ?) */
	/* Weight to give to minimizing delta E to source mapped radially */
	struct {
		double o;			/* Overall Radial weight */
		double h;			/* Hue dominance vs l+c, 0 - 1 */
		double l;			/* l dominance vs, c, 0 - 1 */
	} l;

	/* Absolute error weighting. */
	/* Weight given to minimizing delta E to destination closest point */
	struct {
		double o;			/* Overall Absolute weight */

		double h;			/* Hue dominance vs l+c, 0 - 1 */

		double wl;			/* White l dominance vs, c, 0 - 1 */
		double gl;			/* Grey l dominance vs, c, 0 - 1 */
		double bl;			/* Black l dominance vs, c, 0 - 1 */

		double wlth;		/* White l blend start radius, 0 - 1, at white = 0 */
		double blpow;		/* Black l blend power, linear = 1.0, enhance < 1.0 */

		double lxpow;		/* L error extra power, none = 1.0 */
		double lxthr;		/* L error xover threshold in DE */
	} a;

	/* Relative vector smoothing. */
	struct {
		double rdl;			/* Direction smoothing radius L* dir. (delta E radius at src point)*/
		double rdh;			/* Direction smoothing radius H* (delta E radius at src point)*/

		double dsm;			/* Degree of smoothing (non-linear response) */
	} r;

	/* Depth room weighting. */
	/* Weighing to give to minimizing depth ratio by mapping to/from adequate dest/src depth. */
	/* The idea is to compromize luminance and/or hue to allow room for */
	/* preserving saturation distinction under heavy compression. */
	struct {
		double co;			/* Overall compression weighting */
		double xo;			/* Overall expansion weighting */
	} d;

	/* Fine tuning destination gamut mapping */
	struct {
		double x;			/* Final expansion weight, 0 - 1 */
	} f;					/* Weights fine tuning to expand rather than just compress */

	/* Internal use */
	iweight rl;				/* Resolved radial weight */
	iweight ra;				/* Resolved absolute weight */

	int set;				/* Whether this has been set */
} gammapweights;


/* Blend a two groups of weights into one, given two weightings */
void near_wblend(gammapweights *dst,
	             gammapweights *src1, double wgt1, gammapweights *src2, double wgt2);


/* A neighbour and it's weight for relative error */
typedef struct {
	struct _nearsmth *n;	/* Pointer to neigbor */
	double rw;				/* Raw neighbor interpolation weight (1.0 at point) */
	double w;				/* Neighbor interpolation weight (sums to 1.0) */
} neighb;

/* Returned point values. */
/* Note that source == input point, destination == output point */
/* (All the source values are in rotated L mapped partial gamut mapped space) */
struct _nearsmth {

  /* Public: */
	int    uflag;		/* Use flag, 0 = normal, 1 = grid surface point */
	int    gflag;		/* Gamut direction flag. 0 = not determinable, 1 = comp., 2 = exp. */
	int    vflag;		/* Vector direction flag. 0 = not determinable, 1 = comp., 2 = exp. */
						/* sv2, dv2, sd3 etc. are valid if vflag != 0 */

	/* Gamut surface mapping guide point */
	double sv[3];		/* Source value (input, cusp aligned during fwd optimization) */
	double sr;			/* Corresponding source radius from center */
	double drv[3];		/* Destination radial value (starting point & reference) */
	double drr;			/* Destination radial value radius from center */
	double dv[3];		/* Output destination value */
	double dr;			/* Output destination value radius from center */
	double div[3];		/* gam[cx]pf moderated dv[] value */
	double w1;			/* guide point weight */

	/* Gamut sub-surface mapping guide point (knee shape controlled by gamcknf & gamxknf) */
	double sv2[3];		/* Sub-surface source value */
	double dv2[3];		/* Sub-surface knee'd adjusted destination value */
	double div2[3];		/* gam[cx]pf moderated dv2[] value */
	double w2;			/* Sub-surface weight (set in nearsmth) */

	double sd3[3];		/* Deep sub-surface source & destination value */
	double w3;			/* Deep sub-surface weight */

	/* Diagnostic points */
	double csv[3];		/* Non-cusp mapped source value */

 /* Private to nearsmth: */
	gammapweights wt;	/* Weight for this point determined from original source location */
	int swap;			/* Swapped during nearest finding */
	double mL, fmL;		/* Mid point L value, filtered mid point L value */
	double m2d[3][4];   /* Tangent alignment rotation for sv[] 3D -> 2D */
	double m3d[3][4];   /* Tangent alignment rotation for 2D -> sv[] 3D */
	double _sv[3];		/* Original (non cusp aligned) source value (input) */
	double _sr;			/* Original source radius */
	double naxbf;		/* Blend factor that goes to 0.0 at white & black points. */
	double aodv[3];		/* Absolute error optimized destination value */
	double nrdv[3];		/* No relative smoothed destination value */
	double anv[3];		/* Average neighborhood target point (relative target) */

	double tdst[3];		/* Target destination on gamut */
	int    nott;		/* NZ if not a point that needs to land on gamut */
	double evect[3];	/* Accumulated extension vector direction */
	double clen;		/* Current correction length needed */
	double coff[3];		/* Correction offset */
	double rext;

	double temp[3];		/* General temporary */
	gamut *sgam;		/* Source gamut src_gam = intersection of src and img gamut gamut */
	gamut *dgam;		/* Intersected destination gamut dst_gam */
	gamut *dcgam;		/* Destination Colorspace gamut dc_gam */

	int nnd, _nnd;		/* No & size of direction neighbour list */
	neighb *nd;			/* Allocated list of neighbours */
	double nscale[3];	/* Neighborhood scale change from sv to dv */

	double dcratio;		/* Depth compression ratio */ 
	double dxratio;		/* Depth expansion ratio */ 

	int debug;
	double dbgv[3];		/* Error components va, vr, vd on last itteration */

}; typedef struct _nearsmth nearsmth;


/* Return the upper bound on the number of points that will be generated */
int near_smooth_np(
	gamut **pp_gam,		/* Return gamut that was used for points */
	gamut *sc_gam,		/* Source colorspace gamut */
	gamut *s_gam,		/* Source image gamut (== sc_gam if none) */
	gamut *d_gam,		/* Destination colorspace gamut */
	double xvra,		/* Extra vertex ratio */
	int gmult,			/* Guide point multiplier, typically 4 */
	int surfgres	   	/* surface grid point resolution, 0 for none */
);

/* Return a list of points. Call free_nearsmth() after use */
/* Return NULL on error */
nearsmth *near_smooth(
	int verb,			/* Verbose flag */
	int *npp,			/* Return the number of points returned */
	gamut *sc_gam,		/* Source colorspace gamut */
	gamut *s_gam,		/* Source image gamut (== sc_gam if none) */
	gamut *d_gam,		/* Destination colorspace gamut */
	int src_kbp,		/* Use K only black point as src gamut black point */
	int dst_kbp,		/* Use K only black point as dst gamut black point */
	double d_bp[3],		/* Destination target black point - may be NULL */
	gammapweights *wh,  /* Structure holding weights */
	double gamcknf,		/* Gamut compression knee factor, 0.0 - 1.0 */
	double gamxknf,		/* Gamut expansion knee factor, 0.0 - 1.0 */
	int    usecomp,		/* Flag indicating whether smoothed compressed value will be used */
	int    useexp,		/* Flag indicating whether smoothed expanded value will be used */
	double xvra,		/* Extra vertex ratio */
	int    mapres,		/* Target grid res for 3D RSPL, (allowing for gexp) */
	double mapsmooth,	/* Target smoothing for 3D RSPL */
	double gexp,		/* Total grid expansion ratio, none = 1.0 */
	int   surfgres,		/* Surface grid point resolution, 0 for none */
	datai map_il,		/* Return input range */
	datai map_ih,
	datao map_ol,		/* Return output range */
	datao map_oh 
);

/* Free the list of points that was returned */
void free_nearsmth(nearsmth *smp, int npp);

/* Expand the compact form of weights into the explicit form. */
int expand_weights(gammapweights out[14], gammapweights *in);
	
/* Blend two expanded groups of individual weights into one */
void near_xwblend(
gammapweights *dst,
gammapweights *src1, double wgt1,
gammapweights *src2, double wgt2
);

/* Blend three expanded groups of individual weights into one */
void near_xwblend3(
gammapweights *dst,
gammapweights *src1, double wgt1,
gammapweights *src2, double wgt2,
gammapweights *src3, double wgt3
);

/* Tweak weights according to extra cmy cusp flags or rel override */
void tweak_weights(gammapweights out[14], int dst_cmymap, int rel_oride);

#endif /* NEARSMTH_H */

