//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed May 23 15:07:46 PDT 2012
// Last Modified: Wed May 23 15:07:51 PDT 2012
// Filename:      ...sig/examples/all/sinko.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/sinko.cpp
// Syntax:        C++; museinfo
//
// Description:   Identify syncopations in a piano texture.
//

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


#ifndef OLDCPP
   #include <iostream>
#else
   #include <iostream.h>
#endif



// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   printLine(HumdrumFile& infile, int line, int measure, 
                          int lh, int rh, int metlev, int trackcount);
void   processRecords(HumdrumFile& infile);
int    getKernTracks(HumdrumFile& infile);

// global variables
Options      options;            // database for command-line arguments
int          emptyQ = 0;         // used with -d option

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


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

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

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

   for (int 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 {
         infile.read(options.getArg(i+1));
      }

      infile.analyzeRhythm();
      processRecords(infile);
       
   }
   return 0;
}


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

//////////////////////////////
//
// getKernTracks --
//

int getKernTracks(HumdrumFile& infile) { 
   Array<int> tracks;
   infile.getTracksByExInterp(tracks, "**kern");
   return tracks.getSize();
}



//////////////////////////////
//
// processRecords -- 
//

void processRecords(HumdrumFile& infile) {
   PerlRegularExpression pre;
   int i, j, track;
   int lh, rh, state;  
   int measure = 0;

   int kerntracks = getKernTracks(infile);
   if (kerntracks > 2 || kerntracks < 1) {
      cerr << "ERROR: there must be one or two **kern spines\n";
      exit(1);
   }

   Array<int> metlev;
   infile.analyzeMetricLevel(metlev);

   if (kerntracks == 2) {
      cout << "**bar\t**beat\t**mlev\t**LH\t**RH\n";
   } else if (kerntracks == 1) {
      cout << "**bar\t**beat\t**mlev\t**H\n";
   }

   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isMeasure()) {
         if (pre.search(infile[i][0], "=(\\d+)")) {
            measure = atoi(pre.getSubmatch(1));
         }
      }
      if (!infile[i].isData()) {
         continue;
      }
      rh = 0;
      lh = 0;
      track = 0;
      for (j=0; j<infile[i].getFieldCount(); j++) {
         track = infile[i].getPrimaryTrack(j);
         if (strcmp(infile[i][j], ".") == 0) {
            state = 0;
         } else if (strchr(infile[i][j], '_') != NULL) {
            state = 0;
         } else if (strchr(infile[i][j], ']') != NULL) {
            state = 0;
         } else if (strchr(infile[i][j], 'r') != NULL) {
            state = 0;
         } else {
            state = 1;
         }
         if (track == 1) {
            lh = state;
         } else if (track == 2) {
            rh = state;
         }
      }
      printLine(infile, i, measure, lh, rh, metlev[i], kerntracks);
   }

   if (kerntracks == 2) {
      cout << "*-\t*-\t*-\t*-\t*-\n";
   } else {
      cout << "*-\t*-\t*-\t*-\n";
   }

}



//////////////////////////////
//
// printLine --
//

void printLine(HumdrumFile& infile, int line, int measure, int lh, int rh,
      int metlev, int trackcount) {


   if (emptyQ) {
      if ((trackcount == 1) && (lh == 0)) {
         return;
      }
      if ((trackcount == 2) && (lh == 0) && (rh == 0)) {
         return;
      }
   }


   cout << measure;
   cout << "\t";
   cout << infile[line].getBeat();
   cout << "\t";
   cout << metlev;
   cout << "\t";

   if (trackcount == 2) {
      if (lh) { cout << "LH"; } else { cout << "."; }
      cout << "\t";
      if (rh) { cout << "RH"; } else { cout << "."; }
      cout << endl;
   } else {
      if (lh) { cout << "H"; } else { cout << "."; }
      cout << endl;
   }
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("d|empty|no-empty|no-empties=b","do not display null tokens in hands");
   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, Mar 2011" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: Nov 2000" << 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);
   }
   emptyQ = opts.getBoolean("empty");
}



//////////////////////////////
//
// example -- example usage of the sonority program
//

void example(void) {
   cout <<
   "                                                                         \n"
   "# example usage of the sonority program.                                 \n"
   "# analyze a Bach chorale for chord qualities:                            \n"
   "     sonority chor217.krn                                                \n"
   "                                                                         \n"
   "# display the chord analysis with original data:                         \n"
   "     sonority -a chor217.krn                                             \n"
   "                                                                         \n"
   "# display only the roots of chords:                                      \n"
   "     sonority -r chor217.krn                                             \n"
   "                                                                         \n"
   << endl;
}



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

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



// md5sum: 65af416bcb15089e7bde541b923d0b97 sinko.cpp [20120614]