/*
 *		K A R Y S U B S 2 . C  --  More karyotype interaction
 *
 *			 Jim Piper
 *           Image Recognition Systems
 *           720 Birchwood Boulevard
 *           Birchwood Science Park
 *           Warrington
 *           Cheshire
 *           WA3 7PX
 *
 *	Date:	 23rd January 1984
 *
 *	Copyright (c) and intellectual property rights Image Recognition Systems (1988)
 *
 *
 *  Modifications
 *
 *  13 Oct 1994		JimP            make MAXCLASS dependent on species: alc->scrf->undefclass
 *  25 Feb 1992		Joanne		Added extra test in firstclass() to avoid
 *					empty karyogram locations while there are
 *					chrom. of that class in the stack line
 *  10 Jun 1991		ih			Check for valid mol && mol->plist in various places
 *	 7 Feb 1991		CAS			voids
 *  24 Jul 1989		SEAJ		Removed section in classify() which sets all 
 *								objects to be chromosomes with conf = 100.
 *   4 Jul 1989		SEAJ		Added new functions newCpgroup & newpgroup,
 *								newclass nolongewr sets pgroup.
 *	 2 Dec 1988		CAS			Make firstclass use plist if set up
 *								displayall,hidenstackcount,kdisplay,unkdisplay
 *								remove_class here in effort to
 *								reduce duplication
 *	 2 Dec 1988		CAS/SEAJ	Moved newclass here from karysubs1 (kyy reasons)
 *								and changed params to it and firstclass and swapclass
 *								Wrote a proper firstclass
 *	02-09-87		jimp@IRS	Tsize in dispmark
 *  GJP		05-03-87	Use kary_exit not exit
 *	jimp	05-02-87	NSTACKPOS removed to chromanal.h
 *	jimp	04-02-87	stacktop() routine added for top-of-stack.
 *	jimp	03-02-87	tellstack() returns stackocc value cleanly.
 *	jimp	17-12-86	Noise/composites not confirmed by operator must
 *						have otype/Cotype = CHROMOSOME, otherwise they
 *						will disappear after writing to disc.
 *	jp		09-12-86	Merge of karylib and kyy versions into karylib
 *	jp		13-11-86	Merge of karylib and kyy versions
 *	jp		12-11-86	includes
 *	JP		28-10-86	Non-chromosome active objects classified as
 *						abnormal in "classify()" (result of operator not
 *						confirming machine object typing).
 *	JP		17-10-86	display.h used
 *  12/5/86  GJP   Position of Marker
 *  27/1/86		Denis		stack notes number in stackocc
 */

#include <stdio.h>
#include <allcontr.h>
#include <wstruct.h>
#include <interact.h>
#include <chromanal.h>
#include <kcontrol.h>
#include <display.h>
#include <dispform.h>

extern struct allkontr *alc;
int stackocc[NSTACKPOS+1];
extern struct kclasscont kcc[];

/*
 * get a stack location
 */
stack(n) /* and note number */
{
	register int i;
	for (i=1; i<=NSTACKPOS; i++) {
		if (stackocc[i] <= 0) {
			stackocc[i] = n+1;
			return(i);
		}
	}
	fprintf(stderr,"stack(): object stack overflow\n");
	kary_exit(12);
}

/*
 * release a stack location
 */
unstack(i)
{
	stackocc[i] = -1;
}

/*
 * report a stack location
 */
int tellstack(i)
{
	return(stackocc[i]);
}

/*
 * report highest used stack location
 */
int stacktop()
{
	register int i,iused;
	iused = 0;
	for (i=1; i<=NSTACKPOS; i++)
		if (stackocc[i] > 0)
				iused = i;
	return(iused);
}



/*
 * make a mark to draw attention to a particular chromosome
 */
dispmark(f)
struct pframe *f;
{
	static char *mark = "^";
	int x,y;
	findtsize(&x,&y);
	colour(OVERLAY1);
	tsize(120,100);
	dispstring(mark,f->dx-60,f->dy-264);
	dispstring(mark,f->dx-60,f->dy-272);
	dispstring(mark,f->dx-60,f->dy-280);
	tsize(x,y);
}


/*
 * read chromosome class, one of figure 1-22, letter A, X, Y.
 */
getclass(in)
FILE *in;
{
	register int i, class;
	while ((class = getc(in)) == ' ')
		;
	class &= 127;
	if (class == 'A' || class == 'a')
		return(24);
	else if (class == 'X' || class == 'x')
		return(22);
	else if (class == 'Y' || class == 'y')
		return(23);
	else if (class < '0' || class > '9')
		return(-1);
	else {
		class -= '0';
		i = getc(in);
		if (i < '0' || i > '9') {
			ungetc(i,in);
			return(class-1);
		} else
			return(10*class + i-'0'-1);
	}
}


/*
 * read chromosome position in a class -
 * specified by upper or lower case letter
 */
getpos(in)
FILE *in;
{
	int pos;
	while ((pos = getc(in)) == ' ')
		;
	pos &= 127;
	if (pos >= 'a' && pos <= 'z')
		return(pos-'a');
	else if (pos >= 'A' && pos <= 'Z')
		return(pos-'A');
	else
		return(-1);
}


/*
 * swap the class of two objects in identifying structures k1, k2.
 */
swapclass(k1,k2,kcont,disppos)
struct kident *k1, *k2;
struct kdisppos *disppos;
{
	int c1, c2;
	c1 = k1->kclass;
	c2 = k2->kclass;
	remove_class(k1);
	remove_class(k2);
	newCpgroup(k2, c1, kcont);
	newclass(k2,c1,kcont,disppos);
	newCpgroup(k1, c2, kcont);
	newclass(k1,c2,kcont,disppos);
}

/*
 *	F I R S T C L A S S --
 *
 * This routine looks to see if data is previusly karyotyped and if
 *	so tries to put things back in the same place
 */
firstclass(o,kcont,disppos)
struct kcontrol *kcont;
struct	kdisppos *disppos;
{
	struct kident kid;
	int class;
	
	kid.no = o;
	if (kcont->mol&&kcont->mol[o]&&kcont->mol[o]->plist) {
		if (kcont->mol[o]->plist->Cpgroup)
			class = kcont->mol[o]->plist->Cpgroup;
		else
			class = kcont->mol[o]->plist->pgroup;
		if ((kcont->mol[o]->plist->disppos > 0) && 
		    (kcont->mol[o]->plist->disppos < disppos[class-1].npos))
			reposition(&kid,class-1,kcont,disppos);
		else
			newclass(&kid,class-1,kcont,disppos);
	}
}



/*
 *	R E P O S I T I O N  --
 *
 * re-insert object from previously classified file in specified class
 * at specified position
 */
reposition(k,c,kcont,disppos)
register struct kident *k;
struct	kcontrol *kcont;
struct	kdisppos *disppos;
{
	register struct kclasscont *kc;
	register int i;
	register struct chromplist *plist;
	
	if (kcont->mol&&kcont->mol[k->no]&&kcont->mol[k->no]->plist)
		plist=kcont->mol[k->no]->plist;
	else
		return(0);
	if ((c < 0) || (c > alc->scrf->undefclass)) c = alc->scrf->undefclass;	/* Abnormal if silly class */
	kc = kcc + c;
	k->kclass = c;
	/*
	 *	Make sure there is somewhere to put it - if not newclass can cope
	 */
	if ((plist->disppos >= MAXCHILD) || (kc->kno[plist->disppos -1] != -1))
		newclass(k,c,kcont,disppos);
	else {
		if (kc->maxn < plist->disppos)
			kc->maxn = plist->disppos;
		kc->n++;
		k->pos = plist->disppos;
		kc->kno[k->pos-1] = k->no;
		/*
		 * check whether a stack position needs to be allocated
		 */
		if (k->pos > disppos[c].npos)
			k->stackpos = stack(k->no);
		else
			k->stackpos = -1;
		kc->sp[k->pos-1] = k->stackpos;
	}
}
/*
 * 	N E W C P G R O U P
 */
newCpgroup(k,c,kcont)
register struct kident *k;
int		c;
struct	kcontrol *kcont;
{
	if (kcont->mol&&kcont->mol[k->no]&&kcont->mol[k->no]->plist) {
		if ((c < 0) || (c > alc->scrf->undefclass))
			c = alc->scrf->undefclass;						/* Abnormal if silly class */
		kcont->mol[k->no]->plist->Cpgroup = c+1;
	}
}

/*
 * N E W P G R O U P
 */
newpgroup(k,c,kcont)
register struct kident *k;
int		c;
struct	kcontrol *kcont;
{
	if (kcont->mol&&kcont->mol[k->no]&&kcont->mol[k->no]->plist) {
		if ((c < 0) || (c > alc->scrf->undefclass)) 
			c = alc->scrf->undefclass;					/* Abnormal if silly class */
		kcont->mol[k->no]->plist->pgroup = c+1;
	}
}
/*
 *	N E W C L A S S -- insert object in specified class
 */
newclass(k,c,kcont,disppos)
register struct kident *k;
int		c;
struct	kcontrol *kcont;
struct	kdisppos *disppos;
{
	register struct kclasscont *kc;
	register int i;

	if ((c < 0) || (c > alc->scrf->undefclass)) c = alc->scrf->undefclass;	/* Abnormal if silly class */
	/*
	 * add to new class.  3 cases:
	 *	- nothing in new class - no problem
	 *	- number of object in new class == high water mark.
	 *	  add object, increase high water mark.
	 *	- fewer objects than high water mark.  Find first empty slot.
	 */
	kc = kcc + c;
	k->kclass = c;
	if (kc->n == 0) {
		kc->maxn = k->pos = kc->n = 1;
		kc->kno[0] = k->no;
	} else if (kc->n == MAXCHILD) {
		fprintf(stderr, "Newclass: class %d contents structure full\n", c+1);
		return(0);		/* SOMETHING WILL BE LOST HERE. */
	} else if (kc->n == kc->maxn) {
		kc->kno[kc->n] = k->no;
		kc->n++;
		kc->maxn++;
		k->pos = kc->n;
	} else {
		for (i=0; i<kc->maxn; i++)
			if (kc->kno[i] < 0) {
				k->pos = i+1;
				kc->kno[i] = k->no;
				break;
			}
		kc->n++;
	}
	if (kcont->mol&&kcont->mol[k->no]&&kcont->mol[k->no]->plist)
		kcont->mol[k->no]->plist->disppos = k->pos;

	/*
	 * check whether a stack position needs to be allocated
	 */
	if (k->pos > disppos[c].npos)
		k->stackpos = stack(k->no); /* get stack pos, give number */
	else
		k->stackpos = -1;
	kc->sp[k->pos-1] = k->stackpos;
}

/*
 * C L A S S I F Y -- if object active, but not classified make it abnormal.
 *
 */
classify(kc)
struct kcontrol *kc;
{
	register struct chromplist *plist;
	register i,j,ac;
	
	for (i=0; i<kc->maxnumber; i++) {
		ac = kc->acl[i];
		ac &= ACTIVE;
		if ((ac!=0)&&kc->mol&&kc->mol[i]&&kc->mol[i]->plist) {
			plist = kc->mol[i]->plist;
			if (plist->otype <= CHROMOSOME) {
				if (plist->pgroup <= 0) {
					fprintf(stderr,"unclassified CHR %d called ABNORMAL\n",i);
					plist->pgroup = 25;
				}
			} else {
				/*
				 * This is likely to be a composite or noise as determined
				 * by the feature measurement but not confirmed by operator
				 */
				plist->pgroup = 25;
			}
			/* 
			 * Commented out as these should not be set here...SEAJ
			 * plist->Cotype = plist->otype = CHROMOSOME;
			 * plist->otconf = 100; 
			 */
		}
	}
}


/*
 *	K D I S P L A Y  --
 *
 * display a chromosome at the position implied by the
 * identification structure "k", which includes the chromosome
 * class and an ordinal within the class among other information.
 * Insert suitable frame values in kcont->ivf frame array.
 */
kdisplay(k,kcont)
register struct kident *k;
register struct kcontrol *kcont;
{
	struct chromosome *obj = kcont->mol[k->no];
	register struct chromplist *plist = obj->plist;
	struct pframe *f = kcont->ivf + k->no;
	/*
	 * set up the frame, display marks, etc.
	 */
	kdsetup(k,kcont);
	/*
	 * display the object
	 */
	colour(GREYCOLS);
	picframe(obj,f);
	/*
	 * if temporary highlight required, draw a rectangle round the object
	 * It is up to the user to ensure that kdisplay with intens(1)
	 * follows the setting of TEMP_MARK_REQ.  Then the next unkdisplay
	 * or dispall call will eliminate the rectangle.
	 */
	if (plist->dispmark & TEMP_MARK_REQ) {
		plist->dispmark |= TEMP_MARK_DONE;
		plist->dispmark &= ~TEMP_MARK_REQ;
		colour(OVERLAY1);
		bdisprect(obj,f);
	}
}


/*
 * delete a chromosome and its label, mark, etc., from screen
 */
unkdisplay(k,kcont)
register struct kident *k;
register struct kcontrol *kcont;
{
	register struct chromplist *plist = kcont->mol[k->no]->plist;

	intens(0);
	/*
	 * delete the rectangle draw by the TEMP_MARK handling in kdisplay,
	 * by setting TEMP_MARK_REQ, calling kdisplay, unsetting it.
	 */
	if (plist->dispmark & TEMP_MARK_DONE)
		plist->dispmark |= TEMP_MARK_REQ;
	kdisplay(k,kcont);
	/*
	 * Then finally get rid of the TEMP_MARK_DONE bit
	 */
	plist->dispmark &= ~TEMP_MARK_DONE;
	unbox();		/* undisplay rectangle also, just in case */
	intens(1);
}


/*
 *	H I D D E N S T A C K  C O U N T   ---   Count number of hidden objects
 *											 on stack
 *
 */
hiddenstackcount()
{
	static int hidden_count = -1;
	char s[20];
	colour(OVERLAY1);
	tsize(50,50);
	if (hidden_count > 0) {
		intens(0);
		sprintf(s,"(%d hidden objects)",hidden_count);
		moveto(200,8);
		text(s);
	}
	intens(1);
	hidden_count = stacktop() - VIEWED_STACK;
	if (hidden_count > 0) {
		sprintf(s,"(%d hidden objects)",hidden_count);
		moveto(200,8);
		text(s);
	}
}


/*
 * remove this object from the class it curently occupies
 */
remove_class(k)
struct kident *k;
{
	struct kclasscont *kc = kcc + k->kclass;
	kc->n--;
	kc->kno[k->pos-1] = -1;
	if (k->stackpos >= 0) {
		unstack(k->stackpos);
		kc->sp[k->pos-1] = -1;
	}
}

/*
 * initial orientation :
 * invert if area c.i. > 50 (should really be length c.i.),
 * or if c.i. unknown then if mwdd[0] positive
 */
initialorientation(i,kc)
struct kcontrol *kc;
{
	register struct chromplist *plist;
	plist = kc->mol[i]->plist;
	if (plist->dispor != 1 && plist->dispor != -1) {
		if (plist->cindexa > 50)
			plist->dispor = -1;
		else if (plist->cindexa == -1 && plist->mwdd[0] > 0)
			plist->dispor = -1;
		else
			plist->dispor = 1;
	}
}