/*    This program will read all blocks in the input file and write
      out the PSSM of each.

      Aug 20, 96' - blimps 3.1 defines matrix->weights as double (was int)
                    matrix->weights values are rounded before printing.

      Mar 21. 97' - changed passed arguments interface and added option
                    for specifying aa frequencies file.
                    replaced fprintf_matrix procedure with slightly
                    modified (some debugs) from BASE_PRIDE v03.

      Mar 23      - introduced sequence type (a or nuc) option. switched to
                    output_matrix_st from output_matrix.

      Mar 24      - added output_mast_matrix_st and option for it.

      Mar 28      - made fprint_matrix1 (from fprint_matrix), uses alphabet variable.
====================================================================*/

#define EXTERN
#define MAXNAME 80	/* Maximum file name length */

#define YES 1
#define NO 0

#define AA_ALPHABET_FULL "ARNDCQEGHILKMFPSTWYVBZX"
#define AA_ALPHABET_SHRT "ARNDCQEGHILKMFPSTWYVX"

#define NT_ALPHABET_FULL "ABCDGHKMNRSTUVWY"
#define NT_ALPHABET_SHRT "ACGTN"

/* # of positions (4 chars each) per line */
#define WWW_MATRIX_PRINT_WIDTH 165
#define SHL_MATRIX_PRINT_WIDTH 18

#define INP_DFLT_FNAME "stdin"
#define INP_DFLT        stdin

#define OUT_DFLT_FNAME "stdout"
#define OUT_DFLT        stdout

#define AA_FREQUENCY_FNAME_DFLT "default.amino.frq"
                                 /* file name of aa frequencies for
                                    load_frequencies procedure */

#define OUTPUT_FORMAT_DFLT 'G'     /* default output format 
                                      (G : generic format) */

#define OUTPUT_FORMAT_TYPES " G g B b M m " 


#define ALPHABET_TYPE_DFLT "FULL" /* default alphabet type */

#define ALPHABET_TYPES " F f FULL full S s SHORT short " /* each type must be flanked by blanks ! */

#define SEQS_TYPE_DFLT 'A'      /* default sequences type */

#define SEQS_TYPES " A a N n "  

#define PSSM_TYPE_DFLT 2        /* default block to matrix conversion method
                                   2 : use seq weights and odds-ratio*/

#define PSSM_TYPES " 2 3 5 6 10 20 30 " /* each type must be flanked by blanks ! */

#define PSSM_DEFS {"","","odds ratios normalized to 100","pseudos, log odds, nats","","pseudos, log odds, half bits","pseudos, log odds, third bits","","","","pseudos, odds ratios, nats","","","","","","","","","","just counts+pseudo counts","","","","","","","","","","average score"}

#define USAGE "%s [blocks-input_file_name [output_file_name]] [-Ppssm_type] [-Faa_frequency_file] [-Ooutput_format] [-Ssequences_type] [-Aalphabet_type]\n\n"

#define GET_HELP "((input now expected from screen; CTRL-C to quit))\n\n"

#define DEFAULTS "Defaults: in-\"%s\" out-\"%s\" pssm_type-\"%d\" aa_frequency-\"%s\" output_format-\"%c\" sequences_type-\"%c\" alphabet_type\"%s\"\n\n"

#define COMMENTS "Comments: \"-\" instead of a input/output file name => stdin/stdout\n          The order of the arguments is not important but the\n          first name is for the input file and the second for the output.\n      output_format = G: generic\n                      B: BLIMPS input\n                      M: MAST input\n     sequences_type = A: amino acids for protein sequences\n                      N: nucleotides  for DNA or RNA sequences\n      alphabet_type = F/FULL including degenerate codes\n                       aa: %s\n                       nt: %s\n                      S/SHORT only specific or unknown aa and DNA nucleotides\n                       aa: %s\n                       nt: %s\n          pssm_type = 2 odds ratios normalized to 100\n                      3 pseudos, log odds, nats\n                      5 pseudos, log odds, half bits\n                      6 pseudos, log odds, third bits\n                     10 pseudos, odds ratios, nats\n                     20 just counts+pseudo counts\n                     30 average score\n\n"

#include "blocksprogs.h"

/*
 * Local variables and data structures
 */

void    fprint_matrix1() ;
void    output_mast_matrix_st() ;
void    getargs() ;

Boolean WWW_FLAG = YES ;


/*=======================================================================*/
/*
 * main
 *   controls flow of program
 *   Parameters: argc, argv
 *   Error codes:
 */

int main(argc, argv)
     int argc;
     char *argv[];
{
  FILE  *bfp=INP_DFLT, *outf=OUT_DFLT ;
  FILE  *fqij;
  char  bdbname[MAXNAME]=INP_DFLT_FNAME, outfname[MAXNAME]=OUT_DFLT_FNAME ;
  char  frqname[MAXNAME]="", qijname[MAXNAME]="";
  char  *pssm_defs[]=PSSM_DEFS ;
  char  seqs_type = SEQS_TYPE_DFLT, output_format = OUTPUT_FORMAT_DFLT ;
  char  alphabet_type[MAXNAME] = ALPHABET_TYPE_DFLT ;
  char  *blimps_dir, tmp[MAXNAME], *alphabet, *aa_alphabet, *nt_alphabet ;
  Block *block;
  Matrix *matrix ;
  int   pssm_type = PSSM_TYPE_DFLT, out_style=INT_OUTPUT; /*also possible FLOAT_OUTPUT*/
  int i1 ;

  blimps_dir = getenv("BLIMPS_DIR");

/* Amino acid frequency */
  if (blimps_dir != NULL) 
       sprintf(frqname, "%s/docs/%s", blimps_dir, "default.amino.frq");
  else sprintf(frqname, "%s", AA_FREQUENCY_FNAME_DFLT) ;

  if (argc == 1) /* show usage and what defaults are used */
     {
     fprintf(stderr, USAGE, argv[0]) ;
     fprintf(stderr, DEFAULTS, bdbname, outfname, pssm_type
                             , frqname, output_format, seqs_type
                             , alphabet_type) ;
     fprintf(stderr, COMMENTS, AA_ALPHABET_FULL, NT_ALPHABET_FULL, 
                               AA_ALPHABET_SHRT, NT_ALPHABET_SHRT) ;
     fprintf(stderr, GET_HELP) ;
     }
  else
     getargs(argc,argv,&bfp,&outf,bdbname,outfname,&pssm_type,
             frqname,&output_format,&seqs_type,alphabet_type) ;

/* check arguments */

  sprintf (tmp, " %d ", pssm_type) ;
  if (strstr(PSSM_TYPES, tmp) == NULL) 
     {
     fprintf(stderr, "Warning ! Unspecified pssm type (%d) requested.\nUsing default pssm type (%d).\n\n",
             pssm_type, PSSM_TYPE_DFLT) ;
     pssm_type = PSSM_TYPE_DFLT ;
     }

  if (strchr(OUTPUT_FORMAT_TYPES, output_format) == NULL) 
     {
     fprintf(stderr, "Warning ! Unknown output format (%c) requested.\nUsing default sequences type (%c).\n\n",
             output_format, OUTPUT_FORMAT_DFLT) ;
     output_format = OUTPUT_FORMAT_DFLT ;
     }

  sprintf (tmp, " %s ", alphabet_type) ;
  if (strstr(ALPHABET_TYPES, tmp) == NULL) 
     {
     fprintf(stderr, "Warning ! Unspecified alphabet type (%s) requested.\nUsing default pssm type (%s).\n\n",
             alphabet_type, ALPHABET_TYPE_DFLT) ;
     strcpy(alphabet_type,ALPHABET_TYPE_DFLT) ;
     }

  if (strcmp(alphabet_type,"S") !=0 && strcmp(alphabet_type,"SHORT") !=0 &&
      strcmp(alphabet_type,"s") !=0 && strcmp(alphabet_type,"short") !=0) 
     {
     aa_alphabet = AA_ALPHABET_FULL ;
     nt_alphabet = NT_ALPHABET_FULL ;
     }
  else
     {
     aa_alphabet = AA_ALPHABET_SHRT ;
     nt_alphabet = NT_ALPHABET_SHRT ;
     }

  if (strchr(SEQS_TYPES, seqs_type) == NULL) 
     {
     fprintf(stderr, "Warning ! Unspecified sequences type (%c) requested.\nUsing default sequences type (%c).\n\n",
             seqs_type, SEQS_TYPE_DFLT) ;
     seqs_type = SEQS_TYPE_DFLT ;
     }
  switch (toupper(seqs_type))
     {
     case 'A' :
        seqs_type = AA_SEQ ;
        alphabet = aa_alphabet ;
        break ;
     case 'N' :
        seqs_type = NA_SEQ ;
        alphabet = nt_alphabet ;
        break ;
     default : /* this shouldn't happen (but so does shit) */
        fprintf(stderr, "Using mino acids sequence type.\n") ;
        seqs_type = AA_SEQ ;
        alphabet = aa_alphabet ;
     }


/* open files */
/* Input */
  if (strcmp(bdbname,INP_DFLT_FNAME) !=0 && (bfp=fopen(bdbname, "r")) == NULL)
     {
     fprintf(stderr, "Cannot open input file %s\n", 
             bdbname);
     exit(-1);
     }

/* output */
  if (strcmp(outfname,OUT_DFLT_FNAME) !=0 && (outf=fopen(outfname, "w")) == NULL)
     {
     fprintf(stderr, "Cannot open output file %s\n", 
             outfname);
     exit(-1);
     }

/* qij */
  if (blimps_dir != NULL) sprintf(qijname, "%s/docs/", blimps_dir);

  if (pssm_type == 30) strcat(qijname, "default.iij"); /*average score*/
  else                 strcat(qijname, "default.qij");

  Qij = NULL;
  if ( (fqij=fopen(qijname, "r")) != NULL)
     { Qij = load_qij(fqij); fclose(fqij); }
  RTot = LOCAL_QIJ_RTOT;


  ErrorLevelReport = WARNING_ERR_LVL; /* report all errors */

                   /* load the frequencies for converting blocks to matrices */
  load_frequencies(frqname) ;

/* print parameters used */
  fprintf(outf, "residue frequencies: %s\n", frqname) ;
  fprintf(outf, "PSSM calculation: %s\n\n", pssm_defs[pssm_type]) ;

  while ((block = read_a_block(bfp)) != NULL)
     {
     matrix = block_to_matrix(block,pssm_type) ;

     switch (toupper(output_format))
	{
        case 'G' : 
          fprint_matrix1(matrix,outf,alphabet) ;
          break ;
        case 'B' :
           output_matrix_st(matrix,outf,out_style,seqs_type) ;
           break ;
        case 'M' :
           output_mast_matrix_st(matrix,outf,alphabet) ;
           break ;
	default :
           fprintf(stderr, 
               "Warning unrecognized outout format option - \"%c\".\n", 
               output_format);
           fprint_matrix1(matrix,outf,alphabet) ;
	}

     free_matrix(matrix) ;
     free_block(block) ;
     }

  fclose(bfp);
  exit(0);

}  /* end of main */

/*
 * fprint_matrix1
 *
 *  Prints a Matrix data structure to a file in a generic format. 
 *  adapted from fprint_matrix
 *
 *   Parameters: 
 *     Matrix *matrix:   the matrix to print
 *     FILE   *omfp:     the output matrix file pointer
 *     char   *alphabet: the printed matrix residues and their order
 *   Error Codes: none
 *
 */

void fprint_matrix1(matrix,omfp,alphabet)
     Matrix *matrix;
     FILE *omfp;
     char *alphabet ;
{
  int  pos, low, high, matrix_print_width ;
  char *ptr ;


  if (WWW_FLAG)
     matrix_print_width = WWW_MATRIX_PRINT_WIDTH ;
  else
     matrix_print_width = SHL_MATRIX_PRINT_WIDTH ;

  high = matrix_print_width;
  for (low=0; 
       low<high && low<matrix->width; 
       low = (high+=matrix_print_width) - matrix_print_width) {

  fprintf(omfp,"\n");

  fprintf(omfp,"%s\n", matrix->number);

  if (matrix->width > 99) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->width; pos++) {
      if (pos+1 > 99) {
	fprintf(omfp,"% 4d", (pos+1-(((pos+1)/1000)*1000)) / 100);
      }
      else {
	fprintf(omfp,"    ");
      }
    }
  fprintf(omfp,"\n");
  }

  if (matrix->width > 9) {
    fprintf(omfp,"  |");
    for (pos=low; pos<high &&  pos<matrix->width; pos++) {
      if (pos+1 > 9) {
	fprintf(omfp,"% 4d", (pos+1-(((pos+1)/100)*100)) / 10);
      }
      else {
	fprintf(omfp,"    ");
      }
    }
  fprintf(omfp,"\n");
  }
  
  fprintf(omfp,"  |");
  for (pos=low; pos<high &&  pos<matrix->width; pos++) {
    fprintf(omfp,"%4d", ((pos-((pos/10)*10))+1)%10);
  }
  
  fprintf(omfp,"\n");
  fprintf(omfp,"--+");
  for (pos=low; pos<high &&  pos<matrix->width; pos++) {
    fprintf(omfp,"----");
  }

  fprintf(omfp,"\n");

  for (ptr=alphabet; *ptr != '\0'; ptr++)
      {
      fprintf(omfp,"%c |", *ptr);
      for (pos=low; pos<high &&  pos<matrix->width; pos++) 
          {
          fprintf(omfp,"% 4d", 
                  round(matrix->weights[aa_atob[*ptr]][pos]));
          }
      fprintf(omfp,"\n");
      }

  }

}

/*****************************************************************************
 * get input and output file names and other program parameters.
 *
 * All parameters, except explicit input and output file names, 
 * should be preceded by a "-".
 * A sole "-" (implicitly) specifies a default for a file name.
 * The first explicit or implicit file name will be assigned to the input file
 * and the second to the output file.
 *****************************************************************************/

void getargs(argc,argv,inpf,outf,inpfname,outfname,pssm_type,frqname,
             output_format,seqs_type,alphabet_type)

int    argc;
int    *pssm_type;
char   *argv[];
char   inpfname[], outfname[], frqname[], *output_format ;
char   *seqs_type, alphabet_type[] ;
FILE   **inpf, **outf;

{
   int i1 ;
   int fnames=0 ;
   char *chr_ptr ;

   for(i1=1; i1<argc; i1++)                       /* go through all arguments*/
      {
      if (argv[i1][0] != '-')                        /* presumed a file name */
	 {
         if (fnames == 0)                    /* first name is for input file */
	    {
            strcpy(inpfname,argv[i1]) ;
            fnames++ ;
	    }
         else if (fnames == 1)             /* second name is for output file */
	    {
            strcpy(outfname,argv[i1]) ;
            fnames++ ;
	    }
         else                                           /* probably an error */
            fprintf(stderr, 
            "Warning unrecognized parameter - \"%s\".\n", 
            argv[i1]);
	 }
      else
	 {
         switch (toupper(argv[i1][1]))
	    {
            case 'P' :
               chr_ptr = argv[i1] + 2 ;
               *pssm_type = atoi(chr_ptr) ;
               break ;
            case 'F' :
               chr_ptr = argv[i1] + 2 ;
               strcpy(frqname,chr_ptr) ;
               break ;
            case 'O' :
               chr_ptr = argv[i1] + 2 ;
               *output_format = *chr_ptr ;
               break ;
            case 'S' :
               chr_ptr = argv[i1] + 2 ;
               *seqs_type = *chr_ptr ;
               break ;
            case 'A' :
               chr_ptr = argv[i1] + 2 ;
               strcpy(alphabet_type,chr_ptr) ;
               break ;
	    case '\0' :                           /* parameter is just a "-" */
               fnames++ ;
               break ;
	    default :
               fprintf(stderr, 
               "Warning unrecognized parameter - \"%s\".\n", 
               argv[i1]);
	    }
         }
      }

      return ;
}

/*=======================================================================
    modified from Jorja's output_mast_matrix procedure to use a string
   for the required residues and their order. All residues in the string
   (alphabet) must be present at the matrix structure:

    - A R N D C Q E G H I L K M F P S T W Y V B Z X * J O U 
========================================================================*/
void output_mast_matrix_st(matrix, outf, alphabet)
Matrix *matrix;
FILE *outf;
char *alphabet ;
{
   int pos, aa ;
   char *ptr ;

   fprintf(outf, "ALPHABET= %s\n", alphabet);
   fprintf(outf, "log-odds matrix: alength= %d w= %d\n",
                 strlen(alphabet),matrix->width);
 
   for (pos=0; pos < matrix->width; pos++)
       {
       for (ptr=alphabet; *ptr != '\0'; ptr++)
           {
           fprintf(outf, "% 6.3f ", 
                   matrix->weights[aa_atob[*ptr]][pos]);
           }
       fprintf(outf, "\n");
       }

}  /* end of output_mast_matrix_st */
