//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Feb  6 14:33:36 PST 2011
// Last Modified: Sun Feb  6 14:33:39 PST 2011
// Filename:      ...sig/examples/all/satb2gs.cpp 
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/satb2gs.cpp
// Syntax:        C++; museinfo
//
// Description:   Converts Soprano/Alto/Tenor/Bass staves into 
//                Grand-staff style.
//

#include <math.h>
#include <time.h>    /* for current time/date */

#ifndef OLDCPP
   #include <iostream>
   #include <sstream>
   #define SSTREAM stringstream
   #define CSTRING str().c_str()
   using namespace std;
#else
   #include <iostream.h>
   #ifdef VISUAL
      #include <strstrea.h>
   #else
      #include <strstream.h>
   #endif
   #define SSTREAM strstream
   #define CSTRING str()
#endif

#include "string.h"

#include "humdrum.h"
#include "PerlRegularExpression.h"
#include "MuseData.h"


//////////////////////////////////////////////////////////////////////////

// function declarations:
void  checkOptions(Options& opts, int argc, char** argv);
void  example(void);
void  usage(const char* command);
void  convertData(HumdrumFile& infile);
int   getSatbTracks(Array<int>& tracks, HumdrumFile& infile);
void  printSpine(HumdrumFile& infile, int row, int col, 
                                Array<int>& satbtracks);
void  printExInterp(HumdrumFile& infile, int line, 
                                Array<int>& tracks);
void  printLastLine(HumdrumFile& infile, int line, 
                                Array<int>& tracks);

// User interface variables:
Options options;
int    debugQ    = 0;             // used with --debug option

//////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv) {
   HumdrumFile infile;

   // initial processing of the command-line options
   checkOptions(options, argc, argv);

   if (options.getArgCount() < 1) {
      infile.read(cin);
   } else {
      infile.read(options.getArg(1));
   }

   convertData(infile);

   return 0;
}


//////////////////////////////////////////////////////////////////////////


//////////////////////////////
//
// convertData -- data is assumed to be in the order from
// bass, tenor, alto, soprano, with non-**kern data found 
// in any order.  Only the first four **kern spines in the file
// will be considered.
//

void convertData(HumdrumFile& infile) {
   Array<int> satbtracks;
   satbtracks.setSize(4);
   int exinterpline = getSatbTracks(satbtracks, infile);
   int i, j;
   int lastline = -1;
   for (i=0; i<exinterpline; i++) {
      cout << infile[i] << endl;
   }

   printExInterp(infile, exinterpline, satbtracks);

   for (i=exinterpline+1; i<infile.getNumLines(); i++) {
      if (infile[i].getFieldCount() == 1) {
         cout << infile[i] << endl;
         continue;
      }
      if (strcmp(infile[i][0], "*-") == 0) {
         lastline = i;
         break;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         printSpine(infile, i, j, satbtracks);
         if (j < infile[i].getFieldCount() - 1) {
            cout << '\t';
         }
      }
      cout << '\n';
   }

   if (lastline < 0) {
      return;
   }
   printLastLine(infile, lastline, satbtracks);

   for (i=lastline+1; i<infile.getNumLines(); i++) {
      cout << infile[i] << endl;
   }
}


//////////////////////////////
//
// printLastLine --
//

void printLastLine(HumdrumFile& infile, int line, Array& tracks) {
   int j;
   int track;

   SSTREAM output;
   for (j=0; j<infile[line].getFieldCount() - 1; j++) {
      track = infile[line].getPrimaryTrack(j);
      if ((track == tracks[1]) || (track == tracks[3])) {
         continue;
      }
      if (track == tracks[0])  {
         output << "*v\t*v";
      } else if (track == tracks[2])  {
         output << "*\t*";
      } else {
         output << "*";
      }
      output << "\t";
   }

   output << ends;
   Array<char> strang;
   strang.setSize(strlen(output.CSTRING) + 1);
   strcpy(strang.getBase(), output.CSTRING);
   PerlRegularExpression pre;
   pre.sar(strang, "\t+$", "", "");
   cout << strang;
   cout << endl;

   SSTREAM output2;
   for (j=0; j<infile[line].getFieldCount() - 1; j++) {
      track = infile[line].getPrimaryTrack(j);
      if ((track == tracks[1]) || (track == tracks[3])) {
         continue;
      }
      if (track == tracks[2])  {
         output2 << "*v\t*v";
      } else if (track == tracks[0])  {
         output2 << "*";
      } else {
         output2 << "*";
      }
      output2 << "\t";
   }

   output2 << ends;
   strang.setSize(strlen(output2.CSTRING) + 1);
   strcpy(strang.getBase(), output2.CSTRING);
   pre.sar(strang, "\t+$", "", "");
   cout << strang;
   cout << endl;

   for (j=0; j<infile[line].getFieldCount()-2; j++) {
      cout << infile[line][j];
      if (j < infile[line].getFieldCount() - 3) {
         cout << "\t";
      }
   }
   cout << "\n";

}



//////////////////////////////
//
// printExInterp -- print only tenor and soprano tracks
//

void printExInterp(HumdrumFile& infile, int line, Array& tracks) {
   SSTREAM output;
   int j;
   int track;


   // first print exclusive interpretations
   for (j=0; j<infile[line].getFieldCount(); j++) {
      track = infile[line].getPrimaryTrack(j);
      if ((track == tracks[1]) || (track == tracks[3])) {
         continue;
      }
      output << infile[line][j] << "\t";
   }
   Array<char> strang;
   output << ends;
   strang.setSize(strlen(output.CSTRING) + 1);
   strcpy(strang.getBase(), output.CSTRING);
   PerlRegularExpression pre;
   pre.sar(strang, "\t+$", "");
   cout << strang;
   cout << endl;

   SSTREAM output2;
   SSTREAM output3;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      track = infile[line].getPrimaryTrack(j);
      if ((track == tracks[1]) || (track == tracks[3])) {
         continue;
      }
      if (track == tracks[0]) {
         output3 << "*clefF4";
         output2 << "*^";
      } else if (track == tracks[2]) {
         output3 << "*clefG2";
         output2 << "*^";
      } else {
         output3 << "*";
         output2 << "*";
      }
      output3 << "\t";
      output2 << "\t";
   }

   output3 << ends;
   strang.setSize(strlen(output3.CSTRING) + 1);
   strcpy(strang.getBase(), output3.CSTRING);
   pre.sar(strang, "\t+$", "", "");
   cout << strang;
   cout << endl;

   output2 << ends;
   strang.setSize(strlen(output2.CSTRING) + 1);
   strcpy(strang.getBase(), output2.CSTRING);
   pre.sar(strang, "\t+$", "", "");
   cout << strang;
   cout << endl;

}



///////////////////////
//
// printSpine --
//

void printSpine(HumdrumFile& infile, int row, int col, Array& satbtracks) {
   int track = infile[row].getPrimaryTrack(col);
   int j, k;
   int target = -1;
   for (k=0; k<satbtracks.getSize(); k++) {
      if (track == satbtracks[k]) {
         if (k % 2 == 0) {
            target = satbtracks[k+1];
         } else {
            target = satbtracks[k-1];
         }
         break;
      }
   }

   if (target < 0) {
      // does not need to be switched
      cout << infile[row][col];
      return;
   }

   // print the SAT or B token(s) (there should be only one token for each)
   //
   // If a tenor has a fermata and a bass has a fermata and both are
   // the same duration, then hide the tenor's fermata.
   //
   // If an alto has a fermata and a soprano has a fermata and both are
   // the same duration, then hide the alto's fermata.
   //


   // first identify the column for each voice, considering only the first
   // layer, if there are multiple layers.
   Array<int> cols(4);
   cols.setAll(-1);
   for (j=0; j<infile[row].getFieldCount(); j++) {
      track = infile[row].getPrimaryTrack(j);
      for (k=0; k<satbtracks.getSize(); k++) {
         if (cols[k] >= 0) {
            continue;
         }
         if (track == satbtracks[k]) {
            cols[k] = j;
         }
      }
   }


   PerlRegularExpression pre;
   Array<char> strang;
   int count = 0;
   for (j=0; j<infile[row].getFieldCount(); j++) {
      track = infile[row].getPrimaryTrack(j);
      if (track == target) {
         if (count > 0) {
            cout << '\t';
         }
         strang.setSize(strlen(infile[row][j]) + 1);
         strcpy(strang.getBase(), infile[row][j]);
         pre.sar(strang, "^\\*clef", "!*clef", "");

         if (infile[row].isData()) {

            if ((cols[0] == col) && (strchr(infile[row][col], ';') != NULL)) {
               RationalNumber tenordur;
               RationalNumber bassdur;
               tenordur = Convert::kernToDurationR(infile[row][cols[0]]);
               bassdur  = Convert::kernToDurationR(infile[row][cols[1]]);
               if (tenordur == bassdur) {
                  pre.sar(strang, ";", ";y", "g"); // hide fermata
               }
            }

            if ((cols[3] == col) && (strchr(infile[row][col], ';') != NULL)) {
               RationalNumber altodur;
               RationalNumber sopranodur;
               altodur = Convert::kernToDurationR(infile[row][cols[3]]);
               sopranodur  = Convert::kernToDurationR(infile[row][cols[2]]);
               if (altodur == sopranodur) {
                  pre.sar(strang, ";", ";y", "g"); // hide fermata
               }
            }

         }

         cout << strang.getBase();
         count++;
      }
   }
}



///////////////////////////////
//
// getSatbTracks -- return the primary track numbers of the satb spines.
//
//

int getSatbTracks(Array& tracks, HumdrumFile& infile) {
   int i, j;
   int output = -1;
   tracks.setSize(0);
   int track;
   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isInterpretation()) {
         continue;
      }
      output = i;
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (!infile[i].isExInterp(j, "**kern")) {
            continue;
         }
         track = infile[i].getPrimaryTrack(j);
         tracks.append(track);
         if (tracks.getSize() == 4) {
            return output;
         }
      }
      break;
   }

   if (tracks.getSize() != 4) {
      cerr << "Error: there are only " << tracks.getSize() << " **kern spines"
           << " in input data" << endl;
      exit(1);
   }

   return output;
}



//////////////////////////////
//
// checkOptions -- 
//

void checkOptions(Options& opts, int argc, char** argv) {
   opts.define("d|debug=b",    "Debugging information");

   opts.define("author=b",    "Program author");
   opts.define("version=b",   "Program version");
   opts.define("example=b",   "Program examples");
   opts.define("h|help=b",    "Short description");
   opts.process(argc, argv);

   // handle basic options:
   if (opts.getBoolean("author")) {
      cout << "Written by Craig Stuart Sapp, "
           << "craig@ccrma.stanford.edu, Feb 2011" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 5 Feb 2011" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << MUSEINFO_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }

   debugQ     =  opts.getBoolean("debug");

}



//////////////////////////////
//
// example -- example function calls to the program.
//

void example(void) {


}



//////////////////////////////
//
// usage -- command-line usage description and brief summary
//

void usage(const char* command) {

}


// md5sum: 8a410372ece24e6182d3581c017329a7 satb2gs.cpp [20110215]