//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Oct 15 04:30:17 PDT 2008
// Last Modified: Wed Oct 15 08:13:13 PDT 2008
// Filename:      ...sig/examples/all/marcello1.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/marcello1.cpp
// Syntax:        C++; museinfo
//
// Description:   Feature extractor which extract the metrical position
//                of each beat, the number of events in the left hand, 
//                and the number of events in a beat for the right hand
//                in piano music (two primary spines of music, the first
//                **kern spine contains the left hand, the second **kern spine
//                contains the right hand.  Any primary **kern spines after
//                the first two will be ignored.

#ifndef OLDCPP
   #include <iostream>
   using namespace std;
#else
   #include <iostream.h>
#endif

#include "humdrum.h"

#define FIELDCOUNT 5

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

// function declarations:
void      checkOptions(Options& opts, int argc, char** argv);
void      example(void);
void      usage(const char* command);
void      countEvents(int& lh, int &rh, HumdrumFile& infile, int line);
void      createAnalysisOne(ostream& out, HumdrumFile& infile);

// User interface variables:
Options   options;

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

int main(int argc, char** argv) {

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

   HumdrumFile infile;
   infile.read(options.getArg(1));

   createAnalysisOne(std::cout, infile);

   return 0;
}

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

//////////////////////////////
//
// createAnalysisOne -- print the metrical position, the number of
//   events in the left hand (bottom staff), the number of events in 
//   the right hand (top staff).
//

void createAnalysisOne(ostream& out, HumdrumFile& infile) {
   int i, j;
   int absbeat = -1;
   int lastabsbeat = -1;
   int metrical = -1;
   int lastmetrical = -1;
   int lhcounter = -1;
   int rhcounter = -1;
   int rh;
   int lh;
   int datafound = 0;
   int endofdata = -1;
   int measureQ = 0;
   int difference;
   int measurenum;
   char measure[32] = {0};

   infile.analyzeRhythm("4");
   

   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() == E_humrec_bibliography) {
         if (endofdata < 0) {
            // only print the bibliographic records which start before 
            // end of the data.  Ignore any bibliographic records at 
            // the end of the data until later.
            out << infile[i] << "\n";
         }
         continue;
      }
      if (infile[i].getType() == E_humrec_interpretation) {
         if (strncmp(infile[i][0], "**", 2) == 0) {
            // store the of end of data location in the input file
            // for printing bibliographic data after the end of the
            // data in the original file.
            endofdata = i;
         }
      }
      if (infile[i].getType() == E_humrec_data_measure) {
         measurenum = -1;
         if (sscanf(infile[i][0], "=%d", &measurenum) == 1) {
            sprintf(measure, "%d", measurenum);
         } else {
            measure[0] = '\0';
         }
         measureQ = 1;

      }

      if (infile[i].getType() != E_humrec_data) {
         continue;
      }
      if (datafound == 0) {
         out << "**kern\t**absbt\t**meter\t**lhcnt\t**rhcnt\n";

         if (measureQ) {
            out << "=" << measure;
            for (j=1; j<FIELDCOUNT; j++) {
               out << "\t=" << measure;
            }
            out << "\n";
            measureQ = 0;
         }
         datafound = 1;
      }
      absbeat = int(infile[i].getAbsBeat() + 0.001);
      metrical = int(infile[i].getBeat() + 0.001);
      if (lastabsbeat < 0) {
         lastabsbeat = absbeat;
         lastmetrical = metrical;
         lhcounter = 0;
         rhcounter = 0;
      } else if (absbeat != lastabsbeat) {

         out << "4"          << "\t" 
              << lastabsbeat  << "\t"
              << lastmetrical << "\t" 
              << lhcounter    << "\t" 
              << rhcounter    << endl;

         difference = absbeat - lastabsbeat;
         if (difference > 1) {
            for (j=1; j<difference; j++) {
               out << "4"                          << "\t" 
                    << lastabsbeat + j              << "\t"
                    << (((lastmetrical-1) + j)%3)+1 << "\t" 
                    << 0                            << "\t" 
                    << 0                            << endl;
            }
         }

         if (measureQ) {
            out << "=" << measure;
            for (j=1; j<FIELDCOUNT; j++) {
               out << "\t=" << measure;
            }
            out << "\n";
            measureQ = 0;
         }

         lastabsbeat = absbeat;
         lastmetrical = metrical;
         lhcounter = 0;         
         rhcounter = 0;         
      }
      countEvents(lh, rh, infile, i);
      lhcounter += lh;
      rhcounter += rh;
   }

   difference = int(infile.getTotalDuration()+0.99) - absbeat;
   for (j=0; j<difference; j++) {
      out << "4"                             << "\t" 
           << absbeat + j                    << "\t"
           << (((lastmetrical-1) + j)%3)+1   << "\t" 
           << 0                              << "\t" 
           << 0                              << endl;
   }

   out << "==\t==\t==\t==\t==\n";
   out << "*-\t*-\t*-\t*-\t*-\n";

   // print Bibliographic records after the end of the data
   if (endofdata < 0) {
      // this should never occur.
      return;
   }
   for (i=endofdata; i<infile.getNumLines(); i++) {
      if (infile[i].getType() == E_humrec_bibliography) {
         out << infile[i] << "\n";
      }
   }

}



//////////////////////////////
//
// countEvents -- count the number of events in the primary and
//    secondary track for the current line.  The number of 
//    events is the number of notes or chords found in sequence.
//    (one chord counts is equal to one note).  If notes are
//    found in separate voices, they could be counted, or not.
//    But currently they are not counted.
//

void countEvents(int& lh, int &rh, HumdrumFile& infile, int line) {
   int i;
   int trackindex;
   int primary;
   Array<int> trackfound;
   trackfound.setSize(2);
   trackfound.setAll(0);
   Array<int> trackcounts;
   trackcounts.setSize(2);
   trackcounts.setAll(0);
 
   for (i=0; i<infile[line].getFieldCount(); i++) {
      if (infile[line].getExInterpNum(i) != E_KERN_EXINT)  {
         continue;
      }
      primary = infile[line].getPrimaryTrack(i);
      if (trackfound[0] == 0) {
         trackfound[0] = primary;
         trackindex = 0;
      } else if (trackfound[0] == primary) {
         trackindex = 0;
      } else if (trackfound[1] == 0) {
         trackfound[1] = primary;
         trackindex = 1;
      } else if (trackfound[1] == primary) {
         trackindex = 1;
      } else {
         break; // some other spine of **kern data other than the first two
      }
 
      if (strcmp(infile[line][i], ".") == 0) {
         // don't count null tokens
         continue;
      }

      if (strchr(infile[line][i], 'r') != NULL) {
         // don't count rests
         continue;
      }
      trackcounts[trackindex]++;
   }

   lh = trackcounts[0] ? 1 : 0;
   rh = trackcounts[1] ? 1 : 0;
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {

   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, October 2008" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 15 October 2008" << 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);
   }
   
}



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

void example(void) {


}



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

void usage(const char* command) {

}


// md5sum: e4b0b470fd381c0e8bf1fedaa0428043 marcello1.cpp [20081017]