//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Nov 12 22:57:24 PST 2012
// Last Modified: Mon Nov 12 22:57:30 PST 2012
// Filename:      ...sig/examples/all/menchop.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/menchop.cpp
// Syntax:        C++; museinfo
//
// Description:   Extract music with a specific mensurations from input.
//
// Limitations:   Presumes no spine splits or subtracks.
// 

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

// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   processFile(const char* filename);
void   processTrack(HumdrumFile& infile, int track, 
                                 const char* basename);
char* getFileName(char* buffer, HumdrumFile& infile, int line, 
                                 const char* basename, int track);
char* buildFileName(char* buffer, const char* basename, int track, 
                                 int line, const char* met);

// global variables
Options      options;            // database for command-line arguments
int          debugQ     = 0;     // used with the --debug option
int          jrpQ       = 0;     // used with --jrp option

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

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

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

   const char* filename = "";
   int i;

   for (i=1; i<=options.getArgCount(); i++) {
      filename = options.getArg(i);
      processFile(filename);
   }

   return 0;
}


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


//////////////////////////////
//
// processFile --
//

void processFile(const char* filename) {
   Array<char> basename;
   basename.setSize(strlen(filename)+1);
   strcpy(basename.getBase(), filename);
   PerlRegularExpression pre;
   pre.sar(basename, ".*/", "");
   pre.sar(basename, "\\....$", "");
   Array<int> kerntracks;
   HumdrumFile infile(filename);
   infile.getTracksByExInterp(kerntracks, "**kern");
   int i;
   for (i=0; i<kerntracks.getSize(); i++) {
      processTrack(infile, kerntracks[i], basename.getBase());
   }
}



//////////////////////////////
//
// processTrack --
//

void processTrack(HumdrumFile& infile, int track, const char* basename) {
   int i, j;
   int ttrack;
   int exinterpline = -1;
   char buffer[1024] = {0};
   int dataQ = 1;
   int endQ = 0;
   int notecount = 0;

   fstream output;

   Array<char> shortname;
   shortname.setSize(strlen(basename)+1);
   strcpy(shortname.getBase(), basename);

   PerlRegularExpression pre;
   if (jrpQ) {
      pre.sar(shortname, "-.*", "");
   }

   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isGlobalComment()) {
         continue;
      }
      if (infile[i].isBibliographic()) {
         continue;
      }
      if (infile[i].isEmpty()) {
         continue;
      }
      if (infile[i].isInterpretation() || infile[i].isLocalComment() ||
          infile[i].isMeasure()) {
         if (dataQ) {
            getFileName(buffer, infile, i, shortname.getBase(), track);
            if (strcmp(buffer, "") != 0)  {
               if ((endQ == 0) && (exinterpline >= 0)) {
                  output << "*-" << endl;
               }
               if (output.is_open()) {
                  output.close();
               }
               cout << "!!FILENAME: " << buffer << endl;
               output.open(buffer, ios::out);
               if (!output.is_open()) {
                  cerr << "ERROR: cannot write to " << buffer << endl;
                  exit(1);
               }
               output << "!!original-filename:\t"<< basename << ".krn" << endl;
               if (exinterpline >= 0) {
                  output << "**kern" << endl;
                  if (notecount > 1) {
                     // how to deal with tied notes starting a segement
                     // will need to be considered.  In this case the tied
                     // note will (probably) be ignored.  
                     output << "!noff:" << track << ";" << notecount+1 << endl;
                  }
               } 
            }
            dataQ = 0;
         } 
      }

      if (infile[i].isData()) {
         dataQ = 1;
      }
      if (strcmp(infile[i][0], "*-") == 0) {
         endQ = 1;
      }

      for (j=0; j<infile[i].getFieldCount(); j++) {
         ttrack = infile[i].getPrimaryTrack(j);
         if (ttrack != track) {
            continue;
         }
         if (strcmp(infile[i][j], ".") == 0) {
            continue;
         }
         if (strcmp(infile[i][j], "*") == 0) {
            continue;
         }
         if (strcmp(infile[i][j], "!") == 0) {
            continue;
         }
         if (strcmp(infile[i][j], "*^") == 0) {
            // no spine changes allowed in output data
            continue;
         }
         if (strcmp(infile[i][j], "*v") == 0) {
            // no spine changes allowed in output data
            continue;
         }
         if (strcmp(infile[i][j], "=-") == 0) {
            continue;
         }
         if (strncmp(infile[i][j], "**", 2) == 0) {
            exinterpline = i;
            output << infile[i][j] << "\n";
            output << "!noff:" << track << ";" << 1 << endl;
            continue;
         }
         if (infile[i].isData()) {
            if (pre.search(infile[i][j], "[a-z]", "i")) {
               if (!((strchr(infile[i][j], ']') != NULL) ||
                   (strchr(infile[i][j], ']') != NULL) ||
                   (strchr(infile[i][j], 'r') != NULL))) {
                  // should consider what to do with rest counting.
                  notecount++;    
               }
            }
         }
         output << infile[i][j] << "\n";
      }
   }
   // cout << "\n\n"; 
   if (output.is_open()) {
      output.close();
   }
}



///////////////////////////////
//
// getFileName --
//

char* getFileName(char* buffer, HumdrumFile& infile, int line, 
      const char* basename, int track) {
   int i, j;
   int ttrack;
   PerlRegularExpression pre;
   for (i=line; i<infile.getNumLines(); i++) {
      if (infile[i].isData()) {
         break;
      }      
      if (!infile[i].isInterpretation()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         ttrack = infile[i].getPrimaryTrack(j);
         if (ttrack != track) {
            continue;
         }
         if (strncmp(infile[i][j], "*met(", 5) != 0) {
            continue;
         }
         if (pre.search(infile[i][j], "^\\*met\\((.*?)\\)")) {
             if (strcmp(pre.getSubmatch(1), "")  == 0) {
                buffer[0] = '\0';
                return buffer;
             }
             return buildFileName(buffer, basename, track, i+1, 
                   pre.getSubmatch());
         }
      }
   }
   buffer[0] = '\0';
   return buffer;
}



//////////////////////////////
//
// buildFileName --
//

char* buildFileName(char* buffer, const char* basename, int track, int line,
      const char* met) {
   char voicenum[32] = {0};
   strcpy(buffer, basename);
   strcat(buffer, "-T");
   sprintf(voicenum, "%d", track);
   strcat(buffer, voicenum);
   strcat(buffer, "L");
   sprintf(voicenum, "%d", line);
   strcat(buffer, voicenum);
   strcat(buffer, "-");

   if (strcmp(met, "C|") == 0) {
      strcat(buffer, "MenCutC");
   } else if (strcmp(met, "O") == 0) {
      strcat(buffer, "MenCircle");
   } else if (strcmp(met, "C3") == 0) {
      strcat(buffer, "MenC3");
   } else if (strcmp(met, "O/3") == 0) {
      strcat(buffer, "MenOOver3");
   } else if (strcmp(met, "C") == 0) {
      strcat(buffer, "MenC");
   } else if (strcmp(met, "O2") == 0) {
      strcat(buffer, "MenCircle2");
   } else if (strcmp(met, "O|") == 0) {
      strcat(buffer, "MenCutCircle");
   } else if (strcmp(met, "C|3") == 0) {
      strcat(buffer, "MenCutC3");
   } else if (strcmp(met, "O.") == 0) {
      strcat(buffer, "MenCircleDot");
   } else if (strcmp(met, "C2") == 0) {
      strcat(buffer, "MenC2");
   } else if (strcmp(met, "C|2") == 0) {
      strcat(buffer, "MenCutC2");
   } else if (strcmp(met, "2") == 0) {
      strcat(buffer, "Men2");
   } else if (strcmp(met, "3") == 0) {
      strcat(buffer, "Men3");
   } else if (strcmp(met, "Cr") == 0) {
      strcat(buffer, "MenCReverse");
   } else if (strcmp(met, "C.") == 0) {
      strcat(buffer, "MenCDot");
   } else if (strcmp(met, "3/2") == 0) {
      strcat(buffer, "Men3Over2");
   } else if (strcmp(met, "O|3") == 0) {
      strcat(buffer, "MenCutCircle3");
   } else if (strcmp(met, "O|3/2") == 0) {
      strcat(buffer, "MenCutCircle3Over2");
   } else if (strcmp(met, "O3") == 0) {
      strcat(buffer, "MenCircle3");
   } else if (strcmp(met, "C|/2") == 0) {
      strcat(buffer, "MenCutCOver2");
   } else if (strcmp(met, "C|/3") == 0) {
      strcat(buffer, "MenCutCOver3");
   } else if (strcmp(met, "O/3") == 0) {
      strcat(buffer, "MenCircleOver3");
   } else if (strcmp(met, "C.|") == 0) {  // preferred
      strcat(buffer, "MenCutCDot");
   } else if (strcmp(met, "C|.") == 0) {  // in case of alternates
      strcat(buffer, "MenCutCDot");
   } else {
      strcat(buffer, "MenUnknown");
   }

   strcat(buffer, ".krn");

   return buffer;
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("jrp=b",         "JRP catalog extraction from filename");   

   opts.define("debug=b",       "trace input parsing");   
   opts.define("author=b",      "author of the program");   
   opts.define("version=b",     "compilation information"); 
   opts.define("example=b",     "example usage"); 
   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, Nov 2012" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 12 Nov 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);
   }

   debugQ   = opts.getBoolean("debug");
   jrpQ     = opts.getBoolean("jrp");
}



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

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



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

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


// md5sum: d8db7c428a8274f9ffba82ae03efd376 menchop.cpp [20090419]