/* COPYRIGHT 1995-2001 Fred Hutchinson Cancer Research Center */
/* This program will convert a Prints protein motifs database to a 
   blocks database. By Shmuel Pietrokovski */

/* Feb 8 96' ver 5 (previous version 4)
   If verbose flag is on, program outputs to stderr the lines it reads
   until it reaches the first line of a prints entry ("gc; ..."). This 
   is implemented in order to catch errors in the number of prints in an 
   entry (line "gn; ..). If the number is smaller than the actual number the
   program would not read the last prints. If the verbose flag is on these
   prints will be shown, if there are no such errors only the entry-separating
   lines (if any) will be shown.

 Feb 8 96' ver 4 (previous version 3)
   Changed in main the loop calling transform_a_prints_entry. The program 
   now doesn't exit when error is encountered but reports it and goes on to
   the next entry. Depending on the error, some motifs from the erroneus entry
   might be transformed. The data for these in the .info file might not be
   accurate.

 Aug 21 95' ver 3 (previous version 2)
   PRINTS version 9 introduced an accession field (gx) the accession is 7
   characters long, begins with "PR" and is followed by 5 digits. This is
   used as the blocks accession (previously the block accession was assigned
   according to the order of the entry int database 
*/

#define EXTERN


#include <blocksprogs.h>

#define OK    0
#define ERROR 1
#define MAXNAME 80
#define MAXLINELEN 5000

/* variables set by the configuration file in blimps program */

int BlockToMatrixConversionMethod=2 ; /* default method is two */


int    entries=0, motifs=0 ;
Boolean verbose=FALSE ;
char   transform_a_prints_entry() ; 

/*
 * main
 */

int main(argc, argv)
     int argc;
     char *argv[];
{

   FILE   *inpf, *outf[2] ;
   char   inpfname[MAXNAME], outfname[2][MAXNAME] ;

/* ---------------1st arg = Prints database file name ------------------------*/
   if (argc > 1)
      strcpy(inpfname, argv[1]);
   else
      {
      printf(
       "\nEnter name of input file (Prints protein motifs database): ");
      gets(inpfname);
      }

   if ( (inpf=fopen(inpfname, "r")) == NULL)
      {
      printf("\nCannot open input file %s\n", inpfname);
      exit(-1);
      }

/* ---------------2nd arg = verbose flag state, unprompted ------------------*/
   if (argc > 2) verbose = atoi(argv[2]);

/* open output files */

   sprintf(outfname[0], "%s.dat", inpfname) ;

   if ( (outf[0]=fopen(outfname[0], "w")) == NULL)
      {
      printf("\nCannot open output file %s\n", outfname[0]);
      exit(-1);
      }

   sprintf(outfname[1], "%s.info", inpfname) ;

   if ( (outf[1]=fopen(outfname[1], "w")) == NULL)
      {
      printf("\nCannot open output file %s\n", outfname[1]);
      exit(-1);
      }

   fprintf(outf[1],
      "Block family PRINTS entry name    # of   Cross references to -\n") ;
   fprintf(outf[1],
      "                                  blocks PROSITE    BLOCKS     PRINTS\n"
          ) ;
   fprintf(outf[1],
      "------------ -------------------- ------ ---------- ---------- ----------\n") ;


                                             /* go through all entries in db */
/* note that each entry usually has a number of motifs each of which is 
   transformated into a block. The transformation is done in the procedure.
   A Prints entry, termed finger print, is analogous to a block family and each
   finger print motif is analogous to a block.                               */

                             /* read a Prints entry and write out its blocks */
  while (! feof(inpf))
      if (transform_a_prints_entry(inpf,outf[0],outf[1]) == OK) entries++ ;

  fclose(inpf) ; 
  fclose(outf[0]) ; 
  fclose(outf[1]) ; 

  printf("\n%d entries with %d motifs read from Prints database %s\n", 
         entries, motifs, inpfname) ;
  printf("and written as blocks in file %s.\n", outfname[0]) ;
  printf("Information about the processed entries written in file %s.\n", 
         outfname[1]) ;

  exit(0) ;

}  /* end of main */



/****************************************************************************
 *
 * Read a PRINTS entry from the input file, for each motif put all relevant 
 * data in a block structure and write it out to the output file. 
 * While doing that the program also writes to another file some general 
 * information about each translated entry - its name, the block family name 
 * assigned to it, how many motifs (blocks) did it comprise of, and cross
 * references (if any) to BLOCKS and PROSITE entries. 
 * 
 ****************************************************************************/

char transform_a_prints_entry(dbin,dbout,outf2)

#define BLOCK_SEQUENCE_INCREASE_SIZE 50   /* default number of sequences to */
                                  /* allocate since the number of sequences */
                                  /* in the entries motifs is not specified */
                                  /* when starting to read them.            */


FILE   *dbin, *dbout, *outf2 ;

{
   Block    *block ;
   char     line[MAXLINELEN], word[MAXLINELEN], entry_name[MAXLINELEN] ;
   char     fam_acc[8] ;
   char     PS_crssref[11] = "-", BL_crssref[11] = "-" ;
   char     PR_crssref[MAXLINELEN] = "" ;
   char     *ptr ;
   int      i1, i2, num_blocks, num_seqs, rc, max_pos, min_pos, temp ;
   Boolean  SIMPLE_entry ;
   Sequence *sequence_pointer ;
   Residue  *residue_pointer ;


             /* read until a prints entry first line - begins with a "gc; " */
/* only the first two chars compared here and elsewhere in the program because
  a few typos (colon ":" instead of semi colon ";") were found in PRINTS7.0 */ 

   while((fgets(line, MAXLINELEN, dbin)) != NULL && 
         strncmp(line, "gc;", 2) != 0) 
   {
      if (verbose) fprintf(stderr, "%s", line) ;
      /*   Check for extra motifs from previous entry;
	   the gn; COMPOUND() line may not
	   match the actual number of motifs in the entry   */
      if (strncmp(line, "fc;", 2) == 0)
      {
          printf("Extra motif: %s\n", line);
      }
   }

                           /* check if required line or end of file reached */
   if (strncmp(line, "gc;", 2) != 0) return(-1) ;

                              /* get the entry's name, 2nd word in gc line */
   ptr = get_token(line) ;

   if ((ptr = get_token(NULL)) == NULL) 
      {
      printf("Error ! Problem in format of Prints entry gc line,\n") ;
      printf("can't find the general code\n") ;
      return(-2) ;
      }

   strcpy(entry_name,ptr) ;

               /* read until a prints accession line - begins with a "gx; " */
   while((fgets(line, MAXLINELEN, dbin)) != NULL && 
         strncmp(line, "gx;", 2) != 0) ;

                           /* check if required line or end of file reached */
   if (strncmp(line, "gx;", 2) != 0)
      {
      printf(
           "Error ! Accession line (gx; ...) not found in Prints entry %s.\n",
             entry_name) ; 
      return(-3) ;
      }

                           /* get the entry's accession, 2nd word in gc line */
   ptr = get_token(line) ;

   if ((ptr = get_token(NULL)) == NULL) 
      {
      printf("Error ! Problem in format of Prints entry %s gx line.\n",
             entry_name) ;
      printf("Can't find the accession code.\n") ;
      return(-2) ;
      }

            /* check that the accession code in no longer than 7 characters */ 
   if ((int) strlen(ptr) > 7)
      {
/*
      printf(
      "Error ! Accession code of Prints entry %s (\"%s\") is too long (>7).\n",
             entry_name) ;
      return(-2) ;
*/
      ptr[7] = '\0';
      }
   else if ((int) strlen(ptr) < 7)    /* add "_"s until code is 7 chars long*/
      {
      printf("Note - the accesion code of Prints %s (\"%s\")\n",
             entry_name, ptr) ;
      printf("is shorter than 7 characters.\n") ;

      for (i1=(int) strlen(ptr); i1<=7; i1++) ptr[i1] = '_' ;
      ptr[i1] = '\0' ;
      }

   strcpy(fam_acc,ptr) ;

              /* read until a prints entry type line - begins with a "gn; " */
   while((fgets(line, MAXLINELEN, dbin)) != NULL && 
         strncmp(line, "gn;", 2) != 0) ;

                           /* check if required line or end of file reached */
   if (strncmp(line, "gn;", 2) != 0)
      {
      printf("Error ! Type line (gn; ...) not found in Prints entry %s.\n",
             entry_name) ; 
      return(-3) ;
      }

   if (strstr(line,"SIMPLE") != NULL)           /* a simple entry - 1 block */
      {
      num_blocks = 1 ;
      SIMPLE_entry = TRUE ;
      }
   else if (strstr(line,"COMPOUND") != NULL)/* a compound entry - blocks > 1*/
      {
                                        /* get the entry's number of motifs */
      if ((ptr = strstr(line, ")")) == NULL) 
         {
         printf(
           "Error ! Problem in format of gn line in Prints entry %s -\n",
                entry_name) ; 
         printf("%s", line) ;
         printf("can't find closing )\n") ;
         return(-3) ;
         }

      *ptr = '\0' ;
      if ((ptr = strstr(line,"(")) == NULL) 
         {
         printf("Error ! Problem in format of gn line, can't find (\n") ;
         printf("in Prints entry %s.\n", entry_name) ; 
         return(-3) ;
         }

      num_blocks = atoi(ptr + 1) ;  /* convert number of motifs to a number */

      SIMPLE_entry = FALSE ;
      }
   else
      {
      printf("Error ! Type line (gn; ...) is neither SIMPLE or COMPOUND\n") ;
      printf("in Prints entry %s.\n", entry_name) ; 
      return(-3) ;
      }

                                               /* allocate space for blocks */
   CheckMem(block = (Block *) calloc(num_blocks, sizeof(Block))) ;

  /* save blocks general code and accessions, give the values common to all */

   for(i1=0; i1 < num_blocks; i1++)
      {
      if (num_blocks > 1)
                   /* 65+i1 translates 0 into ascii value of A, 1 to B etc. */
         sprintf(block[i1].number, "%s%c", fam_acc, 65+i1) ;
      else      /* No letter suffix for accessions of single block families */
         sprintf(block[i1].number, "%s", fam_acc) ;
      sprintf(block[i1].ac, "%s;", block[i1].number) ;
      sprintf(block[i1].id, "%s; BLOCK", entry_name) ;

      block[i1].num_clusters = block[i1].max_clusters = 1 ; 
      block[i1].motif[0] = '\0';
      block[i1].percentile = 0 ;
      block[i1].strength = 0 ;
      }

       /* read until a prints descriptive title line - begins with a "gt; " */
   while((fgets(line, MAXLINELEN, dbin)) != NULL && 
         strncmp(line, "gt;", 2) != 0) ;

                           /* check if required line or end of file reached */
   if (strncmp(line, "gt;", 2) != 0)
      {
      printf("Error ! title line (gt; ...) not found in Prints entry %s.\n",
             entry_name) ; 
      return(-4) ;
      }

   if ((ptr=strchr(line,'\n')) != NULL) *ptr = '\0' ;     /* get rid of EOL */

   ptr = line + 4 ;       /* get description - begining in 5th char of line */

                                                 /* save blocks description */
   for(i1=0; i1 < num_blocks; i1++) 
      strncpy(block[i1].de, ptr, SMALL_BUFF_LENGTH) ;
                           /* in case word is longer than SMALL_BUFF_LENGTH 
                                      and thus the last char copied isnt \0 */ 
   block[i1].de[SMALL_BUFF_LENGTH-1] = '\0' ;

            /* read until a prints final motifs line - begins with a "fm; " */
   while((fgets(line, MAXLINELEN, dbin)) != NULL && 
         strncmp(line, "fm;", 2) != 0) 
      if (strncmp(line, "gp;", 2) == 0) /* search for cross reference lines */
      {
         if (strstr(line, "PROSITE") != NULL) /* cross reference to PROSITE */ 
	    {
              /* get the cross referenced entry's name, 3rd word in gp line */
            ptr = get_token(line) ;
            ptr = get_token(NULL) ;
            if ((ptr = get_token(NULL)) != NULL) strncpy(PS_crssref,ptr,11) ;
	    }
         else
         if (strstr(line, "BLOCKS") != NULL)   /* cross reference to BLOCKS */ 
	    {
              /* get the cross referenced entry's name, 3rd word in gp line */
            ptr = get_token(line) ;
            ptr = get_token(NULL) ;
            if ((ptr = get_token(NULL)) != NULL) strncpy(BL_crssref,ptr,11) ;
	    }
         else
         if (strstr(line, "PRINTS") != NULL)   /* cross reference to PRINTS */ 
	    {
/* get the cross referenced entries name, begining with 3rd word in gp line */
            ptr = get_token(line) ;
            ptr = get_token(NULL) ;
            while ((ptr = get_token(NULL)) != NULL)
	       {
              /* strip possible separator (";", "," etc.) from end of word */
               if (!(isalnum(ptr[(int) strlen(ptr) - 1]))) 
                  ptr[(int) strlen(ptr) - 1] = '\0' ;
               strcat(PR_crssref, ptr) ;
               strcat(PR_crssref, " ") ;
	       }
	    }
      }

                           /* check if required line or end of file reached */
   if (strncmp(line, "fm;", 2) != 0)
      {
      printf(
        "Error ! Final motifs line (fm; ...) not found in Prints entry %s.\n",
             entry_name) ; 
      return(-5) ;
      }

                                             /* write out entry information */
   fprintf(outf2, "%-12s %-20s %5d  %-10s %-10s %s\n", 
          fam_acc, entry_name, num_blocks, PS_crssref, BL_crssref, 
          PR_crssref) ;

   for(i1=0; i1 < num_blocks; i1++)                       /* for each motif */
      {

      min_pos = 10000 ;
      max_pos = 0 ;

              /* read until a final motif code line - begins with a "fc; " */
      while((fgets(line, MAXLINELEN, dbin)) != NULL && 
            strncmp(line, "fc;", 2) != 0) ;

                           /* check if required line or end of file reached */
      if (strncmp(line, "fc;", 2) != 0)
         {
         printf("Error ! final motif code line (fc; ...) not found in\n") ;
         printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
         return(-6) ;
         }

                               /* check that the motif belongs to the entry 
                                   (entry name is in the motif's code line) */
      if (strstr(line, entry_name) == NULL)
         {
         printf("Error ! Problem in motif %d of Prints entry %s.\n", 
                i1+1, entry_name) ; 
         printf(
     "  The motif's code line (fc; ...) does not contain the entry name = %s\n",
		entry_name) ;
         printf("%s", line) ;
         return(-6) ;
         }

             /* read until a final motif length line - begins with a "fl; " */
      while((fgets(line, MAXLINELEN, dbin)) != NULL && 
            strncmp(line, "fl;", 2) != 0) ;

                           /* check if required line or end of file reached */
      if (strncmp(line, "fl;", 2) != 0)
         {
         printf("Error ! final motif length line (fl; ...) not found in\n") ;
         printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
         return(-6) ;
         }

                             /* get the motif's length, 2nd word in fl line */
      ptr = get_token(line) ;

      if ((ptr = get_token(NULL)) == NULL || 
          (block[i1].width = atoi(ptr)) <= 0) 
         {
         printf(
       "Error ! Problem in format of final motif length line (fl; ...) in\n") ;
         printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
         return(-6) ;
         }

                        /* following part adapted from 
                            blimps procedure read_block_body by Bill Alford */

                                 /* since number of sequences not known yet */
      block[i1].max_sequences = BLOCK_SEQUENCE_INCREASE_SIZE ;
 
        /* allocate space for all the Sequences of the block sequence array */
      CheckMem(
            sequence_pointer = 
            (Sequence *) calloc(block[i1].max_sequences, sizeof(Sequence))) ;

                                    /* initialize the block sequences array */
      block[i1].sequences = sequence_pointer;

                                         /* allocate space for the clusters */
      CheckMem(block[i1].clusters = (Cluster *) 
                            calloc(block[i1].max_clusters, sizeof(Cluster))) ;

                                         /* allocate space for all residues */
      CheckMem(
            residue_pointer = (Residue *)  
            calloc(block[i1].width * block[i1].max_sequences,
                                                         sizeof(Residue))) ;

                                       /* initialize the residues 2-d array */
      CheckMem(
            block[i1].residues = (Residue **) calloc(block[i1].max_sequences,
                                                       sizeof(Residue *))) ;

	                                /* initialize sequences and residues */
      for(i2=0; i2<block[i1].max_sequences; i2++) 
         {
	 sequence_pointer[i2].length = block[i1].width;
         sequence_pointer[i2].sequence =
 		           &(residue_pointer[i2 * block[i1].width]) ;
	 block[i1].residues[i2] = 
                           &(residue_pointer[i2 * block[i1].width]) ;
         }
 
      num_seqs = 0 ;
                          /* read until a break line - begins with a "bb; " */
      while((fgets(line, MAXLINELEN, dbin)) != NULL && 
            strncmp(line, "bb;", 2) != 0) 
                                         /* process the sequence data lines */
         if (strncmp(line, "fd;", 2) == 0)
  	    {
                                   /* get the sequence, 2nd word in fd line */
            ptr = get_token(line) ;

            if ((ptr = get_token(NULL)) == NULL) 
               {
               printf("Error ! Problem in format of sequence line %d in\n", 
                      num_seqs+1) ;
               printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
               printf("Sequence not found in line.\n") ; 
               return(-7) ;
               }
 
            if ((int) strlen(ptr) != block[i1].width)               
               {
               printf(
                "Error ! Sequence %d in motif %d of Prints entry %s.\n",
                     num_seqs+1, i1+1, entry_name) ;
               printf("is %d chars long, instead of the %d expected\n",
                     (int) strlen(ptr), block[i1].width) ;
               return(-7) ;
               }

    /* next part adopted from blimps  procedure next_cluster by Bill Alford */
                     /* read the sequence from the string into the Sequence */

   /* it is a sequence, so make sure there is enough space for the sequence */
    /* indexing starts at 1 for block[i1].max_sequences and 0 for num_seqs  */
                                               /* more sequences than space */
                                                          /* get more space */
            if (block[i1].max_sequences < num_seqs+1)
               resize_block_sequences(&block[i1]) ;


            rc = 
             read_sequence(&(block[i1].sequences[num_seqs]), AA_SEQ, 0, ptr) ; 
				   /* the Sequence, sequence type, starting */
   				   /* position, string with the sequence    */

                                      /* check to see if there was an error */ 
            if (rc < 0) 
               {
                      /* Error, more residues in the sequence than expected */
               printf("Error reading sequence %s in block %s,", 
                      block[i1].sequences[num_seqs].name, block[i1].number) ;
               printf(
                  "%d more residues in the sequence than the expected %d.\n",
	              -rc, block[i1].sequences[num_seqs].length) ;
               }
            else if (rc < block[i1].sequences[num_seqs].length) 
               {
        /* Error, not enough residues for the sequence, filling with blanks */
               printf("Error reading sequence %s in block %s,", 
                      block[i1].sequences[num_seqs].name, block[i1].number) ;
               printf("not enough residues to fill the sequence.");
               printf("Filling the rest of the sequence with blanks.\n");

                                                     /* filling with blanks */
    	       for(i2=rc; i2<block[i1].sequences[num_seqs].length; i2++) 
	          block[i1].sequences[num_seqs].sequence[i2] = aa_atob['-'];
               }
            else if (rc > block[i1].sequences[num_seqs].length) 
               {
              /* BIG Error, an undocumented return value from read_sequence */
               printf(
              "read_a_prodom_entry(): Error reading sequence %s in block %s,",
 	             block[i1].sequences[num_seqs].name,block[i1].number) ;

               printf("Undocumented return value, %d, from read_sequence().\n",
                      rc) ;
               } /* else, ret_value == seq.length, OK */

                 /* sequences have no weights give all equal weights of 100 */
            block[i1].sequences[num_seqs].weight =  100.0 ;

       /* continue reading the rest of the sequence line - sequence name, 
          start position and distance from previous motif or sequence start */

                                               /* read next word - seq name */
            if ((ptr = get_token(NULL)) == NULL)
               {
               printf("Error ! Problem in format of sequence line %d in\n", 
                      num_seqs+1) ;
               printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
               printf("Sequence name not found in line.\n") ; 
               return(-7) ;
               }
 
            strncpy(block[i1].sequences[num_seqs].name, ptr, 
                    SMALL_BUFF_LENGTH) ;

                           /* in case word is longer than SMALL_BUFF_LENGTH 
                                      and thus the last char copied isnt \0 */ 
            block[i1].sequences[num_seqs].name[SMALL_BUFF_LENGTH-1] = '\0' ; 

                                         /* read next word - start position */
            if ((ptr = get_token(NULL)) == NULL ||
                (block[i1].sequences[num_seqs].position = atoi(ptr)) <= 0) 
               {
               printf("Error ! Problem in format of sequence line %d (%s) in\n", 
                      num_seqs+1, block[i1].sequences[num_seqs].name) ;
               printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
               printf("Error reading sequence start position.\n") ; 
               return(-7) ;
               }

            if (SIMPLE_entry)
               temp = block[i1].sequences[num_seqs].position ; 
     /* SIMPLE entries don't have the distance field in their single motif */
            else                              /* read next word - distance */
	       {
               if ((ptr = get_token(NULL)) == NULL) 
                  {
                  printf("Error ! Problem in format of sequence line %d (%s) in\n", 
                         num_seqs+1, block[i1].sequences[num_seqs].name) ;
                  printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
                  printf("Error reading sequence distance from last motif.\n") ; 
                  return(-7) ;
                  }

               temp = atoi(ptr) ; /* (no simple test for the distance value 
                                because in PRINTS it can also be negative) */
	       }

                          /* find min and max start positions of sequences */
            if (temp > max_pos) max_pos = temp ;
            if (temp < min_pos) min_pos = temp ;

            num_seqs++ ;
            }
                           /* check if required line or end of file reached */
      if (strncmp(line, "bb;", 2) != 0)
         {
         printf("Error ! End of file reached while reading") ;
         printf("motif %d of Prints entry %s.\n", i1+1, entry_name) ; 
         return(-8) ;
         }

      block[i1].num_sequences = block[i1].max_sequences = num_seqs ;

          /* assign the sequences to the cluster (space has been allocated) */

      block[i1].clusters[0].num_sequences = block[i1].num_sequences ;

      block[i1].clusters[0].sequences = &(block[i1].sequences[0]) ;



/* add min and max distance from previous block (sequence start) to AC line */
      sprintf(word, " distance from previous block=(%d,%d)", 
              min_pos, max_pos);
      strcat(block[i1].ac, word) ;

      sprintf(block[i1].bl, "adapted from PRINTS entry; width=%d; seqs=%d;",
              block[i1].width, block[i1].num_sequences) ;

/*output_block(block[i1], stdout) ;*/ 
      output_block(block[i1], dbout) ;        /* write block to output file */

      motifs++ ;                       /* update number of blocks processed */

                            /* free memory allocated to the block structure */
      free(block[i1].residues);
      free(block[i1].clusters);
      free(block[i1].sequences[0].sequence);
      free(block[i1].sequences);
      }  /* end for i1 = num_blocks */

                    /* free memory allocated to the arrays block structures */
   free(block);

   return(OK) ;
} /* end of transform_a_prints_entry */

