////////////////////////////////////////////////////////////////////////
//
// mscore.C - main program: parsing parameters, creating objects and
//            calling subroutines
//
// $Id: mscore.C,v 1.7 2002/01/20 11:13:54 goetz Exp $
//
////////////////////////////////////////////////////////////////////////

// include system stuff needed:
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <stdlib.h>
#include <unistd.h>

// include modules in this directory:
#include "Exception.h"
#include "Alignment.h"
#include "Score.h"
#include "ScoreMatrix.h"
#include "PwDistWeight.h"
#include "SecWeight.h"
#include "iotools.h"

// include modified version of CLUSTAL's score matrices
#include "matrices.h"

// include stuff used for parsing etc.
#include "parsetools.h"

// file setting program name and version
#include "version.h"

int main (int argc, char **argv)
{
  // these are markers to determine which parameters were given
  bool got_a(false), got_f(false), got_r(false), got_d(false),
    got_s(false), got_S(false), got_l(false), got_t(false),
    got_T(false), got_L(false), got_m(false), got_g(false),
    got_p(false), got_P(false), got_q(false), got_Q(false),
    got_v(false), got_V(false), got_G(false), 
    got_o(false), got_O(false);

  char *secfile(0), *reffile(0), *matrname(0), *alfile(0), *pwmatrname(0),
    *groupfile(0);
  double alscale(0), aloffs(0), betscale(0), betoffs(0), pwscale(0),
    inGroupScale(1), outGroupScale(0);
  int gappen(0), pwgappen(0);

  if (argc == 1) {
      printShortHelp();
      exit(-1);
  }    

  char c;
  while ((c= getopt(argc, argv, "haf:r:ds:S:lt:T:Lm:Mg:pP:q:Q:G:o:O:vV")) 
	 != EOF ) 
    switch(c) {
    case 'h':
      printLongHelp();
      exit(-1);
    case 'a':
      got_a= true;
      break;
    case 'f':
      got_f= true;
      secfile= new char[strlen(optarg)+1];
      strcpy(secfile, optarg);
      break;
    case 'r':
      got_r= true;
      reffile= new char[strlen(optarg)+1];
      strcpy(reffile, optarg);
      break;
    case 'd':
      got_d= true;
      break;
    case 's':
      got_s= true;
      alscale= atof(optarg);
      break;
    case 'S':
      got_S= true;
      aloffs= atof(optarg);
      break;
    case 'l':
      got_l= true;
      break;
    case 't':
      got_t= true;
      betscale= atof(optarg);
      break;
    case 'T':
      got_T= true;
      betoffs= atof(optarg);
      break;
    case 'L':
      got_L= true;
      break;
    case 'm':
      got_m= true;
      matrname= new char[strlen(optarg)+1];
      strcpy(matrname, optarg);
      break;
    case 'M':
      printMatrixNames();
      exit(-1);
    case 'g':
      got_g= true;
      gappen= atoi(optarg);
      break;
    case 'p':
      got_p= true;
      break;
    case 'P':
      got_P= true;
      pwscale= atof(optarg);
      break;
    case 'q':
      got_q= true;
      pwmatrname= new char[strlen(optarg)+1];
      strcpy(pwmatrname, optarg);
      break;
    case 'Q':
      got_Q= true;
      pwgappen= atoi(optarg);
      break;
    case 'G':
      got_G= true;
      groupfile= new char[strlen(optarg)+1];
      strcpy(groupfile, optarg);
      break;
    case 'o':
      got_o= true;
      inGroupScale= atof(optarg);
      break;
    case 'O':
      got_O= true;
      outGroupScale= atof(optarg);
      break;
    case 'v':
      got_v= true;
      break;
    case 'V':
      got_V= true;
      break;
    default:
      printShortHelp();
      exit(-1);
    }
  
  if ( optind == argc)  // is there an additional parameter (file name)?
    fatal( "No file name for reading alignment data given!?" );
  else if ( optind < argc - 1)
    fatal( "Additional parameters given!?" );
  
  alfile= new char[strlen(argv[optind])+1];
  strcpy(alfile, argv[optind]);
  
  try { // chatch any exceptions and print them
  
    Alignment    *al(0); // pointer to the objects we're going to use
    SecWeight    *secw(0);
    PwDistWeight *pwd(0);
    
    ifstream ifs_al(alfile);
    
    // first, get any information about secondary structure and build 
    // Alignment and SecWeight from that
    if ( got_f ) { // did we get secondary structure info?
      ifstream ifs_ftb(secfile);
      if ( ! got_r ) 
	fatal( "Need reference file for sec. structure reading: -r option!" );
      ifstream ifs_ref(reffile);
      
      // create alignment with all that info
      al= new Alignment(ifs_al, ifs_ftb, ifs_ref, alfile);

      if ( got_d || got_m ) { // some scoring is wanted
	if (!got_s || !got_t) 
	  fatal("Supply secondary structure weighting fn using -s and -t opt.");
      
	SecWeightFunc *alfn(0), *betfn(0);

	// get more information about alpha helix wighting fn:
	if (!got_S)
	  alfn= new BlockScaleFixed( alscale );
	else if ( aloffs < 1 ) {
	  if (got_l)
	    alfn= new LinearProp( alscale, aloffs );
	  else
	    alfn= new BlockScaleProp(alscale, aloffs );
	} else {
	  if (got_l)
	    alfn= new LinearFixed( alscale, aloffs );
	  else
	    alfn= new BlockScaleFixed( alscale, aloffs );
	}

	// same for beta strand wighting fn:
	if (!got_T)
	  betfn= new BlockScaleFixed( betscale );
	else if ( betoffs < 1 ) {
	  if (got_L)
	    betfn= new LinearProp( betscale, betoffs );
	  else
	    betfn= new BlockScaleProp( betscale, betoffs );
	} else {
	  if (got_L)
	    betfn= new LinearFixed( betscale, betoffs );
	  else
	    betfn= new BlockScaleFixed( betscale, betoffs );
	}
      
	secw= new IndepSecW( alfn, betfn );
      } // scoing is wanted
    
    } else { // no secondary structure given
      if ( got_s || got_S || got_t || got_T || got_l || got_L )
	fatal("Need to specify secondary structure file using -f option!");

      al= new Alignment( ifs_al, alfile );
      secw= new DefaultSecW();
    } // if (got_f)

    if (got_v || got_V) {
      cerr << "Filename: " << alfile << endl;
      // maybe more to come here ...
    }

    // print alignment parameters as first output
    if (got_a)
      printAlignmentParam( cout, *al ) << endl;

  // now calculate pairwise distance weighting functions
    if ( got_p || got_P ) {
      if ( got_o || got_O || got_G )
	fatal("Only one pairwise weighting method at same time (-p,-P,-G)");
      // extra scoring matrix ?
      if ( got_q ) {
	if (got_p)
	  pwd= new PwDistDynPrg( *al, parseScoreMatr( pwmatrname, pwgappen, got_Q ));
	else // got_P
	  pwd= new PwDistDynPrg( *al, parseScoreMatr( pwmatrname, pwgappen, got_Q ),
				 fabs(pwscale), (pwscale < 0) );
      } else { // ! (got_q || got_Q)
	// is standard scoring system specified ?
	if (got_p)
	  pwd= new PwDistDynPrg( *al, parseScoreMatr( matrname, gappen, got_g ));
	else // got_P
	  pwd= new PwDistDynPrg( *al, parseScoreMatr( matrname, gappen, got_g ),
				 fabs(pwscale), (pwscale < 0) );
      }

      // be verbose if required
      if (got_V)
	cerr << *pwd << endl;
    } if ( got_G ) {
      ifstream ifs_grp( groupfile );

      pwd= new GroupPwCut( ifs_grp, *al, inGroupScale, outGroupScale);
      // be verbose if required
      if (got_V)
	cerr << *pwd << endl;;
    } else { // if (got_p)
      pwd= new DefaultPwDist();
    } // else if (got_p)

    // next step: get type of scoring (difference / matrix) and do scoring
    if (got_d || got_m) { // common block for all scores

      Score *sc(0);

      if (got_d) {        // distance score is wanted
	if (!got_r) 
	  fatal("Need reference alignment (-r) for difference score!");
      
	ifstream ifs_ref(reffile);
	sc= new DiffScore( *al, Alignment(ifs_ref, "ref"), *secw, *pwd );
      } else {            // absolute (matrix-) score is wanted
	PredefScore pdfsc( parseScoreMatr( matrname, gappen, got_g ) );
	sc= new SPScore( *al, pdfsc , *secw, *pwd );
	if (got_V)
	  cerr << "Gap penalty: " << pdfsc.gap << endl;
      } // else if (got_d)
    
      if (got_v || got_V) {
	cerr << "Score details:" << endl;
	if ( got_f )              // secondary structure known?
	  printAlignmentSecWithScore( cerr, *al, *sc ) << endl;
	else
	  printAlignmentWithScore( cerr, *al, *sc ) << endl;
      } else { // normal output of score:
	cout << (const string)(*sc) << ": " << (int)(*sc) << endl;
      }
    } // if (got_d || got_m)
    
  } catch (Exception e) {
    fatal( ((const string)e).c_str() );
  } // we don't know how to handle other exceptions, so leave it to the
  // system to do that!
  return 0;
}
    
