/*  File: keysetdump.c
 *  Author: Jean Thierry-Mieg (mieg@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: Needed in acequery and xace
 * Exported functions:
 * HISTORY:
 * Last edited: Jul  9 11:53 2003 (rnc)
 * * Aug 19 17:36 1998 (rd): changed condition on dumping keys from
 *	(!pickList[class(*kp)].protected && !lexIsKeyInvisible(*kp))
 *      to (lexIsKeyVisible (*kp)) and prevented lexaddkey() on parsing
 *	protected keys, silently.
 * * Nov 12 14:23 1991 (mieg): added heap system
 * * Oct 23 18:12 1991 (mieg): added keySetParse
 * Created: Mon Oct 21 19:55:34 1991 (mieg)
 *-------------------------------------------------------------------
 */


/* $Id: keysetdump.c,v 1.32 2003/07/22 02:59:05 mieg Exp $ */

#include "acedb.h"
#include "aceio.h"
#include "bs.h"	  /* need bsKeySet */
#include "lex.h"  /* needed in keysetdump and keysetorder */
#include "pick.h" /* needed in keysetdump */
#include "a.h"    /* needed in keySetParse */
#include "whooks/sysclass.h"
#include "java.h"
#include "dump.h"
#include "utils.h"           /* for lexstrcmp */

/************************************************************/
  /* Heap sort
   * source is an array to be partially sorted,
   * nn is the desired number of sorted objects,
   * order is the order function
   * min and max are extrema of the sorting functions
   */

/**************************************************************/
/************ Dedicated heap function *************************/

KEYSET  keySetHeap(KEYSET source, int nn, int (*order)(KEY *, KEY *))
{ KEYSET h, result ;
  int i , k = 1 , max , pos ;
  KEY  new ;
    
  if(!source || !arrayMax(source))
    return keySetCreate() ;
  
  if(!arrayExists(source))
    messcrash("keySetHeap received a non-magic source keySet") ;

  messStatus ("Heaping") ;
    /* Note that KEY 0 is used as maximal element */
  if(nn > arrayMax(source))
    nn = arrayMax(source) ;
  while( ( 1 << ++k) <= nn) ;
  max = 1 << k ;
  h = arrayCreate(max, KEY) ;
  arrayMax(h) = max ; 
  
  i = arrayMax(source) ;
  while (i --)
    { new = keySet(source, i) ;

        /*  HEAP_PUSH(new) */
      { pos = 1 ;
	while( ! arr(h, pos, KEY) ||
	      (order(&new,arrp(h, pos, KEY)) == -1 ) )
	  { if (pos>>1)
	      arr(h, pos>>1, KEY) = arr(h, pos, KEY) ;
	    arr(h, pos, KEY) = new ;
	    if ( (pos <<= 1) >= max )
	      break ;
	    if( ! arr(h, pos + 1, KEY) ||
	       ( order( arrp(h, pos, KEY), arrp(h, pos + 1 ,KEY)) == -1) )
	      pos++ ;
	  }
      }
    }

/* Test print out 
  for(i=0 ; i < arrayMax(h) ; i++)
    printf("%d %s \n", i, name(arr(h,i,KEY))) ;
*/  
              /* To dump,
	       * Create a minimal key
	       * and push it repeatedly
	       * collecting the top key 
	       */
  result = arrayCreate(max, KEY) ;
  arrayMax(result) = max - 1 ;
  
  i = max - 1 ;  /* pos 0 is not filled with any relevant data */
  new = KEYMAKE(0, lexMax(0)) ;
  while(i--)
    { arr(result, i, KEY) = arr(h, 1, KEY) ;
        /*  HEAP_PUSH(minimal key) */
      { pos = 1 ;
	while( TRUE ) /* I pretend to push a minimal key */
	  { if (pos>>1)
	      arr(h, pos>>1, KEY) = arr(h, pos, KEY) ;
	    arr(h, pos, KEY) = new ;
	    if ( (pos <<= 1) >= max )
	      break ;
	    if( arr (h, pos, KEY) == new ||
	       (order( arrp(h, pos, KEY), arrp(h, pos + 1 ,KEY)) == -1))
	      pos++ ;
	  }
      }
    }

  if(arrayMax(source) < arrayMax(result))
    (arrayMax(result) = arrayMax(source)) ;
  
  keySetDestroy(h) ;

  return result ;
}

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

KEYSET keySetNeighbours(KEYSET oldSet)
{
  KEYSET new = keySetCreate() , nks ; KEY key, *kp ;
  int  i = keySetMax(oldSet) , j1 = 0, j2;
 
  i = keySetMax(oldSet) ;
  while(i--)
    { nks = (KEYSET) bsKeySet(keySet(oldSet, i)) ;
      if (nks)
	{ j2 = keySetMax(nks) ; kp = arrp(nks,0,KEY) - 1 ;
	  while(kp++, j2--)
	    if (!pickXref(class(*kp)) && 
		iskey(*kp) == 2 &&
		class(*kp) != _VUserSession)
	      keySet(new, j1++) = *kp ;
	  keySetDestroy(nks) ;
	}
      if (messIsInterruptCalled())
	break ;
    }  
  i = keySetMax(oldSet) ;
  while(i--)
    {
      key = keySet(oldSet, i) ;
      if (class(key) == _VKeySet &&
	  (nks = arrayGet (key,KEY,"k")))
	{ 
	  j2 = keySetMax(nks) ; kp = arrp(nks,0,KEY) - 1 ;
	  while(kp++, j2--)
	    if (!pickXref(class(*kp)) && 
		iskey(*kp) == 2 &&
		class(*kp) != _VUserSession)
	      keySet(new, j1++) = *kp ;
	  keySetDestroy(nks) ;
	}
    }
  keySetSort(new) ;
  keySetCompress(new) ;
  keySetDestroy (oldSet) ;
  return new ;
}

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

       /* first order classes */
     /* then compares KEYs acoording to their Word meaning*/

int keySetAlphaOrder(void *a,void *b)
{
  register int i = class(*(KEY*)a), j = class(*(KEY*)b) ;

  if (i != j)   
    return lexstrcmp (className(*(KEY *)a),
                   className(*(KEY *)b)  ) ;
  else
    return lexstrcmp(name(*(KEY *)a),
                   name(*(KEY *)b)  );
} /* keySetAlphaOrder */

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

BOOL keySetDump (ACEOUT dump_out, KEYSET s)
{
  int i = keySetMax(s) ;
  KEY *kp  = arrp(s,0,KEY)  - 1 ; 

  if(s->size != sizeof(KEY))
    return FALSE ;

  while (kp++, i--)
    if (lexIsKeyVisible(*kp))
      aceOutPrint (dump_out, "%s : %s\n",
		   className(*kp),
		   freeprotect(name(*kp))) ;

  return TRUE ;
}

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

BOOL javaDumpKeySet (ACEOUT dump_out, KEY key) 
{
  KEYSET ks = arrayGet(key,KEY,"k") ;
  int i ;
  KEY *kp;
  BOOL first = TRUE ;

  if (!ks) 
    return FALSE;

  aceOutPrint (dump_out, "?Keyset?%s?\t", freejavaprotect(name(key)));
 
  i = keySetMax(ks) ;
  kp = arrp(ks,0,KEY) - 1 ;
  while (kp++,i--)
    if (lexIsKeyVisible(*kp)) 
      { 
	if (!first) 
	  aceOutPrint (dump_out, "\t") ;
	first = FALSE ;
	aceOutPrint (dump_out, "?%s?%s?\n", className(*kp),
		     freejavaprotect(name(*kp)));
      }
  
  aceOutPrint (dump_out, "\n");

  keySetDestroy (ks);

  return TRUE ;
} /* javaDumpKeySet */

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

BOOL keySetDumpFunc (ACEOUT dump_out, KEY key)
     /* DumpFuncType for class _VKeyset */
{ 
  KEYSET ks = arrayGet(key, KEY, "k");
  BOOL result;

  if (!ks)
    return FALSE;

  result = keySetDump (dump_out, ks);
  keySetDestroy (ks);

  return result;
} /* keySetDumpFunc */

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

ParseFuncReturn keySetParse (ACEIN parse_io, KEY key, char **errtext)
     /* ParseFuncType */
{
  char *cp = 0 , c;
  KEYSET kS = keySetCreate() ;
  int  classe ; KEY k ;
  ParseFuncReturn result ;

  while (aceInCard(parse_io) && (cp = aceInWordCut(parse_io, ":-/\" ",&c)))
    {
      classe = pickWord2Class(cp) ;
      if (!classe)
	goto abort ;
      if (pickList[classe].protected) /* can't create protected keys */
	continue ;
      aceInStep(parse_io, ':') ;
      if((cp = aceInWord(parse_io)))
	{ 
	  lexaddkey(cp,&k,classe) ;
	  keySetInsert(kS,k) ;
	}
      else
	goto abort ;
    }

  if (keySetMax(kS))
    {
      arrayStore(key,kS,"k") ;
      result = PARSEFUNC_OK ;
    }
  else
    result = PARSEFUNC_EMPTY ;

  keySetDestroy(kS) ;

  return TRUE ;
  
 abort:
  *errtext = messprintf("keySetParse error at  Line %7d  KeySet %s, bad word %s\n", 
			aceInStreamLine(parse_io), name(key),
			cp ? cp : "void line") ;
  keySetDestroy(kS) ;

  return PARSEFUNC_ERR ;
} /* keySetParse */

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

KEYSET keySetRead (ACEIN keyset_in, ACEOUT error_out) 
     /* read a .ace formatted keyset listing as generated by the List 
      * command and return it as a keyset data structure
      * original by Detlef Wolf, DKFZ
      * new version 951017 from Detlef adpated in minor ways by rd
      * 
      * accept  Locus a;b;c;Sequence e;f  // all on one or multiple lines
     */
{ 
  KEYSET ks = keySetCreate() ;
  int n, table = 0 , tt;
  int nbad = 0 ;
  KEY key;
  char *word ;
    
  while (aceInCard (keyset_in))   /* will close file */
    { 
      while ((word = aceInWord(keyset_in)))
        { 
	  if ((tt = pickWord2Class (word)))
	    {
	      table = tt ;
	      aceInStep (keyset_in,':') ;
	      word = aceInWord(keyset_in) ;
	    }
	  if (tt) table = tt ;
	  if (!table)
            { ++nbad ; continue ; }
          if (!word)
            { 
	      aceOutPrint (error_out, 
			   "// No object name, line %d\n", 
			   aceInStreamLine (keyset_in)) ;
              continue ;
            }
          if (table == _VKeySet && !keySetMax(ks))
            continue ;
          if (lexword2key (word, &key, table))
            keySet(ks, keySetMax(ks)) = key ;
          else
            ++nbad ;
        }
    }

  if (nbad)
    aceOutPrint (error_out, 
		 "// %d objects not recognized\n", nbad) ;
				/* finally sort new keyset */
  n = keySetMax(ks) ;
  keySetSort(ks) ;
  keySetCompress(ks) ;

  if (n - keySetMax(ks)) 
    aceOutPrint (error_out, "// %d duplicate objects removed\n",
		 n - keySetMax(ks)) ;

  return ks ;
} /* keySetRead */

/**************************************************************/
/* kill all 'a' and 'b' type members of given keyset */
void keySetKill (KEYSET ks) 
{
  int i ;
  KEY key ;

  i = keySetExists (ks) ? keySetMax (ks) : 0 ;
   while (i--)
    { key = keySet (ks, i) ;
    switch ( pickType(key))
      {
      case 'A':
        arrayKill (key) ;
        break ;
      case 'B':
        { OBJ obj ;
        if ((obj = bsUpdate (key)))
          bsKill (obj) ;              
        }
        break ;
      }
    }
}

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