#ifndef GAMUT_H
#define GAMUT_H

/* 
 * gamut
 *
 * Gamut support routines.
 *
 * Author:  Graeme W. Gill
 * Date:    9/3/2000
 * Version: 1.00
 *
 * Copyright 2000-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.
 */



/*
	Note that the input and output are expected to be Lab style
	color coordinates, and that L->Z, a->X, b->Y.

*/

#include "../h/llist.h"

#define BSPDEPTH 100		/* Maximum BSP tree depth */

#define PFARNDIST  0.1			/* Positive (inwards) far "near" distance */
#define NFARNDIST -200.0		/* Negative (outwards) far "near" distance */
#define MFARNDIST PFARNDIST		/* Minimum (absolute) of the two */

#define MAXGAMN 10				/* Maximum gamut point neighbors returned */
#define NSLOTS 6				/* Number of maximum direction slots */

/* ------------------------------------ */
#define NODE_STRUCT							\
	int tag;		/* Type of node, 1 = vertex, 2 = quad */	\
	double w, h;	/* longitude width, latitude height of this box */ \
	double hc, vc;	/* longitude center, latitude center of this box */ \

struct _gnode {
	NODE_STRUCT
}; typedef struct _gnode gnode;

/* ------------------------------------ */
/* Vertex node */
struct _gvert {
	NODE_STRUCT
	int rc;				/* reference count */
	struct _gvert *ul;	/* Unused list */

	int n;			/* Index number of vertex */
	int sn;			/* Set Index number of vertex */
	int tn;			/* Triangulated Index number of vertex */
	int f;			/* Flag value */
#define GVERT_NONE 0x0000		/* No flags */
#define GVERT_SET  0x0001		/* Value has been set */
#define GVERT_TRI  0x0002		/* Vertex has been added to triangulation (Exclsv with _INSIDE) */
#define GVERT_INSIDE  0x0004	/* Vertex is inside the log hull (Exclusive with _TRI) */
#define GVERT_ISOS  0x0008		/* Intersecting gamuts "outside other gamut" flag */
#define GVERT_ESTP  0x0010		/* Non-fake establishment point */
#define GVERT_FAKE  0x0020		/* Fake establishment point */
	int k0;			/* k0 direction reference count */

	double p[3];		/* Point in xyz rectangular coordinates, absolute */
	double r[3];		/* Radial coordinates */
	double lr0;			/* log scaled r[0] */
	double sp[3];		/* Point mapped to surface of unit sphere, relative to center */
	double ch[3];		/* Point mapped for convex hull testing, relative to center */

	int as;				/* Assert checking flag, compdstgamut flag */
}; typedef struct _gvert gvert;

/* ------------------------------------ */
/* Quadtree node */
struct _gquad {
	NODE_STRUCT
	gnode *qt[4][NSLOTS];

	/* Child nodes, NULL if none, */
	/* Nodes are ordered: */
	/* --------- */
	/* | 2 | 3 | */
	/* --------- */
	/* | 0 | 1 | */
	/* --------- */
	/* and each node contains NSLOTS slots. */
	/* If the quadtree recurses, the first slot containts another */
	/* quadtree, otherwise the slots contain pointers to the */
	/* four best verticies in the various directions. */

}; typedef struct _gquad gquad;

/* ------------------------------------ */

/* Common base class of BSP nodes */
#define BSP_STRUCT																			\
	int tag;		/* Type of node, 1 = bsp node, 2 = triangle, 3 = list */				\
	double rs0, rs1;	/* min/max radius squared from origin of all contained triangles */ 

struct _gbsp {
	BSP_STRUCT
}; typedef struct _gbsp gbsp;

/* ------------------------------------ */

/* A BSP tree decision node */
struct _gbspn {
	BSP_STRUCT
	int n;					/* Serial number */
	double pe[4];			/* Plane equation values (center relative) */
	struct _gbsp  *po;		/* Positive branch */
	struct _gbsp  *ne;		/* Negative branch */
}; typedef struct _gbspn gbspn;

/* ------------------------------------ */

/* A BSP tree triangle list node */
struct _gbspl {
	BSP_STRUCT
	int n;					/* Serial number */
	int nt;					/* Number of triangles in the list */
	struct _gtri  *t[];	/* List of triangles - allocated with struct */
}; typedef struct _gbspl gbspl;

/* ------------------------------------ */

/* A triangle in the surface mesh */
struct _gtri {
	BSP_STRUCT
	int n;			/* Serial number */
	struct _gvert *v[3];		/* Verticies in cw order */
	struct _gedge *e[3];		/* Edges v[n] - v[n+1] */
	int           ei[3];		/* Index within edge structure of this triangle [0..1] */

	double pe[4];		/* Vertex plane equation (absolute) */
						/* (The first three elements is the unit normal vector to the plane, */
						/*  which points inwards) */
	double che[4];		/* convex hull testing triangle plane equation (relative) */
	double spe[4];		/* sphere mapped triangle plane equation (relative) */
	double ee[3][4];	/* sphere sp[] Edge triangle plane equations for opposite edge (relative) */

	int sort;			/* lookup: Plane sorting result for each try */
	int bsort;			/* lookup: Current best tries sort */

	unsigned int touch;	/* nn: Per value touch count */
	double mix[2][3];	/* nn: Bounding box min and max */

	double area;		/* Area - computed by nssverts() */
	int    ssverts;		/* Number of stratified sampling verts needed - computed by nssverts() */

	LINKSTRUCT(struct _gtri);	/* Linked list structure */
}; typedef struct _gtri gtri;

/* ------------------------------------ */

/* An edge shared by two triangle in the mesh */
struct _gedge {
	int n;			/* Serial number */
	struct _gvert *v[2];	/* Verticies of edge */
	struct _gtri  *t[2];	/* Triangles edge is part of */
	int           ti[2];	/* record of indexes of edge within the triangles [0..2]*/
	double        re[4];	/* Radial edge plane equation (relative) */

	int as;				/* Assert checking flag */

	LINKSTRUCT(struct _gedge);	/* Linked list structure */
}; typedef struct _gedge gedge;

/* ------------------------------------ */

/* The gamut nearest neighbor search structure */
struct _gnn {
	struct _gamut *s;		/* Base gamut object */
	int n;					/* Number of points stored */
	gtri **sax[3 * 2];		/* Sorted axis pointers, one for each direction */
	unsigned int tbase;		/* Touch base value for this pass */
	unsigned int ttarget;	/* Touch target value for this pass */
}; typedef struct _gnn gnn; 

/* ------------------------------------ */

/* A vector intersction point */
struct _gispnt {
	double ip[3];			/* Intersecion Point */
	double pv;				/* Parameter value at intersection */
	int dir;				/* Direction: 1 = into gamut, 0 = out or gamut */
	int edge;				/* Edge:      2 = no isect, 1 = on edge, 0 = not on edge */
	gtri *tri;				/* Pointer to intersection triangle */
}; typedef struct _gispnt gispnt; 

/* Gamut object */
struct _gamut {

/* Private: */
	double sres;		/* Surface triangle resolution */
	int isJab;			/* nz if Jab CIECAM02 type space rather than L*a*b* */
	int isRast;			/* nz if raster file point cloud, else colorspace */
						/* (This affects convex hull filtering) */
	double cent[3];		/* Gamut center for radial conversion. Default 50.0,0,0 */
						/* Must be same to compare radial values. */

	int nv;				/* Number of verticies used out of allocation */
	gvert *ul;			/* Linked list of unused verticies */
	int na;				/* Number of verticies allocated */
	int nsv;			/* Number of verticies that have been set */
	int ntv;			/* Number of verticies used in triangulation */
	gvert **verts;		/* Pointers to allocated verticies */
	int read_inited;	/* Flag set if gamut was initialised from a read */
	int lu_inited;		/* Flag set if radial surface lookup is inited */
	int ne_inited;		/* Flag set if nearest lookup is inited */
	int cu_inited;		/* Flag set if cusp values inited and trustworthy */
	int nofilter;		/* Flag, skip segmented maxima filtering */
	int no2pass;		/* Flag, do only one pass of convex hull */
	int doingfake;		/* Internal transient state */
	int pass;			/* Pass number for multi-pass */
	double logpow;		/* Convex hull compression power (default 0.25) */

	gquad *tl, *tr;		/* Top left and quadtree elements */

	gtri *tris;			/* Surface triangles linked list */
	gedge *edges;		/* Edges between the triangles linked list */

	gbsp  *lutree;		/* Lookup function BSP tree root */
	gnn   *nns;			/* nearest neighbor acceleration structure */

	int cswbset;		/* Flag to indicate that the cs white & black points are set */
	double cs_wp[3];	/* Color spaces white point */
	double cs_bp[3];	/* Color spaces black point */
	double cs_kp[3];	/* Color spaces K only black point */
	int gawbset;		/* Flag to indicate that the gamut white & black points are set */
	double ga_wp[3];	/* Gamut white point */
	double ga_bp[3];	/* Gamut black point */
	double ga_kp[3];	/* Gamut K only black point */

	int dcuspixs;		/* Cusp we're up to */
	double dcusps[6][3];/* Entered cusp values to setcusps(, 3, ) */
	double cusps[6][3];	/* Cusp values for red, yellow, green, cyan, blue & magenta */ 
						/* if cu_inited nz */

	double mx[3], mn[3];	/* Range covered by input points */

	double xvra;		/* Extra vertex ratio - set/used by nssverts() */
	int    ssnverts;	/* total ss verticies - set/used by nssverts() */
	int	   ssvertn;		/* Number of verts created for current triangle */
	sobol *ss;			/* Sibol ss generator currently being used */

	gtri *nexttri;		/* Context for getnexttri() */

/* Public: */
	/* Methods */
	void (*del)(struct _gamut *s);						/* Free ourselves */

	gvert *(*expand)(struct _gamut *s, double in[3]);		/* Expand the gamut surface */

	int (*getisjab)(struct _gamut *s);	/* Return the isJab flag value */

	int (*getisrast)(struct _gamut *s);	/* Return the isRast flag value */

	void (*setnofilt)(struct _gamut *s);	/* Disable segmented maxima filtering */

	void (*getcent)(struct _gamut *s, double *cent);	/* Return the gamut center location */

	void (*getrange)(struct _gamut *s, double *min, double *max);	/* Return the gamut range */

	double (*getsres)(struct _gamut *s);	/* Return the surface resolution */

	int (*compatible)(struct _gamut *s, struct _gamut *t);	/* Return the nz if compatible gamuts */

	int (*nrawverts)(struct _gamut *s); /* Return the number of raw verticies */

	int (*getrawvert)(struct _gamut *s, double pos[3], int ix);
									/* Return the raw verticies location */

	int (*nraw0verts)(struct _gamut *s); /* Return the number of raw verticies in */
	                                    /* the radial maxima direction*/

	int (*getraw0vert)(struct _gamut *s, double pos[3], int ix);
									/* Return the raw 0 direction verticies location */

	int (*nverts)(struct _gamut *s); /* Return the number of surface verticies */

	int (*getvert)(struct _gamut *s, double *rad, double pos[3], int ix);
								/* Return the surface triangle verticies location and radius */
								/* start at 0, and returns value is next index or -1 if last */

	int (*nssverts)(struct _gamut *s, double vpua);
								/* Return the number of stratified sampling surface verticies, */
								/* for the given verticies per unit area parameter. */

	int (*getssvert)(struct _gamut *s, double *rad, double pos[3], double norn[3], int ix);
								/* Return the stratified sampling surface verticies */
								/* location and radius. nssverts() sets vpua */
								/* norm will contain the normal of the triangle */
								/* the point originates from. */

	void (*startnexttri)(struct _gamut *s); /* Reset indexing through triangles for getnexttri() */

	int (*getnexttri)(struct _gamut *s, int v[3]);
								/* Return the next surface triange, nz on no more */
								/* Index v[] corresponds to order of getvert() */ 

	double (*volume)(struct _gamut *s);
								/* Return the total volume enclosed by the gamut */

	int (*intersect)(struct _gamut *s, struct _gamut *s1, struct _gamut *s2);
								/* Initialise this gamut with the intersection of the */
								/* the two given gamuts. */

#ifdef NEVER	/* Deprecated */
	int (*expandbydiff)(struct _gamut *s, struct _gamut *s1, struct _gamut *s2, struct _gamut *s3, int docomp);
								/* Initialise this gamut with a gamut which is s1 expanded */
								/* (but never reduced) by the distance from s2 to s3. */
								/* If docomp != 0, make gamut trace s3 if it's smaller than s1 */ 
#endif

	int (*compdstgamut)(struct _gamut *s, struct _gamut *img, struct _gamut *src,
	                    struct _gamut *dst, int docomp, int doexpp, struct _gamut *nedst,
	                    void (*cvect)(void *cntx, double *p2, double *p1), void *cntx);
								/* Initialise this gamut with a destination mapping gamut. */

	double (*radial)(struct _gamut *s, double out[3], double in[3]);
								/* return point on surface in same radial direction. */
								/* Return the radial radius to the surface point in */
								/* colorspace units. out[] may be NULL */

	double (*nradial)(struct _gamut *s, double out[3], double in[3]);
								/* return point on surface in same radial direction, */
								/* and normalised radial radius. This will be <= 1.0 if within */
								/* gamut, and > 1.0 if out of gamut. out[] may be NULL */

	void (*nearest)(struct _gamut *s, double out[3], double in[3]);
	                          /* return point on surface closest to input */

	void (*nearest_tri)(struct _gamut *s, double out[3], double in[3], gtri **ctri);
	                          /* return point on surface closest to input & triangle */

	int (*vector_isect)(struct _gamut *s, double *p1, double *p2, double *min, double *max,
	                                                               double *mint, double *maxt,
	                                                               gtri **mntri, gtri **mxtri);
							/* Compute the intersection of the vector p1->p2 with */
							/* the gamut surface. min is the intersection in the p1 direction, */
							/* max is intersection in the p2 direction. mint and maxt are */
							/* the parameter values at the two intersection points, a value of 0 */
							/* being at p1 and 1 being at p2. mintri and maxtri return the */
							/* intersection triangles.  min, max, mint, maxt, */
							/* mintri & maxtri  may be NULL */
							/* Return 0 if there is no intersection with the gamut. */

	int (*vector_isectns)(struct _gamut *s, double *p1, double *p2, gispnt *lp, int ll); 
							/* Compute all the intersection pairs of the vector p1->p2 with */
							/* the gamut surface.  lp points to an array of ll gispnt to be */
							/* filled in. If the list is too small, intersections will be */
							/* arbitrarily ignored. */
							/* Return the number of intersections set in list. Will be even. */
							/* These will all be in then out pairs in direction p1->p2. */

	void (*setwb)(struct _gamut *s, double *wp, double *bp, double *kp);
							/* Define the colorspaces white, black and K only black points. */
							/* May be NULL if unknown, and will be set to a default. */
	                        /* Same colorspace as gamut */

	int (*getwb)(struct _gamut *s, double *cswp, double *csbp, double *cskp,
	                               double *gawp, double *gabp, double *gakp);
							/* Get the colorspace and gamut white, black and K only black points. */
							/* Return non-zero if not possible. Same colorspace as gamut */

	void (*setcusps)(struct _gamut *s, int flag, double in[3]);	/* Set potential cusp values. */
																/* flag == 0 = reset, */
																/* flag == 1 = add general point, */
																/* flag == 3 = add definite point, */
																/* flag == 2 = finish */

	int (*getcusps)(struct _gamut *s, double cusps[6][3]);		/* Get the cusp values for */
																/* red, yellow, green, cyan, */
																/* blue & magenta. Return */
																/* nz if no cusps available. */

																/* Following return nz on error: */
	int (*write_vrml)(struct _gamut *s, char *filename,
	                              int doaxes, int docusps); /* Write to a VRML .wrl file */
	int (*write_gam)(struct _gamut *s, char *filename);		/* Write to a CGATS .gam file */
	int (*read_gam)(struct _gamut *s, char *filename);		/* Read from a CGATS .gam file */

	int (*write_trans_vrml)(struct _gamut *s, char *filename, /* Write transformed VRML .wrl */
		int doaxes, int docusps, void (*transform)(void *cntx, double out[3], double in[3]), /* with xform */
		void *cntx);

}; typedef struct _gamut gamut;

/* Creator */
gamut *new_gamut(double sres, int isJab, int isRast);	/* Surface resolution, 0.0 = default */

/* Utility */
void gamut_rect2radial(gamut *s, double out[3], double in[3]);
void gamut_radial2rect(gamut *s, double out[3], double in[3]);
void gamut_Lab2RGB(double *in, double *out);
extern double gam_hues[2][7];	/* Generic Lab & Jab color hues in degrees */


#endif /* GAMUT_H */

