/*  File: align2.c
 *  Author: Richard Durbin (rd@mrc-lmb.cam.ac.uk)
 *  Copyright (C) J Thierry-Mieg and R Durbin, 1991
 * -------------------------------------------------------------------
 * 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-lmb.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr
 *
 * Description:
 **  Realigns the various maps in dna unit                    **
 * Exported functions:
 * HISTORY:
 * Last edited: Nov  9 13:52 1999 (fw)
 * * May 16 22:35 1993 (cgc): rd started again from scratch
 * * Dec 11 12:18 1991 (mieg): myText for non interactive messages
 * Created: Fri Nov 29 14:31:10 1991 (mieg)
 *-------------------------------------------------------------------
 */

/* $Id: align.c,v 1.24 1999/11/09 13:53:00 fw Exp $ */

#include "acedb.h"
#include "a.h"
#include "bs.h"
#include "dna.h"
#include "lex.h"
#include "plot.h"
#include "query.h"
#include "whooks/sysclass.h"
#include "whooks/systags.h"
#include "whooks/tags.h"
#include "whooks/classes.h"
#include "session.h"

#include "display.h"
#include "gmap.h"
#include "vmap.h"

static Graph alignGraph = 0 ;
static int line ;

static void alignDisplay (void) ;

typedef struct
  { KEY key ;
    KEY parent ; /* clone associated with this item */
    float x0, x1 ;
    unsigned int flag ;
  } PMAPSEG ;
#define pMapFormat "kkffi"

/*****************************************************/
/* Routine to get an array of gMap Clone and contigs */
/*****************************************************/

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

   /* Graphic or non graphic printouts */

static void textGraphic(char *cp, int x, int y)
{
  line += y ;
  graphText(cp, x, line) ;
}

static void textNonGraphic(char *cp, int x, int y)
{
  if (!y)
    messdump ("  ") ;
  while (y--)
    messdump ("\n") ;
  messdump (cp) ;
}

static void (*myText)(char*,int,int) ;

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

typedef struct { float x, y ; } PAIR ;

/* strategy is to write an ace file */

static void alignAllContigs (void)
{
  KEY contig, pmap, map ;
  Associator map2pairs = 0;
  Array psegs, pairs, units  ;
  int i, j, pmin, pmax ;
  PAIR *pair, *pair2 ;
  PMAPSEG *pseg ;
  float f1, f2 ;
  FILE *fil ;
  OBJ  obj ;
  BOOL hasPrinted ;

  alignDisplay () ;

  myText ("Before aligning you need up-to-date pMaps.", 
	  1, 1) ;
  myText ("If necessary, make these first.", 1, 1) ;
  myText ("", 1, 1) ;

  if (!(fil = filqueryopen (0, 0, "ace", "w", "Ace file to write")))
    return ;
  /* process contigs */
  contig = 0 ;
  units = arrayCreate (12, BSunit) ;
  while (lexNext (_VContig, &contig))
    { if (!lexReClass (contig, &pmap, _VpMap) ||
	  !(psegs = arrayGet (pmap, PMAPSEG, pMapFormat)))
	continue ;
      pmin = 1000000 ; 
      pmax = -1000000 ;
      map2pairs = assReCreate (map2pairs) ;
      for (i = 0 ; i < arrayMax(psegs) ; ++i)
	{ pseg = arrp(psegs,i,PMAPSEG) ;
	  if (class (pseg->key) == _VClone)
	    { if (pseg->x0 < pmin)
		pmin = pseg->x0 ;
	      if (pseg->x1 > pmax)
		pmax = pseg->x1 ;
	    }
	  else if (class (pseg->key) == _VLocus &&
		   (obj = bsCreate(pseg->key)))
	    { if (bsFindTag (obj, _Map) && 
		  bsFlatten (obj, 3, units))
		for (j = 0 ; j < arrayMax(units) ; j += 3)
		  if (arr(units, j+1, BSunit).k == _Position)
		    { map = arr(units, j, BSunit).k ;
		      if (!assFind (map2pairs, assVoid(map), &pairs))
			assInsert (map2pairs, assVoid(map),
			   (pairs = arrayCreate(8, PAIR))) ;
		      pair = arrayp(pairs, arrayMax(pairs), PAIR) ;
		      pair->x = pseg->x0 ;
		      pair->y = arr(units, j+2, BSunit).f ;
		    }
	      bsDestroy (obj) ;
	    }
	}
      arrayDestroy (psegs) ;
      hasPrinted = FALSE ;
      for (map = 0 ; assNext (map2pairs, &map, &pairs) ; )
	{
	  if (!hasPrinted)
	    { fprintf (fil, "\nContig %s\n", name(contig)) ;
	      fprintf (fil, "pMap %d %d\n", pmin, pmax) ;
	      hasPrinted = TRUE ;
	    }
	  if (arrayMax(pairs) >= 2)
	    { pair = arrp(pairs, 0, PAIR) ;
	      pair2 = arrp(pairs, arrayMax(pairs)-1, PAIR) ;
	      f1 = pair->y - (pair->x - pmin) * 
		(pair2->y - pair->y) / (pair2->x - pair->x) ;
	      f2 = pair2->y + (pmax - pair2->x) * 
		    (pair2->y - pair->y) / (pair2->x - pair->x) ;
	      fprintf (fil, "Map %s Left %.3g // %d hits\n"
		            "Map %s Right %.3g\n",
		       name(map), f1, arrayMax(pairs), name(map), f2) ;
	    }
	  else
	    { pair = arrp(pairs, 0, PAIR) ;
	      f1 = pair->y - (pair->x - pmin) * 0.004 ;
	      f2 = pair->y + (pmax - pair->x) * 0.004 ;
	      fprintf (fil, "Map %s Position %.3g Error %.3g // 1 hit\n",
		       name(map), pair->y, (pmax - pmin) * 0.004) ;
	    }
	  arrayDestroy (pairs) ;
	}
      if (messIsInterruptCalled ())
	break ;
    }

  arrayDestroy (units) ;
  assDestroy (map2pairs) ;

  filclose (fil) ;
  return ;
}

/*******************************************************************/
/****************** code to make sequence maps *********************/

/* rd 950826: sorry - very worm specific just now */

typedef struct {
  KEY key ;
  KEY map ;
  int leftEnd, rightEnd, leftMap ;
} CINFO ;

int cinfoCompare (const void *A, const void *B) /* for qsort() */
{ 
  CINFO *a = (CINFO*)A, *b = (CINFO*)B ;

  if (a->map > b->map) return 1 ;
  if (a->map < b->map) return -1 ;
  if (a->leftMap > b->leftMap) return 1 ;
  if (a->leftMap < b->leftMap) return -1 ;
  return 0 ;
}

void sMapMake (FILE *fil, BOOL isSeq)		/* make sequence maps */
{
  KEYSET contigs, genCan, genClone ;
  int i, offset, x1, x2, j ;
  float pos ;
  KEY key, map ;
  OBJ obj ;
  CINFO *cinf ;
  Array cInfo ;
  static BSMARK mark = 0 ;
  extern Array pMapConvert (void*, KEY, KEY) ;
#define CONTIG_GAP 50		/* nominal size of gap */

	/* find contigs to put on the maps (from last time) */

  contigs = query (0, "FIND Map Sequence-* ; FOLLOW Contig") ;
  cInfo = arrayCreate (keySetMax(contigs), CINFO) ;
  for (i = 0 ; i < keySetMax(contigs) ; ++i)
    if ((obj = bsCreate (keySet(contigs, i))))
      { if (bsGetKey (obj, _Map, &map)) do
	  { mark = bsMark (obj, mark) ;
	    if (strlen (name(map)) > 9 &&
		!strncmp (name(map),"Sequence-",9) &&
		bsPushObj (obj) &&
		bsGetData (obj, _Left, _Float, &pos))
	      { cinf = arrayp(cInfo, arrayMax(cInfo), CINFO) ;
		cinf->key = keySet(contigs,i) ;
		cinf->map = map ;
		cinf->leftMap = pos + 0.5 ; /* 0.5 for rounding */
		bsGoto (obj, mark) ; 	    /* to get out of subobj */
		if (!bsGetData (obj, _pMap, _Int, &cinf->leftEnd) ||
		    !bsGetData (obj, _bsRight, _Int, &cinf->rightEnd))
		  { Array segs ;
		    lexaddkey (name(cinf->key), &map, _VpMap) ;
		    segs = pMapConvert (0, cinf->key, map) ;
		    arrayDestroy (segs) ;
		    bsDestroy (obj) ;
		    obj = bsCreate (cinf->key) ;
		    if (!bsGetData (obj, _pMap, _Int, &cinf->leftEnd) ||
			!bsGetData (obj, _bsRight, _Int, &cinf->rightEnd))
		      { messout ("Problem with ends of %s", name(cinf->key)) ;
			--arrayMax (cInfo) ;
		      }
		  }
		break ;		/* out of do-while loop */
	      }
	    bsGoto (obj, mark) ;
	  }
        while (bsGetKey (obj, _bsDown, &map)) ;
	bsDestroy (obj) ;
	if (messIsInterruptCalled ())
	  break ;
      }
    else
      messout ("Failed to find object %s", keySet(contigs,i)) ;

	/* sort the contigs into map order, and rewrite the map objects */

  qsort (arrp(cInfo,0,CINFO), arrayMax(cInfo), 
	 			sizeof(CINFO), cinfoCompare) ;
  map = 0 ;
  for (i = 0 ; i < arrayMax(cInfo) ; ++i)
    { cinf = arrp(cInfo, i, CINFO) ;
      if (cinf->map != map)
	{ if (map)
	    { fprintf (fil, "\nMap %s\n", name(map)) ;
	      fprintf (fil, "Extent 0 %d\n", offset) ;
	    }
	  map = cinf->map ;
	  fprintf (fil, "\nMap %s\n", name(map)) ;
	  fprintf (fil, "-D Contig\n") ;
	  fprintf (fil, "-D %s\n", isSeq ? "Sequence" : "Clone") ;
	  offset = 0 ;
	}
      else
	offset += CONTIG_GAP ;
      fprintf (fil, "\nContig %s\n", name(cinf->key)) ;
      cinf->leftMap = offset ;
      fprintf (fil, "Map %s Left %d\n", name(cinf->map), offset) ;
      offset += (cinf->rightEnd - cinf->leftEnd) ;
      fprintf (fil, "Map %s Right %d\n", name(cinf->map), offset) ;
    }
  if (map)
    { fprintf (fil, "\nMap %s\n", name(map)) ;
      fprintf (fil, "Extent 0 %d\n", offset) ;
    }

	/* now put objects on map */

  genCan = query (0, "FIND Genome_Sequence") ;
  genClone = keySetCreate() ;
  for (i = 0 ; i < keySetMax (genCan) ; ++i)
    if (lexword2key (name(keySet(genCan,i)), &key, _VClone))
      keySet(genClone, keySetMax(genClone)) = key ;
  keySetSort (genClone) ;
  keySetDestroy (genCan) ;

  for (i = 0 ; i < keySetMax(genClone) ; ++i)
    if ((obj = bsCreate (keySet (genClone, i))))
      { if (!bsGetKey (obj, _pMap, &map))
	  { 
	    if ( /* just bsGet, if() is for compiler happiness */
		bsGetKey (obj, _Approximate_Match_to, &key) ||
		bsGetKey (obj, _Exact_Match_to, &key) ||
		bsGetKey (obj, _Funny_Match_to, &key)) ;
	    bsDestroy (obj) ;
	    if (!(obj = bsCreate (key)))
	      continue ;
	    if (!bsGetKey (obj, _pMap, &map))
	      { bsDestroy (obj) ;
		continue ;
	      }
	  }
	if (bsGetData (obj, _bsRight, _Int, &x1) &&
	    bsGetData (obj, _bsRight, _Int, &x2))
	  { for (j = 0 ; j < arrayMax(cInfo) ; ++j)
	      if ((cinf = arrp(cInfo, j, CINFO))->key == map)
		break ;
	    if (j < arrayMax(cInfo))
	      { x1 += cinf->leftMap - cinf->leftEnd ;
		x2 += cinf->leftMap - cinf->leftEnd ;
		fprintf (fil, "\n%s %s\n", 
			 isSeq ? "Sequence" : "Clone",
			 name(keySet(genClone,i))) ;
		fprintf (fil, "Map %s Left %d\n", name(cinf->map), x1) ;
		fprintf (fil, "Map %s Right %d\n", name(cinf->map), x2) ;
	      }
	  }

	bsDestroy (obj) ;
	if (messIsInterruptCalled ())
	  break ;
      }

  arrayDestroy (cInfo) ;
  keySetDestroy (genClone) ;

  fprintf (fil, "\n// end of file\n") ;
}

void sMapMakeAll (void)		/* make sequence maps */
{
  FILE *fil ;
  BOOL isSeq ;

  if (!(fil = filqueryopen (0, 0, "ace", "w", "Ace file for result")))
    return ;

  isSeq = messQuery ("Do you want Sequence objects on the map (Yes),"
		     " or Clone objects (No)?") ;

  sMapMake (fil, isSeq) ;
  filclose (fil) ;
}

/*****************************************/
/************* action routines ***********/
/*****************************************/

static void alignDestroy (void)
{    
  alignGraph = 0 ;
  myText = textNonGraphic ;
}

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

extern 
  void pMapMakeAll(void), cMapMakeAll(void), gMapMakeAll(void);

static MENUOPT alignMenu[] =
  {
   {graphDestroy, "Quit"},
   {help, "Help"},
   {graphPrint,"Print"},
   {pMapMakeAll,"Make pMaps"},
   {vMapMakeAll,"Make vMaps"},
   {gMapMakeAll,"Make gMaps"},
   {cMapMakeAll,"Make cMaps"},
   {sMapMakeAll,"Make sequence map ace file"},
   {alignAllContigs, "Align contigs - write ace file"},
    {0, 0} 
   } ;

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

static void alignDisplay (void)
{
  if (!graphActivate (alignGraph))
    messcrash ("alignDisplay lost its graph.") ;
 
  graphPop () ;
  graphClear () ;
  graphButtons (alignMenu,3.,1.,40.) ;

  graphText ("To halt any action hit F4.", 5, 8.85) ;
  
  line = 8 ;
  graphTextBounds (80, line += 1 ) ;
  graphRedraw () ;
}

/************************************************/
/***********  public routines   ****************/

void alignMaps (void)
{
  myText = textGraphic ;

  if (graphActivate (alignGraph))
    { graphPop () ;
      return ;
    }

  alignGraph = displayCreate("DtAlign");

  graphTextBounds (80,50) ;   /* needed to for text box sizing */
  graphRegister (DESTROY,alignDestroy) ;
  graphMenu (alignMenu) ;  
 
  alignDisplay() ;
}

void alignMapsNonInteractive (void)
{
  void (*oldText)(char*,int,int) = myText ;

  myText = textNonGraphic ;
  messStatus ("Aligning maps") ;
  alignAllContigs () ;
  myText = oldText ;
}

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