/*  File: fpdisp.c
 *  Author: Danielle et Jean Thierry-Mieg (mieg@mrc-lmba.cam.ac.uk)
 *  Copyright (C) J Thierry-Mieg and R Durbin, 1994
 * -------------------------------------------------------------------
 * Acedb 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 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
 * -------------------------------------------------------------------
 * This file is part of the ACEDB genome database package, written by
 * 	Richard Durbin (MRC LMB, UK) rd@mrc-lmba.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr
 *
 * Description:
 * Exported functions:
 * HISTORY:
 * Last edited: May  6 10:48 2003 (edgrif)
 * * Jul 23 15:36 1998 (edgrif): Add fmap.h public header for fmap
 *      function declares.
 * * Jun  3 11:41 1996 (rd)
 * Created: Thu Jan  6 11:12:36 1994 (mieg)
 * CVS info:   $Id: fpdisp.c,v 1.28 2003/05/06 13:13:45 edgrif Exp $
 *-------------------------------------------------------------------
 */

#include <wh/acedb.h>
#include <wh/lex.h>
#include <wh/bs.h>
#include <wh/session.h>
#include <wh/pick.h>
#include <wh/dna.h>

#include <wh/display.h>
#include <wh/plot.h>
#include <wh/fingerp.h>
#include <wh/restriction.h>
#include <wh/fmap.h>
#include <wh/keysetdisp.h>
#include <wh/smap.h>

#include <whooks/sysclass.h>
#include <whooks/classes.h>
#include <whooks/tags.h>
#include <whooks/systags.h>


/************************************************************/

typedef struct GelStruct {
  KEY clone, clef ;
  Array fp, bp ;
  float maxFp, maxBp ;
} GEL ;

static BOOL isBp = TRUE ;
static float maxBp, maxFp ;
static Array bands = 0 ;
static float rounding = 1 ;
static Array fpSelectedBands = 0 , fpSelectedClefs = 0 , fpAllClefs = 0 , fpAllClones = 0;

static Graph fpGraph = 0 ;
static MAP fpMap = 0 ;
static Array fpGels = 0 ;
static Array fpBoxIndex = 0 ;

static int fpActiveBox = 0, minLiveBox = 0 , minClefBox = 0 ;
static int fpActiveGel = 0 ;
static char fpRoundingBuffer[5] ;

/************************************************************/

static BOOL  fingerPrintData (Array *fpp, Array* bpp,
			      KEY clone, KEY *clef) ;
static BOOL  fpRead (void) ;	/* returns FALSE if unsuccessful */
static Array fpLength2bp(Array fp) ;
static Array fpBp2Length(Array bp) ;

static void  cptAllPredictedBands (void);

static void fpDraw(void) ;
static void fpPick (int box,  double x , double y, int modifier_unused) ;

/************************************************************/

static void fpHisto(void)
{
  Array histo ;
  int i ;
  
  if (!fpRead())
    return;

  if(!bands) 
    return ;
  
  histo  = arrayCreate(5000,int);
  i = arrayMax(bands);
  while(i--)
    array(histo,arr(bands,i,short),int)++;

  plotHisto ("Bands distribution", histo);

  return;
} /* fpHisto */
 
/******************************************/
 
static BOOL fpRead(void)
{
  static char fileName[FIL_BUFFER_SIZE] = "";
  static char dirName[DIR_BUFFER_SIZE] = "";
  int i = 0 , n ;
  void *vp = &n;
  unsigned int nb = 20000, nr, size = sizeof(short);
  FILE *fpfile, *corfile;
   
  if (arrayExists(bands))
    return TRUE;

  if (!(corfile = filqueryopen (dirName, fileName, "cor", "r", 
				"Finger-print rawdata file")))
    return FALSE;
  filclose (corfile) ;

  bands = arrayCreate(nb,short);
  if ((fpfile = filopen (messprintf("%s/%s", dirName, fileName),"bnd","rb")))
    { do
	{ array(bands,(++i)*nb,short) = 0; /* to create space */
	  vp = arrp(bands,nb*(i-1),short); /* possible relocation */
	  fprintf(stderr,"I read %d bands\n",i*nb);
	}
      while ((nr=fread(vp,size,nb,fpfile)) == nb) ;
      
      arrayMax(bands) -= nb - nr; /* artificial space removed */
    }
  else
    { messout ("I reformat the ascii file %s.cor into a binary file %s.bnd",
	       fileName, fileName);
      if (!(corfile= filopen(messprintf("%s/%s", dirName, fileName),"cor","r")) ||
	  !(fpfile= filopen(messprintf("%s/%s", dirName, fileName),"bnd","wb")))
	return FALSE ;
      
      array(bands,(++i)*nb,short) = 0; /* to create space */
      while (freeread(corfile))
	if(freeint(&n))
	  array(bands,i++,short) = n ;
	else
	  { messout("Error parsing line %d of cor file : %s",
		    i, freeword());
	    break;
	  }
      
      filclose(corfile) ;
      fwrite(arrp(bands,0,short),size,arrayMax(bands),fpfile);
    }
  
  messout("I read %d gel bands", arrayMax(bands)) ;
  fpHisto() ;
  filclose(fpfile);
  return TRUE;
} /* fpRead */

/*********************************************************************/
/********************  public routines   *****************************/
/*********************************************************************/
  /* set *clefp or start with *clefp == 0, and repeat to get them all */

static int floatOrder (void *a, void *b)
{ float x =  *(float*)a - *(float*)b ;
  return x > 0 ? 1 : ( x < 0 ? -1 : 0) ;
}

/***********************************************************/

static BOOL fingerPrintData (Array *fpp, Array* bpp, KEY clone, KEY *clefp) 
{
  static int mm = 0 ;
  int from, i, n ;
  Array  fp = 0 ;
  float x , *xp ;
  KEY direc ;
  OBJ Clone ;
  BOOL isGotLength = FALSE ;

  if (!(Clone = bsCreate(clone)))
    return 0 ;

  if (*clefp)
    { if (!bsFindKey (Clone, _Gel, *clefp) || !bsGetKeyTags (Clone, _bsDown , clefp))
	return 0 ;
    }
  else
    if (!bsGetKeyTags (Clone, _Gel, clefp)) /* generic format */
      {
	if (!fp && class(clone) == _VClone &&  /* fall back on nematode specific format */
	    bsGetData(Clone,_Bands,_Int,&from) &&
	    bsGetData(Clone,_bsRight,_Int,&n))
	  { 
	    if(!bands)
	      {
		if (!fpRead())
		  return 0;
	      }
	    
	    isGotLength = TRUE ;
	    if (bands)
	      {
		if(from+n <= arrayMax(bands))
		  { 
		    KEY fpKey ;
		    
		    lexaddkey ("Observed", &fpKey, _VMotif) ;
		    *clefp = fpKey ;
		    fp = arrayCreate(n, float) ;
		    while(n--)
		      array(fp,n,float) = (float) (array(bands,from+n,short)) ;
		    if (arrayMax(fp)) /* && isWriteAccess())*/
		      { bsDestroy(Clone) ;
			if ((Clone = bsUpdate(clone)))
			  { bsAddTag (Clone, _Gel) ;
			    bsAddKey (Clone, _bsRight, fpKey) ;
			    bsPushObj (Clone) ;
			    bsAddTag (Clone, _Band_Lengths) ;
			    /* first value allready stored */
			    for (n = 0 ; n < arrayMax(fp) ; n++)
			      { x = arr(fp, n, float) ;
				bsAddData(Clone, _bsRight, _Float, &x) ;
			      }
			    bsSave(Clone) ;
			    Clone = 0 ;
			  }		
		      }
		  }
		else
		  messout
		    ("Clone %s\n Band %d > Max %d of data file",
		     name(clone), from+n, arrayMax(bands)) ;
	      }
	  }
	goto laba ;
      }

  if (bsPushObj (Clone))
      { direc = 0 ;
	i = 0 ;
	if ( bsGetData (Clone, _Bands, _Float, &x))
	  direc = _bsRight ;
	else if ( bsGetData (Clone, _Band_Lengths, _Float, &x))
	  { direc = _bsRight ;
	    isGotLength = TRUE ;
	  }
	else if ( bsGetData (Clone, _Band, _Float, &x))
	  direc = _bsDown ;
	fp = arrayCreate (8, float) ;
	if (direc)
	  do { array (fp, i++, float) = x ;
	     } while (bsGetData (Clone, direc, _Float, &x)) ;
	
      }
  
  
 laba:
  if (!fp && mm++ < 3)
    messout("Sorry, no finger print data known about clone %s",
	    name(clone)) ;

  if (Clone)
    bsDestroy(Clone) ;
  
  if (rounding == 0 || rounding <= 0.001) rounding = .001 ;
  if (fp)
    for (n = 0 , xp = arrp(fp, 0, float); n < arrayMax(fp) ; n++, xp++)
      { i = *xp / rounding ; 
	*xp = i * rounding ;
      }
  if (!fp || !arrayMax(fp))
    { arrayDestroy (fp) ;
      return FALSE ;
    }
  else
    arraySort (fp, floatOrder) ;
    
  if (isGotLength)
    { isBp = FALSE ;
      *fpp = fp ;
      *bpp = fpLength2bp(fp) ;
    }
  else
    { *bpp = fp ;
      *fpp = fpBp2Length (fp) ;
    }
  return TRUE ;
} /* fingerPrintData */

/*************************************************************/
/*************************************************************/

  /* donnees is a boolean matrix clones/lengths */
  /* donnees must be created as 2 arrays of KEYSET */
void pmapCptGetData (Array marInDef, Array defInMar, KEYSET clones, KEYSET bands)
{ KEY key = 0 , kb, clef ;
  Array aa,  fp, bp, xp ;
  Associator assB ;
  void *vp ;
  int  i, j, nB = 0 , nC, nBmax = 0 , nCmax ;
  KEYSET *ksp ;
  OBJ obj ;

  if (!keySetActive(&aa, 0))
    { messout("First select a keyset containing clones") ;
      return ;
    }
  j = 0 ;
  for (i = 0 ; i < keySetMax(aa) ; i++)
    { key = keySet(aa, i) ;
      if (class(key) == _VClone && (obj = bsCreate(key)))
	{ if (bsFindTag(obj, _Bands))
	    keySet(clones, j++) = key ;
	  bsDestroy(obj) ;
	}
    }
  nCmax = keySetMax(clones) ;
  if (!nCmax)
    { messout("First2 select a keyset containing clones") ;
      return ;
    }
  assB = assCreate() ;
  i = nCmax ;
  ksp = arrayp(marInDef, i - 1, KEYSET) ;
  while(i--)
    *ksp-- = keySetCreate() ;
  for (nC = 0 ; nC < nCmax ; nC++) 
    { clef = 0 ;
      while (fingerPrintData(&fp, &bp, keySet(clones, nC), &clef))
	{ if (keySetExists(fpSelectedClefs) &&
	      ! keySetFind (fpSelectedClefs, clef, &j))
	    continue ;
	  xp = isBp ? bp : fp ;
	  j = arrayMax(xp) ;
	  while (j--)
	    { lexaddkey(messprintf("%s.%g", name(clef), arr(xp, j, float)), 
			&kb, _VCalcul) ;
	      if (!assFind (assB, assVoid(kb), &vp))
		{ vp = assVoid(nBmax) ;
		  assInsert(assB, assVoid(kb), vp) ;
		  nB = nBmax ;
		  array(defInMar, nB, KEYSET) = keySetCreate() ;
		  keySet(bands, nBmax++) = kb ;
		}
	      else
		nB = assInt(vp) ;
	      ksp = arrp(marInDef, nC, KEYSET) ;
	      keySet(*ksp, keySetMax(*ksp)) = nB ;
	      ksp = arrp(defInMar, nB, KEYSET) ;
	      keySet(*ksp, keySetMax(*ksp)) = nC ;
	    }
	  arrayDestroy(fp) ;
	  arrayDestroy(bp) ;
	}
    }
  assDestroy(assB) ;
  i = arrayMax(marInDef) ;
  ksp = arrp(marInDef, 0, KEYSET) - 1 ;
  while(ksp++, i--)
    keySetSort(*ksp) ;
  i = arrayMax(defInMar) ;
  ksp = arrp(defInMar, 0, KEYSET) - 1 ;
  while(ksp++, i--)
    keySetSort(*ksp) ;

  return;
} /* pmapCptGetData */

/***************************************************************/
/***************************************************************/


static void fpDestroy(void)
{
  int i ;

  if (graphExists(fpGraph))
    {
      fpGraph = 0 ;
      i = arrayMax (fpGels) ;
      while(i--)
	{ arrayDestroy(arrp(fpGels, i, GEL)->fp) ;
	  arrayDestroy(arrp(fpGels, i, GEL)->bp) ;
	}
      arrayDestroy(fpGels) ;
      arrayDestroy(fpBoxIndex) ;
      arrayDestroy(fpSelectedBands) ;
      arrayDestroy(fpSelectedClefs) ;
      arrayDestroy(fpAllClefs) ;
      arrayDestroy(fpAllClones) ;

      mapDestroy(fpMap) ;
      graphAssRemove (&MAP2LOOK_ASSOC) ;
    }

  return;
} /* fpDestroy */

/**************************************************************/

static void fpRmClone(void)
{ int n , i , j ;
  KEY clone ;

  if (!graphActivate(fpGraph))
    return ;
  n = fpActiveGel ;
  if (n == 100000)
    return ;
  clone = arr (fpGels, n, GEL).clone ;
  i = arrayMax(fpGels) ; j = 0 ;
  while (i--)
    if (clone == arr (fpGels, i, GEL).clone)
     { arrayDestroy(arr(fpGels, i, GEL).fp) ;
       arrayDestroy(arr(fpGels, i, GEL).bp) ;
       for (j = i ; j < keySetMax(fpGels) - 1 ; j++)
	 array(fpGels, j, GEL) = array(fpGels, j + 1, GEL) ; 
       arrayMax(fpGels)-- ;
     }
  
  fpDraw() ;

  return;
} /* fpRmClone */

static void fpUnBlock (void)
{
  displayUnBlock() ;
  if (graphActivate(fpGraph))
    graphRegister(PICK, fpPick) ;

  return;
} /* fpUnBlock */

/*************************************/

static BOOL fpInterpret (int *np)
{ int i, j, dummy, n = *np ;
  KEY clef ;

  n /= 2 ;
  
  for (i = 0 , j = 0 ; i < arrayMax(fpGels) ; i++)
    { clef = arr(fpGels, i, GEL).clef ;
      if (keySetFind (fpSelectedClefs, clef, &dummy))
	{ if (j == n)
	    break ;
	  j++ ;
	}
    }
  if (i >= arrayMax(fpGels))
    return FALSE ;
  *np = i ;
  return TRUE ;
} /* fpInterpret */

/*************************************/
/*
static void fpSort(void)
{ int n = arrayMax(fpGels) ;
  KEYSET ks = keySetCreate() ;

  while (n--)
    keySet(ks, n) = arrp(fpGels, n, GEL)->clone ;
  
  messout("No direct coding yet, use mapping from main menu, sorry") ;
}
*/
/*************************************/

static void fpSelectAND (Array x, Array y)
{
  float *xp, *yp, *zp ;
  register int i = arrayMax(x), j = arrayMax(y) , k = 0 ;

  xp = zp = arrp (x, 0, float) ;
  yp = arrp (y, 0, float) ;

  while(i && j)
    { 
                      /*success, skip further in the 3 index */
      if(*xp == *yp)
	{ *zp = *xp ;
	  i-- ; j-- ;
	  xp++ ; yp++ ; zp++ ;
	  k++ ;
	}
      else      /*recall that every index is in increasing order*/
	if (*xp < *yp)
	  { i--, xp++ ; } 
	else
	  { j--; yp++ ;}
    }
  arrayMax (x) = k ;

  return;
} /* fpSelectAND */

/*************************************/

static void fpDoSelectBands (int box, double x_unused, double y_unused, int modifier_unused)
{
  int n , nn ;
  Array fp ;
  Array old ;

  old = fpSelectedBands ;
  fpSelectedBands = 0 ;
  if (old && !arrayMax(old))
    arrayDestroy(old) ;
  
  if (box > minLiveBox)
    { n = box - minLiveBox - 1 ;
      nn = n & 1 ;
      if (!fpInterpret (&n))
	return ;
      fpActiveGel = n ;
      fp = isBp ? array(fpGels, n, GEL).bp 
	: array(fpGels, n, GEL).fp ;
	 
      if (old)
	fpSelectAND (old, fp) ;
      else
	old = arrayCopy (fp) ;

      fpSelectedBands  = old ;
      fpDraw() ;
    }
  
  return;
} /* fpDoSelectBands */

/**************************************************************/

static void fpSelectBands (void)
{
  int  n = 0 , max ;
   
  n = fpActiveGel ;
  max = arrayMax(fpGels) ;
  if (n >= max)  
    return ;
  
  graphRegister (MESSAGE_DESTROY, fpUnBlock) ;
  graphRegister (PICK, fpDoSelectBands) ;
  displayBlock ((BlockFunc)fpUnBlock,
		"Pick in succession clones or lanes, their intersect will turn thick.\n"
		"This will continue until you remove this message or click outside the gel.") ;
} /* fpSelectBands */

/*************************************/

static void fpPickSwitch (int box, double x_unused, double y_unused, int modifier_unused)
{
  int n, a, b;
  GEL gel ;

  if (box > minLiveBox)
    { n = box - minLiveBox - 1 ;
      if (!fpInterpret (&n))
	return ;

      if (fpActiveGel == 100000)
	{ fpActiveGel = n ;
	  fpDraw() ;
	}
      else
	{ a = fpActiveGel ; b = n ;
	  if (a == b)
	    return ;
	  gel = array(fpGels, a, GEL) ;
	  array(fpGels, a, GEL) = array(fpGels, b, GEL) ;
	  array(fpGels, b, GEL) = gel ;
	
	  fpActiveGel = 100000 ;
          fpDraw() ;
	}
    }

  return;
} /* fpPickSwitch */

static void fpSwitch (void)
{ int  n = 0 , max ;
   
  n = fpActiveGel ;
  max = arrayMax(fpGels) ;

  if (n < 0 || n >= max)  
    return ;
  if (n == max - 1)
    {
      if (n>0) n-- ; 
      else return ;
    }

  if (max >= 2)
    { graphRegister (MESSAGE_DESTROY, fpUnBlock) ;
      graphRegister (PICK, fpPickSwitch) ;
      displayBlock ((BlockFunc)fpUnBlock,
		    "Pick in succession a lane and a target.\n"
		    "This will continue until you remove this message or\n"
		    "click outside the gel.") ;
      fpActiveGel = 100000 ; /* to start parity in switch routine */
      return ;
    }
/* difficille a cause du clef select 
  gel = array(fpGels, n , GEL) ;
  array(fpGels, n , GEL) =  array(fpGels, n + 1, GEL) ;
  array(fpGels, n + 1, GEL) = gel ;
  fpDraw() ;
*/  

  return;
} /* fpSwitch */

/*************************************/

static void fpDoAddClone(KEY clone)
{ 
  fpDisplay (clone) ;
  displayRepeatBlock () ;
}

static void fpAddClone(void)
{ 
  if (!graphActivate(fpGraph))
    return ;
  graphRegister (MESSAGE_DESTROY, fpUnBlock) ;
  displayBlock (fpDoAddClone,
	"Double-clicking a clone will show its finger-printing.\n"
	"This will continue until you remove this message.") ;

  return;
} /* fpAddClone */

static void fpAddKeySet(void)
{
  KEYSET aa ;
  int i ;

  if (!graphActivate(fpGraph))
    return ;
  if (!keySetActive(&aa, 0))
    { messout("First select a keyset containing clones") ;
      return ;
    }

  fpClearDisplay() ;
  for (i=0; i < keySetMax(aa) ; i++)
    fpDisplay(keySet(aa,i)) ;

  return;
} /* fpAddKeySet */

/*************************************/

static Array fpLength2bp(Array fp)
{
  Array bp;
  double x ;
  int i = arrayMax(fp) ;
	   
  bp = arrayCreate(arrayMax(fp), float) ;

  while(i--)
    { x = arr(fp, i, float) ;
      /* excellent Fit mathematica sur 79 data d'etalonage  */
      x = log(x) ;
      x = 133.164 - 54.4586 * x + 7.9092 * x * x - .389621 * x * x * x ;
      x = exp(x) ;
      array(bp, i, float) = x ;
    }
  arraySort (bp, floatOrder) ;
  return bp ;
} /* fpLength2bp */

/*************************************/

static Array fpBp2Length(Array bp)
{ Array fp ;
  double x; float y ;
  int i ;

  i = arrayMax(bp) ;
  fp = arrayCreate(arrayMax(bp), float) ;
  while(i--)
    { x = arr(bp, i, float) ;
      if (x < 1)
	x = 1 ;
	     /* 
	        Log relation between lenght and distance of migration:
		figure 6.1 page 6.5 ; Molecular cloning, a laboratory manual
		second ed. Sambrook, Fritsch, Maniatis
		ColdSping Harbor lab. press, 1989
	
                log10 isdeclared in mystdlib.h 
		the limits 10 to 10000 (so 0 <= y < 11.5)
		are recalled in a graphTextMessage.
		The inverse relation is used in drawScale.
	     */
      y = 3.5 * ( 4 - log10(x)) ;
      array(fp, i, float) = y ;
    }
  arraySort (fp, floatOrder) ;
  return fp ;
} /* fpBp2Length */

/*************************************/

static void fpRounding(char *cp) 
{ int n, i, level ;
  float x ;
  GEL *gelp ;
  KEYSET ks = keySetCreate () ;

  level = freesettext(cp,"") ;
  freecard (level) ;
  if (freefloat(&x) && (x>0) && rounding != x )
    { rounding = x ;
      freeclose(level) ;
    }
  else
    { freeclose(level) ;
      return ;
    }

  sprintf(fpRoundingBuffer, "%g", rounding) ;

  i = arrayMax(fpGels) ;
  for (i = 0 ; i < keySetMax (fpGels) ; i++)
    { gelp = arrp(fpGels, i, GEL) ;
      keySet (ks, i) = gelp->clone ;
    }

  n = arrayMax(fpGels) ;
  while(n--)
    { arrayDestroy(arr(fpGels, n, GEL).fp) ;
      arrayDestroy(arr(fpGels, n, GEL).bp) ;
    }  
  fpGels = arrayReCreate (fpGels, 12, GEL) ;
  fpAllClefs = keySetReCreate (fpAllClefs) ;
  fpAllClones = keySetReCreate (fpAllClones) ;

  for (i = 0 ; i < keySetMax (fpGels) ; i++)
    fpDisplay (keySet(ks,i)) ;
  keySetDestroy (ks) ;
	       
  fpDraw() ;
} /* fpRounding */

/**************************************************************/

static void fpTransform(void)
{
  float mx, x1;
  KEY clef ;
  int i, dummy ;
  Array bp ;

  isBp = !isBp ;
  mx = -1 ;
lao:
  if (!keySetExists(fpSelectedClefs))
    fpSelectedClefs = keySetCopy (fpAllClefs) ;
  keySetSort (fpSelectedClefs) ;
  keySetCompress (fpSelectedClefs) ;

 
  for (i = 0 ; i < arrayMax(fpGels) ; i++)
    { clef = array(fpGels, i, GEL).clef ;
      if (keySetFind (fpSelectedClefs, clef, &dummy))
	{ bp = isBp ? array(fpGels, i, GEL).bp : array(fpGels, i, GEL).fp ;
	  x1 = arr (bp, arrayMax(bp) - 1, float) ;
	  if (x1 > mx) mx = x1 ;
	}
    }
  if (mx == -1)
    { mx = 0 ;
      keySetDestroy (fpSelectedClefs) ;
      goto lao ;
    }

  fpMap->max = mx ;
  fpMap->centre = mx / 2 ;
  fpMap->mag = (mapGraphHeight - topMargin - 5) /
    			(1.05 * mx) ;
  fpDraw() ;

  return;
} /* fpTransform */

/***********************************************************/

/* column "Main Bands" */
/* This is of type MapColDrawFunc */
static void fpMainLines(LOOK look_unused, float *offset, MENU *menu_unused)
{
  float y , mx = (isBp ? maxBp : maxFp) ;
  int i , di = mx /10 , j ;

  if (di<=1) di = 2 ;
  j = 1 ;while (j < di) j*= 10 ;
  j /= 10 ; 
  if (j > 1)
    di = di - (di%j) ;
  i = di/j ;
  if (i>5)
    di = 5*j ;

  graphTextHeight (0.75) ;
  for (i = 0 ; i < mx ; i += di)
    { y = MAP2WHOLE(fpMap, i) ;
      graphText (messprintf("%d", i),*offset + 0.5,y-0.25) ;
    }
  graphTextHeight (0) ;
  *offset += 4 ;

  return;
} /* fpMainLines */

/*************************************/

static void fpPick (int box,  double x , double y, int modifier_unused) 
{
  int n , nn ;
  KEY clone, clef ;

  if (!box)
    return ;
  if (box == fpMap->cursor.pickBox)
    graphBoxDrag (fpMap->cursor.box, mapCursorDrag) ;
  else if (box == fpMap->thumb.box - 1)
   { /* coordinates are realtive to thumb -1 .box */
     graphFitBounds (&mapGraphWidth, &mapGraphHeight) ;
     if (y < 1) /* page up */
	fpMap->centre -=  1.5 * fpMap->thumb.halfwidth/fpMap->thumb.fac ;
      else if (y > mapGraphHeight - topMargin - 3.2) 
	fpMap->centre +=  1.5 * fpMap->thumb.halfwidth/fpMap->thumb.fac ;
      else
	fpMap->centre = WHOLE2MAP (fpMap, y + topMargin + .2) ;
      fpDraw () ;
    }
  else if (box == fpMap->thumb.box)
    {  /* coordinates are realtive to thumb.box */
       fpMap->centre += 
	(y - fpMap->thumb.halfwidth)/fpMap->thumb.fac ;
       graphBoxDrag (fpMap->thumb.box, mapThumbDrag) ;
    }
  else if (box > minLiveBox)
    { n = box - minLiveBox - 1 ;
      nn = n & 1 ;
      if (!fpInterpret (&n))
	return ;
      clone = array(fpGels, n, GEL).clone ;
      if (nn)
	display(clone, (KEY)0, 
		!strcmp(name(pickDisplayKey(clone)), "DtPmapFingerprint") ? "TREE" : "") ;
      if (fpActiveGel != n)
	{ /* graphBoxDraw(minLiveBox + 2 + 2*fpActiveGel,
		       BLACK, WHITE) ;
	  graphBoxDraw(minLiveBox + 2 + 2*fpActiveGel,
		       BLACK, WHITE) ;
	  */
	  fpActiveGel = n ;
	  fpDraw() ;
	}
    }
  else if (box > minClefBox)
    { int dummy  = box - minClefBox - 1 ;

      if (dummy <0 || dummy >= keySetMax(fpAllClefs))
	return ;
      clef = keySet (fpAllClefs, dummy) ;
      
      if (keySetFind (fpSelectedClefs, clef, &dummy))
	keySetRemove (fpSelectedClefs, clef) ;
      else
	keySetInsert (fpSelectedClefs, clef) ;
	
      fpDraw() ;
    }

  return;
} /* fpPick */

/**************************************************************/

/* column "Bands" */
/* This is of type MapColDrawFunc */
static void fpDrawGels(LOOK look_unused, float *offset,  MENU *menu_unused)
{ 
  float *xb, x = *offset, y, dx;
  float oldwidth = graphLinewidth(-1.0) ;
  Array fp, bp ;
  KEY clef ;
  int i, j, box, max, max1, dummy ; 

  max = arrayMax(fpGels) ;
  if (!max)
    return ;
 

  minClefBox= graphBoxStart() ;
  graphText ("Select:", 2 , 2) ;
  graphBoxEnd() ;
  dx = 8 ;
  for (i = 0 ; i < keySetMax (fpAllClefs) ; i++)
    { clef = keySet(fpAllClefs, i) ;
      box = graphBoxStart() ;
      graphText (name(clef), dx , 2) ;
      graphBoxEnd () ;
      
      dx += strlen(name(array(fpGels, i, GEL).clef)) + 1 ;

      if (keySetFind (fpSelectedClefs, clef, &dummy))
	graphBoxDraw (box, BLACK, GREEN) ;
      else
	graphBoxDraw (box, BLACK, WHITE) ;
    }
 
  for (max1 = 0, i = 0 ; i < arrayMax(fpGels) ; i++)
    { clef = array(fpGels, i, GEL).clef ;
      if (keySetFind (fpSelectedClefs, clef, &dummy))
	max1++ ;
    }

  if (!max1) max1++ ;
  dx = (mapGraphWidth - x - 6)/(1.2 * max1) ;
  minLiveBox= graphBoxStart() ;
  graphBoxEnd() ;
  for (i = 0 ; i < arrayMax(fpGels) ; i++)
    { clef = array(fpGels, i, GEL).clef ;
      if (!keySetFind (fpSelectedClefs, clef, &dummy))
	continue ;
      graphBoxStart() ;
      graphText (name(array(fpGels, i, GEL).clef), x , topMargin + 1) ;

      fp = array(fpGels, i, GEL).fp ;
      bp = array(fpGels, i, GEL).bp ;
      j = arrayMax(bp) ;
      while(j--)
	{ y = array(bp, j, float) ;
	  xb = isBp ? arrayp(bp, j, float) : arrayp(fp, j, float) ;
	  if (fpSelectedBands &&
	      arrayFind(fpSelectedBands, xb, &dummy, floatOrder))
	    { graphColor(RED) ;
	      graphLinewidth(.04) ;
	    }
	  y = MAP2GRAPH(fpMap, *xb) ;
	  if (y > topMargin + 2)
	    graphLine(x , y , x + dx, y) ;
	  graphLinewidth(oldwidth) ;
	  graphColor(BLACK) ;
	}
      graphBoxEnd() ;
      box = graphBoxStart() ;
      graphText (name(array(fpGels, i, GEL).clone), x , topMargin) ;
      graphBoxEnd() ;
      if (i == fpActiveGel)
	graphBoxDraw(box, BLACK, LIGHTBLUE) ;
      x += dx * 1.2 ;
    }
  graphText(isBp ? "Base Pairs" : "mm/10", *offset + 1 , mapGraphHeight - 2) ;
  *offset = x ;

  return;
} /* fpDrawGels */

/**************************************************************/

static MENUOPT fpMenu[] = {
  {graphDestroy,	"Quit"},
  {help,		"Help"},
  {graphPrint,		"Print Screen"},
  {mapPrint,		"Print Whole Map"},
  {fpClearDisplay,	"Clear"},
  {mapColControl,	"Columns"},
  {0, 0}
} ;

static MENUOPT buttonOpts[] = {
  {mapWhole,		"Whole"}, 
  {mapZoomIn,		"Zoom In"},
  {mapZoomOut,		"Zoom Out"},
  {fpAddClone,		"Add Clone"},
  {fpAddKeySet,		"Add KeySet"},
  {fpRmClone,		"Remove Clone"},
  {fpClearDisplay,	"Clear"},
  {fpSwitch,		"Switch"},
  {fpSelectBands,	"Intersect"},
  {fpTransform,		"millimetre/10 <-> base pairs"},
  {cptAllPredictedBands, "predicted bands"},
  {0, 0}
} ;

/**************************************************************/

static void fpDraw(void)
{
  float x1, x2, y1, y2 ;
  int  n ;
 
  if (!graphActivate(fpGraph))
    return ;
  
  n = keySetMax(fpGels) ;
  
  graphClear () ;

  graphFitBounds (&mapGraphWidth, &mapGraphHeight) ;
  
  halfGraphHeight = 0.5 * (mapGraphHeight - topMargin) ; 

  if (mapGraphHeight < 10 + topMargin)
    { messout ("Sorry, this window is too small for a finger print data") ;
      return ;
    }

 
  fpBoxIndex = arrayReCreate (fpBoxIndex,50,int) ;
  fpActiveBox = 0 ;

  graphButtons (buttonOpts, 2, 3.5, mapGraphWidth * .9) ;
  graphText ("Rounding :", 10, 1) ;
  graphTextEntry (fpRoundingBuffer, 4, 22, 1, fpRounding) ;
  graphBoxDim (0, &x1, &y1, &x2, &y2) ;
  topMargin = y2 + 1 ;
  mapDrawColumns (fpMap) ;
  graphMenu (fpMenu) ;
  
  graphRedraw () ;

  return;
} /* fpDraw */
 
/**************************************************************/

void fpClearDisplay (void)
{
  int n ;
    
  if (!graphActivate(fpGraph))
    return ;

  n = arrayMax(fpGels) ;
  while(n--)
    { arrayDestroy(arr(fpGels, n, GEL).fp) ;
      arrayDestroy(arr(fpGels, n, GEL).bp) ;
    }  
  fpGels = arrayReCreate (fpGels, 12, GEL) ;
  fpAllClefs = keySetReCreate (fpAllClefs) ;
  keySetDestroy (fpSelectedClefs) ; /* don t recreate here */
  fpAllClones = keySetReCreate (fpAllClones) ;
  fpDraw() ;

  return;
} /* fpClearDisplay */
 
/**************************************************************/

void fpDisplay (KEY clone)
{
  int i, n, dummy ;
  Array fp, bp ; 
  KEY clef = 0 ;
  float mx, x1 ;

  if (!fingerPrintData(&fp, &bp, clone, &clef) )
    return ;
  
  if (!keySetExists(fpAllClefs))
    fpAllClefs = keySetCreate () ;
  if (!keySetExists(fpAllClones))
    fpAllClones = keySetCreate () ;
  if (!graphExists(fpGraph))
    { fpGraph = graphCreate (TEXT_FIT,"Finger Printing", .2, .2, .3, .8) ;
      if (!fpGraph)
	return ;
      isBp = TRUE ;
      fpMap = mapCreate (fpDraw) ; /* the map's redraw func */
      mapAttachToGraph (fpMap) ;
      mapInsertCol (fpMap, 1, TRUE, "Main Bands", fpMainLines) ;
      mapInsertCol (fpMap, 2, TRUE, "Locator", mapShowLocator) ;
      mapInsertCol (fpMap, 3, TRUE, "Scale", mapShowScale) ;
      mapInsertCol (fpMap, 4, TRUE, "Bands", fpDrawGels) ;
      mapCursorCreate (fpMap, 1, 0) ; 
      graphRegister (DESTROY, fpDestroy) ;
      graphRegister (RESIZE, fpDraw) ;
      graphRegister (PICK, fpPick) ;

      /* don't associate a the maps on this graph with any look -
	 This fp-display is not a proper look in the sense that fMap
	 is a proper look, but we still use the mapPackage. */
      graphAssociate (&MAP2LOOK_ASSOC, 0) ;

      graphMenu(fpMenu) ;
      fpGels = arrayReCreate(fpGels, 12, GEL) ;
      array(fpGels, 0, GEL).clone = clone ;
      fpActiveGel = 0 ;
      array(fpGels, 0, GEL).fp = fp ;
      arrayp (fpGels, 0, GEL)->clef = clef ;
      keySetInsert (fpAllClefs, clef) ;
      array (fpGels, 0, GEL).bp = bp ;
      fpMap->mag = 1 ;
      fpMap->centre = 500 ;
      sprintf(fpRoundingBuffer, "%g", rounding) ;
    }
  else
    { graphActivate (fpGraph) ;
      graphPop() ;
      n = keySetMax(fpGels) ;
      while(n--)
	if (arrp(fpGels, n, GEL)->clone == clone)
	  return ;
      n = keySetMax(fpGels) ;	
      fpActiveGel = n ;  
      arrayp (fpGels, n, GEL)->clone = clone ;
      arrayp (fpGels, n, GEL)->clef = clef ;
      keySetInsert (fpAllClefs, clef) ;
      arrp (fpGels, n, GEL)->fp = fp ;
      arrp (fpGels, n, GEL)->bp = bp ;
    }
  
  fpMap->min = 0 ;
  i = arrayMax(fp) ;
  while(i--)
    { if (arr(fp, i, float) > maxFp)
	maxFp = arr(fp, i, float) ;
      if (arr(fp, i, float) > maxBp)
	maxBp = arr(bp, i, float) ;
    }
  keySet(fpAllClones, keySetMax(fpAllClones)) = clone;
  while (fingerPrintData(&fp, &bp, clone, &clef))
    { n = keySetMax(fpGels) ;	
      fpActiveGel = n ;  
      arrayp(fpGels, n, GEL)->clone = clone ;
      arrayp(fpGels, n, GEL)->clef = clef ;
      keySetInsert (fpAllClefs, clef) ;
      arrp(fpGels, n, GEL)->fp = fp ;
      arrp(fpGels, n, GEL)->bp = bp ;
      i = arrayMax(fp) ;
      while(i--)
	{ if (arr(fp, i, float) > maxFp)
	    maxFp = arr(fp, i, float) ;
	  if (arr(fp, i, float) > maxBp)
	    maxBp = arr(bp, i, float) ;
	}
    }
 
  keySetSort (fpAllClefs) ;
  keySetCompress (fpAllClefs) ;
  mx = -1 ;
lao:
  if (!keySetExists(fpSelectedClefs))
    fpSelectedClefs = keySetCopy (fpAllClefs) ;
  keySetSort (fpSelectedClefs) ;
  keySetCompress (fpSelectedClefs) ;

 
  for (i = 0 ; i < arrayMax(fpGels) ; i++)
    { clef = array(fpGels, i, GEL).clef ;
      if (keySetFind (fpSelectedClefs, clef, &dummy))
	{ bp = isBp ? array(fpGels, i, GEL).bp : array(fpGels, i, GEL).fp ;
	  x1 = arr (bp, arrayMax(bp) - 1, float) ;
	  if (x1 > mx) mx = x1 ;
	}
    }
  if (mx == -1)
    { mx = 0 ;
      keySetDestroy (fpSelectedClefs) ;
      goto lao ;
    }


  graphFitBounds (&mapGraphWidth, &mapGraphHeight) ;
  halfGraphHeight = 0.5 * (mapGraphHeight - topMargin) ;

  fpMap->centre = mx / 2 ;
  fpMap->mag = (mapGraphHeight - topMargin - 5) /
    			(1.05 * mx) ;
  fpMap->min = 0 ;
  fpMap->max = mx ;

  fpDraw() ;

  return;
} /* fpDisplay */
  
/**************************************************************/

BOOL fingerPrintDisplay (KEY clone, KEY from, BOOL reuse, void *unused)
{ fpDisplay (clone) ;
  return graphExists (fpGraph) ;
}

/**************************************************************/

static void cptPredictedBands (KEY clone)
{
  OBJ Clone = bsCreate(clone);
  KEY seqKey ;
  Array dna;
  Array hind3, sau3a , fp ; 
  int from = 0, to = 0, n;
  int x;
  float f;
  KEY predicted;

  if (!Clone) return;

#ifdef USE_SMAP
  if (bsGetKey(Clone, _Sequence, &seqKey) &&
      (dna = dnaGet(seqKey)))
    {
      from = 0;
      to = arrayMax(dna) -1;
#else
  if (bsGetKey(Clone, _Sequence, &seqKey) &&
       (dna = fMapFindDNA (seqKey, &from, &to, TRUE)))
    {
#endif
      hind3 = arrayCreate(30,int) ;
      sau3a = arrayCreate(30,int) ;
      fp = arrayCreate(30,int) ;
      
      dnacptFingerPrintCompute(dna, from, to, 0, hind3, sau3a, fp);
      
      /* attach to clone */
      lexaddkey("Predicted from dna", &predicted, _VMotif);
      
      bsDestroy(Clone) ;
      if ((Clone = bsUpdate(clone)))
	{
	  bsAddTag (Clone, _Gel) ;
	  bsAddKey (Clone, _bsRight, predicted) ;
	  bsPushObj (Clone) ;
	  bsAddTag (Clone, _Bands) ;
	  /* first value allready stored */
	  for (n = 0 ; n < arrayMax(fp) ; n++)
	    { x = arr(fp, n, int) ; f = x;
	    bsAddData(Clone, _bsRight, _Float, &f) ;
	    }
	  bsSave(Clone) ;
	  Clone = 0 ;
	}		
      
      arrayDestroy(dna);
      arrayDestroy(hind3);
      arrayDestroy(sau3a);
      arrayDestroy(fp);
  }
  bsDestroy(Clone);

  return;
} /* cptPredictedBands */

/**************************************************************/

static void cptAllPredictedBands (void)
{
  int i;
  KEYSET aa = keySetCopy(fpAllClones);
  if (!aa) return;
  fpClearDisplay() ;

  /* for each displayed clone */
  for (i=0; i<keySetMax(aa); i++) {
    cptPredictedBands (keySet(aa, i));
    fpDisplay(keySet(aa,i)) ;
  }
  keySetDestroy(aa);

  return;
} /* cptAllPredictedBands */

/**************************************************************/
/************************ eof *********************************/
 
