//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Apr 23 14:00:47 PDT 2012
// Last Modified: Mon Apr 23 14:00:50 PDT 2012
// Filename:      ...sig/examples/all/tacet.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/tacet.cpp
// Syntax:        C++; museinfo
//
// Description:   Locate/label sections where **kern spines contain no notes.
//

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

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

// function declarations
void  checkOptions(Options& opts, int argc, char* argv[]);
void  example(void);
void  usage(const char* command);
void  checkForTacets(HumdrumFile& infile,
                                     Array<Array<int> >& tacetinfo);
void  printTacetInfo(HumdrumFile& infile, 
                                     Array<Array<int> >& tacetinfo);
void  verifyNoTacetMarkers(HumdrumFile& infile);
void  printWithMarkers(HumdrumFile& infile, 
                                     Array<Array<int> >& tacetinfo);
void  printTacetLine(HumdrumFile& infile, int line, 
                                     Array<Array<int> >& tacetinfo, int aline);

Options   options;                 // database for command-line arguments
const char* Filename = "";         // used with nothing at the moment
int       pathQ      = 1;          // used with nothing at the moment
int       insertQ    = 0;          // used with -i option
int       lineQ      = 1;          // used with -l option 
int       fileQ      = 0;          // used with -f option
int       debugQ     = 0;          // used with --debug option

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

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

   // process the command-line options
   checkOptions(options, argc, argv);

   // figure out the number of input files to process
   int numinputs = options.getArgCount();
   Array<Array<int> > tacetinfo; 

   int i;

   Array<char> filebuffer;
   PerlRegularExpression pre;

   for (i=0; i<numinputs || i==0; i++) {
      infile.clear();

      // if no command-line arguments read data file from standard input
      if (numinputs < 1) {
         infile.read(cin);
      } else {
         Filename = options.getArg(i+1);
         infile.read(Filename);
         if (!pathQ) {
            filebuffer.setSize(strlen(Filename)+1);
            strcpy(filebuffer.getBase(), Filename);
            pre.sar(filebuffer, ".*\\/", "");
            Filename = filebuffer.getBase();
         }
      }

      if (insertQ) {
         verifyNoTacetMarkers(infile);
      }
  
      checkForTacets(infile, tacetinfo);

      if (debugQ) {
         printTacetInfo(infile, tacetinfo);
      }
      if (insertQ) {
         printWithMarkers(infile, tacetinfo);
      }

   }

   return 0;
}

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


//////////////////////////////
//
// printWithMarkers --
//

void printWithMarkers(HumdrumFile& infile, Array >& tacetinfo) {
   int jj = 0;

   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if ((jj <= tacetinfo.getSize()-1) && (tacetinfo[jj][0] == i)) {
         printTacetLine(infile, i, tacetinfo, jj);
         // cout << "!!TACET INFO GOES HERE\n";
         jj++;
      }
      cout << infile[i] << "\n";
   }
}



//////////////////////////////
//
// printTacetLine --
//

void printTacetLine(HumdrumFile& infile, int line, 
      Array<Array<int> >& tacetinfo, int aline) {

   int j;
   int track;
   int printQ = 0;
   char buffer[8192] = {0};

   if (aline == 0) {
      // print *tacet for any track which contains a 0.
      for (j=0; j<infile[line].getFieldCount(); j++) {
         if (infile[line].isExInterp(j, "**kern")) {
            track = infile[line].getPrimaryTrack(j);
            if (tacetinfo[aline][track] == 0) {
               strcat(buffer, "*tacet");
               printQ = 1;
            } else {
               strcat(buffer, "*");
            }
         } else {
            strcat(buffer, "*");
         }
         if (j < infile[line].getFieldCount()-1) {
            strcat(buffer, "\t");
         }
      }
      strcat(buffer, "\n");
      if (printQ) {
         cout << buffer;
      }
      return;
   }

   // print *tacet if 0 (repeating even if last section was also tacet)
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (infile[line].isExInterp(j, "**kern")) {
         track = infile[line].getPrimaryTrack(j);
         if (tacetinfo[aline][track] == 0) {
            strcat(buffer, "*tacet");
            printQ = 1;
         } else if ((tacetinfo[aline][track] > 0) && 
                    (tacetinfo[aline-1][track] == 0)) {
            // print *xtacet if non-zero and previous section was tacet
            strcat(buffer, "*xtacet");
            printQ = 1;
         } else {
            strcat(buffer, "*");
         }
      } else {
         strcat(buffer, "*");
      }
      if (j < infile[line].getFieldCount()-1) {
         strcat(buffer, "\t");
      }
   }
   strcat(buffer, "\n");
   if (printQ) {
      cout << buffer;
   }

}



//////////////////////////////
//
// verifyNoTacetMarkers --
//

void verifyNoTacetMarkers(HumdrumFile& infile) {
   int i, j;
   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isInterpretation()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (!infile[i].isExInterp(j, "**kern")) {
            continue;
         }
         if (strcmp(infile[i][j], "*tacet") == 0) {
            cerr << "Error: Tacet marker in column " << j+1
                 << " on line " << i+1 << ". Cannot proceed" << endl;
         }
         if (strcmp(infile[i][j], "*xtacet") == 0) {
            cerr << "Error: Tacet marker in column " << j+1
                 << " on line " << i+1 << ". Cannot proceed" << endl;
         }
      }
   }
}



//////////////////////////////
//
// printTacetInfo --
//

void printTacetInfo(HumdrumFile& infile, Array >& tacetinfo) {
   int i, j;
   for (i=0; i<tacetinfo.getSize(); i++) {
      for (j=0; j<tacetinfo[i].getSize(); j++) {
         cout << tacetinfo[i][j];
         if (j < tacetinfo[i].getSize()-1) {
            cout << "\t";
         }
      }
      cout << endl;
   }
}



//////////////////////////////
//
// checkForTacets --
//

void checkForTacets(HumdrumFile& infile, Array >& tacetinfo) {

   int lastsection = 0;
   int lastmeasure = 0;
   int datafound = 0;
   int measurenum = 0;
   PerlRegularExpression pre;
   int printedfilename = 0;

   tacetinfo.setSize(1000);
   tacetinfo.setSize(0);
   tacetinfo.setGrowth(1123123);

   Array<int> kerntracks;
   infile.getTracksByExInterp(kerntracks, "**kern");
   Array<int> kernstates;
   kernstates.setSize(infile.getMaxTracks()+1);
   kernstates.setAll(0);

   Array<Array<char> > tracknames;
   tracknames.setSize(infile.getMaxTracks()+1);
   int j;
   for (j=0; j<tracknames.getSize(); j++) {
      tracknames[j].setSize(1024);
      strcpy(tracknames[j].getBase(), "");
   }

   int line;
   int ptrack;
   for (line=0; line<infile.getNumLines(); line++) {
      if (infile[line].isMeasure()) {
         if (pre.search(infile[line][0], "(\\d+)")) {
            measurenum = atoi(pre.getSubmatch(1));
         }
         if (pre.search(infile[line][0], "\\|\\|") ||
             pre.search(infile[line][0], "==") ) {
            // Segmentation boundary: 
            // See what has been silent since last segment.
            for (j=0; j<kerntracks.getSize(); j++) {
               if (kernstates[kerntracks[j]] == 0) {
                  if (!insertQ) {
                     if (fileQ && (printedfilename == 0)) {
                        cout << "File " << Filename << " contains tacets:\n";
                        printedfilename = 1;
                     }
                     if (fileQ) {
                        cout << "\t";
                     }
                     if (strcmp(tracknames[kerntracks[j]].getBase(), "") != 0) {
                        cout << tracknames[kerntracks[j]].getBase();
                     } else {
                        cout << "TRACK " << kerntracks[j];
                     }
                     cout << " is tacet from measure " << lastmeasure;
                     if (lineQ) {
                        cout << " (line " << lastsection << ")";
                     }
                     cout << " to " << measurenum;
                     if (lineQ) {
                        cout << " (line " << line << ")";
                     }
                     cout << endl;
                  }
               }
            }
            tacetinfo.append(kernstates);
            tacetinfo.last()[0] = lastsection;
            lastsection = line;
            lastmeasure = measurenum;
            kernstates.setAll(0);
            datafound = 0;
         }
      } else if (infile[line].isData()) {
         if (!datafound) {
            // store *tacet, *xtacet before first data or measure line 
            // in section
            lastsection = line;
         }
         datafound = 1;
         for (j=0; j<infile[line].getFieldCount(); j++) {
            if (strcmp(infile[line][j], ".") == 0) {
               continue;
            }
            if (!infile[line].isExInterp(j, "**kern")) {
               continue;
            }
            if (strchr(infile[line][j], 'r') == NULL) {
               ptrack = infile[line].getPrimaryTrack(j);
               kernstates[ptrack]++;
            }
         }
      } else if (infile[line].isInterpretation()) {
         for (j=0; j<infile[line].getFieldCount(); j++) {
            if (!infile[line].isExInterp(j, "**kern")) {
               continue;
            }
            if (pre.search(infile[line][j], "\\*I\"(.*)")) {
               ptrack = infile[line].getPrimaryTrack(j);
               strcpy(tracknames[ptrack].getBase(), pre.getSubmatch(1));
            }
         }
      }
   } 
}



//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("i|insert=b", "Insert *tacet/xtacet lines in data");
   opts.define("f|file=b",   "display filename when tacet present");
   opts.define("l|line=b",   "display line number of boundary");
   opts.define("p|path=b",   "remove directory name from filename");

   opts.define("debug=b");           // determine bad input line num
   opts.define("author=b");          // author of program
   opts.define("version=b");         // compilation info
   opts.define("example=b");         // example usages
   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, Apr 2012" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 20 Apr 2012" << 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);
   }

   insertQ = opts.getBoolean("insert");
   fileQ   = opts.getBoolean("file");
   lineQ   = opts.getBoolean("line");
   pathQ   = opts.getBoolean("path");
   debugQ  = opts.getBoolean("debug");
}



//////////////////////////////
//
// example -- example usage of the quality program
//

void example(void) {
   cout <<
   "                                                                         \n"
   << endl;
}



//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//

void usage(const char* command) {
   cout <<
   "                                                                         \n"
   << endl;
}



// md5sum: 209469cdf990cdedba0a469b16796b1f tacet.cpp [20120523]