//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Jun 28 21:41:03 PDT 2010
// Last Modified: Mon Jun 28 21:41:06 PDT 2010
// Filename:      ...sig/examples/all/joscor.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/joscor.cpp
// Syntax:        C++; museinfo
//
// Description:   Generate score from monophonic parts.
//

#include "humdrum.h"
#include "PerlRegularExpression.h"
#include <math.h>


#include <string.h>
#include <ctype.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      getPageNumbers(Array<int>& pagenumber, HumdrumFile& infile, 
                              const char* target);
void      printInfo(Array<int>& pages, Array<int>& staff, 
                              HumdrumFile& infile);
void      getSystemNumber(Array<int>& systemnumber, Array<int>& pagenumber, 
                              HumdrumFile& infile, const char* target);
void      printPage(ostream& out, HumdrumFile& infile, 
                              Array<int>& systemnumber, Array<int>& pagenumber, 
                              int page);
void      getClefState(Array<int>& meterline, Array<int>& meterspine, 
                              HumdrumFile& infile, int startindex, 
                              int stopindex);
int       getStaffCount(HumdrumFile& infile, int line);

// global variables
Options   options;             // database for command-line arguments

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

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();

   int i, j;
   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 {
         infile.read(options.getArg(i+1));
      }
      // analyze the input file according to command-line options
      infile.analyzeRhythm("4");


      Array<int> pagenumber;
      getPageNumbers(pagenumber, infile, "default");
      Array<int> systemnumber;
      getSystemNumber(systemnumber, pagenumber, infile, "default");

      for (j=1; j<=pagenumber[infile.getNumLines()-1]; j++) {
         printPage(cout, infile, systemnumber, pagenumber, j);
      }
      // printInfo(pagenumber, systemnumber, infile);

   }

   return 0;
}


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



//////////////////////////////
//
// printPage --
//

void printPage(ostream& out, HumdrumFile& infile, Array<int>& systemnumber, 
      Array<int>& pagenumber, int page) {

   int startline = -1;
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if (pagenumber[i] == page) {
         startline = i;
         break;
      }
   }

   if (startline < 0) {
      // page not found in file
      return;
   }

   int staffcount = getStaffCount(infile, i);
   int maxtracks = infile.getMaxTracks();

   Array<int> meterline;
   Array<int> meterspine;

   meterline.setSize(maxtracks);
   meterline.setAll(-1);
   meterspine.setSize(maxtracks);
   meterspine.setAll(-1);
   meterline.allowGrowth(0);
   meterspine.allowGrowth(0);

   getClefState(meterline, meterspine, infile, 0, startline);


}


//////////////////////////////
//
// getClefState -- 
//

void getClefState(Array<int>& meterline, Array<int>& meterspine, 
      HumdrumFile& infile, int startindex, int stopindex) {

   PerlRegularExpression pre;

   int i, j;
   for (i=startindex; i<=stopindex; i++) {
      if (!infile[i].isInterpretation()) { 
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (strcmp(infile[i].getExInterp(j), "**kern") != 0) {
            continue;
         }
         if (pre.search(infile[i][j], "^\\*clef", "")) {
            int ptrack = infile[i].getPrimaryTrack(j);
            meterline[ptrack] = i;
            meterspine[ptrack] = j;
         }         
      }   
   }
}



//////////////////////////////
//
// getStaffCount -- return the number of **kern spines (different primary
//    tracks) on the current line.
//

int getStaffCount(HumdrumFile& infile, int line) {
   int j;
   Array<int> primarystaff;
   primarystaff.setSize(1000);
   primarystaff.setAll(0);
   int count = 0;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (strcmp(infile[line].getExInterp(j), "**kern") != 0) {
         continue;
      }
      if (primarystaff[infile[line].getPrimaryTrack(j)] != 0) {
         continue;
      }
      primarystaff[infile[line].getPrimaryTrack(j)]++;
      count++;
   }
   return count;
}




//////////////////////////////
//
// getSystemNumber --
//

void getSystemNumber(Array<int>& systemnumber, Array<int>& pagenumber, 
      HumdrumFile& infile, const char* target) {
   systemnumber.setSize(infile.getNumLines());
   systemnumber.setAll(0);
   systemnumber.allowGrowth(0);
   PerlRegularExpression pre;

   char linesearch[1024] = {0};
   strcpy(linesearch, "!!\\s*layout\\s*:\\s*linebreak\\s*:\\s*");
   strcat(linesearch, target);

   char pagesearch[1024] = {0};
   strcpy(pagesearch, "!!\\s*layout\\s*:\\s*pagebreak\\s*:\\s*");
   strcat(pagesearch, target);

   int i;
   int staff = 1;
   for (i=infile.getNumLines()-1; i>=0; i--) {
      if (pre.search(infile[i].getLine(), linesearch)) {
         systemnumber[i] = staff;
         staff++;
         continue;
      }
      if (pre.search(infile[i].getLine(), pagesearch)) {
         systemnumber[i] = staff;
         staff = 1;
         continue;
      }
      systemnumber[i] = staff;
   }
}



//////////////////////////////
//
// printInfo -- debugging
//

void printInfo(Array& pages, Array& staff, HumdrumFile& infile) {
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      cout << pages[i] << "\t" << staff[i] << "\t" 
           << infile[i].getLine() << endl;
   }
}



//////////////////////////////
//
// getPageNumbers -- calculate what page each line in the humdrum file
//     belongs on.
//

void getPageNumbers(Array<int>& pagenumber, HumdrumFile& infile, 
     const char* target) {
   pagenumber.setSize(infile.getNumLines());
   pagenumber.setAll(0);
   pagenumber.allowGrowth(0);
   PerlRegularExpression pre;
   char search[1024] = {0};
   strcpy(search, "!!\\s*layout\\s*:\\s*pagebreak\\s*:\\s*");
   strcat(search, target);

   int page = 1;
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if (pre.search(infile[i].getLine(), search, "i")) {
         if (infile[i].getAbsBeat() > 0) {
            page++;
         }
      }
      pagenumber[i] = page;
   }
}



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

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

   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, June 2010" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 28 June 2010" << 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 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: 517aaeb4325c043d909344d445e2357a joscor.cpp [20100702]