
/* 
 * Argyll Color Correction System
 *
 * Alphabetic index class.
 *
 * Author: Graeme W. Gill
 * Date:   22/8/2005
 *
 * Copyright 2005, 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.
 */

/* TTBD:
	It would be nice if it was possible to add a delimeter between
    the X and Y identifiers so that number + number or alpha + alpha
    identification was possible.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "numsup.h"
#include "alphix.h"

/* Return the alpha index for the given raw index number */
/* Return NULL on error */
static char *torawix(alphix *p, int ix) {
	int i, j, n;
	int z = 0;
	char *rv, *v;

	if (ix < 0 || ix >= p->rmct)
		return NULL;		/* Index range error */

	if ((rv = malloc((p->nd+1) * sizeof(char))) == NULL)
		return NULL;

	for (v = rv, j = 0, i = p->nd-1; i >= 0; j++, i--) {
		char c;
		n = ix / p->ds[i].b;
		ix -= n * p->ds[i].b;
		c = p->ds[i].seq[n];
		if (p->ds[i].z && z == 0 && c == '0')
			c = ' ';
		if (z != 0 || c != ' ') {
			*v++ = p->ds[i].seq[n];
			z = 1;
		}
	}
	*v = '\000';

	return rv;
}

/* Return the the raw index number given the alpha index */
/* return -1 on error */
static int fromanat(alphix *p, char *ax) {
	char *v, *tb, _tb[11];
	int cl;
	int i, k, rv = 0;

	if (p->nd > 10) {
		if ((tb = malloc((p->nd+1) * sizeof(char))) == NULL)
			return -1;	/* Malloc error */
	} else
		tb = _tb;

	/* Pack the string out to the right number of digits with spaces. */
	cl = strlen(ax);
	for (v = tb; cl < p->nd; v++, cl++)
		*v = ' ';
	strcpy(v, ax);

	/* For each digit, convert to numerical and accumulate */
	for (v = tb, i = p->nd-1; i >= 0; v++, i--) {
		for (k = 0; k < p->ds[i].n; k++) {
			if (*v == p->ds[i].seq[k]
			 || (p->ds[i].z && *v == ' ' && p->ds[i].seq[k] == '0')) {
				rv += k * p->ds[i].b;
				break;
			}
		}
		if (k >= p->ds[i].n) {
			if (tb != _tb)
				free(tb);
			return -1;		/* Unknown digit */
		}
	}

	if (tb != _tb)
		free(tb);

	return rv;
}

/* Working from the end of the string, */
/* return a pointer to the maximum number of characters */
/* that are legaly part of this alpha index. */
static char *find_start(alphix *p, char *ax) {
	int i, k;
	char *v;

	v = ax + strlen(ax) - 1;	/* start at last character */
	for (i = 0; v >= ax && i < p->nd; v--, i++) {		/* working backwards */
		for (k = 0; k < p->ds[i].n; k++) {	/* Locate */
			if (*v == p->ds[i].seq[k])
				break;				/* Found */
		}
		if (k >= p->ds[i].n)		/* Not found */
			break;
	}
	return v+1;
}

/* Convert from cooked index to raw index. */
/* Return -1 if out of range */
static int cookedtoraw(alphix *p, int ix) {
	int i;
	
	if (p->nr == 0)
		return ix;

	for (i = 0; i < p->nr; i++) {
		if (ix >= p->rs[i].c0 && ix <= p->rs[i].c1) {
			ix = ix - p->rs[i].c0 + p->rs[i].r0;
			return ix;
		}
	}
	return -1;
}

/* Convert from raw index to cooked index. */
/* Return -1 if out of range */
static int rawtocooked(alphix *p, int ix) {
	int i;
	
	if (p->nr == 0)
		return ix;

	for (i = 0; i < p->nr; i++) {
		if (ix >= p->rs[i].r0 && ix <= p->rs[i].r1) {
			ix = ix - p->rs[i].r0 + p->rs[i].c0;
			return ix;
		}
	}
	return -1;
}

/* Return the maximum possible length of count */
static int alphix_maxlen(alphix *p) {
	return p->cmct;
}

/* Return the alpha index for the given index number (0 based) */
/* Return NULL on error */
/* (Free after use) */
static char *alphix_aix(alphix *p, int ix) {
	if ((ix = cookedtoraw(p, ix)) < 0)
		return NULL;
	return torawix(p, ix);
}

/* Return the index number for the given alpha index */
int alphix_nix(alphix *p, char *ax) {
	int rv;
	if ((rv = fromanat(p, ax)) < 0)
		return -1;
	return rawtocooked(p, rv);
}

/* Destroy ourselves */
static void
alphix_del(alphix *p) {
	int i;
	for (i = 0; i < p->nd; i++)
		free(p->ds[i].seq);
	free (p->ds);
	free (p->rs);
	free (p);
}

/* Constructor: */
alphix *new_alphix(char *pattern) {
	alphix *p;
	char *pp = pattern;
	int i;

	if ((p = (alphix *)calloc(1,sizeof(alphix))) == NULL)
		error("alphix: malloc failed");

	p->maxlen = alphix_maxlen;
	p->aix    = alphix_aix;
	p->nix    = alphix_nix;
	p->del    = alphix_del;

	/* We now need to parse the pattern to setup our index sequence: */
	/* For all digits */
	for (p->nd = 0; ; p->nd++) {

		if (*pp == '\000' || *pp == ';') {
			break;
		}

		/* We are starting a new digit sequence */
		if (p->nd >= p->_nd) {		/* Allocate space for current digit */
			p->_nd = 2 + p->_nd;
			if ((p->ds = (dig *)realloc(p->ds, p->_nd * sizeof(dig))) == NULL)
				error ("alphix: realloc failed");
		}

		p->ds[p->nd].n = 0;
		p->ds[p->nd].seq = NULL;
		p->ds[p->nd]._n = 0;
		p->ds[p->nd].z = 0;

		/* For all symbols in this digit */
		for (p->ds[p->nd].n = 0; ;) {
			char c, c1, c2;

			/* We are adding another character to the sequence */
			if (*pp == '\000' || *pp == ';' || *pp == ',')
				break;						/* Done this digit */
			if (pp[1] == '-' && pp[2] != '\000' && pp[2] != ';' && pp[2] != ',') {
				c1 = pp[0];
				c2 = pp[2];
				pp += 3;
			} else {
				c1 = c2 = *pp;
				pp++;
			}
			if (c1 == '@') {
				c1 = '0';
				p->ds[p->nd].z = 1;
			}
			if (c2 == '@') {
				c2 = '0';
				p->ds[p->nd].z = 1;
			}

			/* Expand digit sequence */
			for (c = c1; c <= c2; c++) {
	
				if (p->ds[p->nd].n >= p->ds[p->nd]._n) {	/* Allocate space for next character */
					p->ds[p->nd]._n = 20 + p->ds[p->nd]._n;
					if ((p->ds[p->nd].seq = (char *)realloc(p->ds[p->nd].seq, p->ds[p->nd]._n * sizeof(char))) == NULL)
						error ("alphix: realloc failed");
				}
				p->ds[p->nd].seq[p->ds[p->nd].n++] = c;
			}
		}
		if (*pp == '\000' || *pp == ';')
			continue;
		pp++;

	}

	/* Compute the native maximum index count */
	for (p->rmct = 1, i = 0; i < p->nd; i++) {
		p->ds[i].b = p->rmct;
		p->rmct *= p->ds[i].n;
	}

	/* Search for valid ranges */
	if (*pp == ';') {
		char *v, *tb, _tb[11];

		pp++;
		if (p->nd > 10) {
			if ((tb = malloc((p->nd+1) * sizeof(char))) == NULL)
				error ("alphix: malloc failed");
		} else
			tb = _tb;

		/* For each range */
		for (p->nr = 0; ; p->nr++) {

			if (*pp == '\000' || *pp == ';')
				break;

			/* We are adding a new range */
			if (p->nr >= p->_nr) {		/* Allocate space for current range */
				p->_nr = 2 + p->_nr;
				if ((p->rs = (rngsq *)realloc(p->rs, p->_nr * sizeof(rngsq))) == NULL)
					error ("alphix: realloc failed");
			}

			/* Locate the end of the range start */
			for(v = tb; *pp != '\000' && *pp != '-' && *pp != ','; v++, pp++)
				*v = *pp;
			*v = '\000';
			p->rs[p->nr].r0 = p->rs[p->nr].r1 = fromanat(p, tb);
			if (p->rs[p->nr].r0 < 0)
				error("alphix: range start definition error on '%s'",tb);


			if (*pp != '-') {		/* oops - bad definition */
				error("alphix: range definition error - missing '-'");
			}

			/* Locate the end of the range end */
			for(v = tb, pp++; *pp != '\000' && *pp != ','; v++, pp++)
				*v = *pp;
			*v = '\000';
			p->rs[p->nr].r1 = fromanat(p, tb);
			if (p->rs[p->nr].r1 < 0)
				error("alphix: range end definition error on '%s'",tb);

			if (p->rs[p->nr].r1 < p->rs[p->nr].r0)	/* Hmm */
				error("alphix: range definition error, end < start ");

			/* Compute cooked index range */
			p->rs[p->nr].c0 = 0;
			p->rs[p->nr].c1 = p->rs[p->nr].r1 - p->rs[p->nr].r0;
			if (p->nr > 0) {
				int ofs = p->rs[p->nr-1].c1+1;
				p->rs[p->nr].c0 += ofs;
				p->rs[p->nr].c1 += ofs;
			}

			if (*pp == '\000' || *pp == ';')
				continue;
			pp++;
		}

		if (tb != _tb)
			free(tb);
	}

	/* Compute cooked actual range */
	p->cmct = p->rmct;
	if (p->nr > 0) {
		p->cmct = p->rs[p->nr-1].c1+1;
	}

#ifdef NEVER
	// ###########################################################
	/* Debug stuff */
	printf("~1 There are %d digits, %d ranges, raw max = %d, cooked max = %d\n",
		p->nd,p->nr,p->rmct, p->maxlen(p));

	/* Digit sequences */
	for (i = 0; i < p->nd; i++) {
		printf(" ~1 Digit %d has %d symbols\n",i,p->ds[i].n);
		for (j = 0; j < p->ds[i].n; j++)
			printf("  ~1 symbol %d = '%c'\n",j,p->ds[i].seq[j]);
	}

	/* Ranges */
	for (i = 0; i < p->nr; i++) {
		printf(" ~1 Range %d is raw %d - %d, cooked %d - %d\n",
		       i,p->rs[i].r0, p->rs[i].r1, p->rs[i].c0, p->rs[i].c1);
	}

	/* Itterate the sequence */
	for (i = 0; i < p->cmct; i++) {
		char *v;
		v = p->aix(p, i);
		printf("ix %d -> '%s'\n",i,v);
		free(v);
	}
	// ###########################################################
#endif /* NEVER */

	return p;
}


/* ==================================================================== */
/* Utility function: */
/* Given the strip and patch alphix objects, and order flag, */
/* Return a patch location. Free the returned string after use. */
/* Return NULL on error */
char *patch_location(
	alphix *saix,		/* Strip alpha index object */
	alphix *paix,		/* Patch alpha index object */
	int ixord,			/* Index order, 0 = strip then patch */
	int six,			/* Strip index */
	int pix				/* Patch index */
) {
	char *sl, *pl, *rv;
	int ll;

	if ((sl = saix->aix(saix, six)) == NULL)
		return NULL;

	if ((pl = paix->aix(paix, pix)) == NULL) {
		free (sl);
		return NULL;
	}
	ll = strlen(sl) + strlen(pl) + 1;
	if ((rv = malloc(ll * sizeof(char))) == NULL) {
		free (pl);
		free (sl);
		return NULL;
	}

	if (ixord == 0) {
		strcpy (rv, sl);
		strcat (rv, pl);
	} else {
		strcpy (rv, pl);
		strcat (rv, sl);
	}
	return rv;
}

/* ==================================================================== */
/* Utility function: */
/* Given the strip and patch alphix objects, and order flag, */
/* and a corresonding patch location string, return an index */
/* number suitable for sorting location strings. */
/* The sort order is in order of strips, then patches within each strip */
/* Return -1 on error */
int patch_location_order(
	alphix *saix,		/* Strip alpha index object */
	alphix *paix,		/* Patch alpha index object */
	int ixord,			/* Index order, 0 = strip then patch */
	char *_ax			/* Patch location string */
) {
	char *ax;			/* Copy of input string */
	char *v;
	alphix *rh;			/* Least significant, right hand alphix */
	alphix *lh;			/* Most significant, left hand alphix */
	int ri, li;			/* Right hand and left hand index numbers */

	if ((ax = malloc(strlen(_ax)+1)) == NULL)
		return -1;
	strcpy(ax,_ax);

	if (ixord == 0) {
		lh = saix;		/* Strip is left hand */
		rh = paix;		/* Patch is right hand */
	} else {
		rh = saix;		/* Strip is right hand */
		lh = paix;		/* Patch is left hand */
	}

	/* We need to identify the boundary between */
	/* the right hand and left hand indexes. */
	/* We assume that the sequences are distinguishable ... */
	v = find_start(rh, ax);

	ri = rh->nix(rh, v);
	*v = '\000';
	li = lh->nix(lh, ax);
	free(ax);
	if (ri < 0 || li < 0)
		return -1;

	if (ixord == 0)		/* Strip is left hand */
		return li * rh->cmct + ri;
	else
		return ri * lh->cmct + li;
}










