//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue May  4 19:08:22 PDT 2004
// Last Modified: Tue May  4 19:08:25 PDT 2004
// Filename:      ...sig/examples/all/phraseadd.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/phraseadd.cpp
// Syntax:        C++; museinfo
//
// Description:   Add phrases to monophonic music based on a arbitrary 
//                marker placed at the beginning of each phrase.
//

#include "humdrum.h"

#include <string.h>
#include <ctype.h>

#define SLURNONE  0
#define SLURSTART 1
#define SLURCONT  2
#define SLURSTOP  3

#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
   
#include <stdlib.h>             /* for qsort and bsearch functions */

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


// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      processFile(HumdrumFile& infile);
void      getPitchesAndSlurs(HumdrumFile& infile, Array<int>& lines, 
                              Array<int>& pitches, Array<int>& slurstate);
void      printSlurCorrection(HumdrumRecord& line, int slurcorrection);
void      printStringWithSlurEnding(const char* string);
void      printStringWithSlurStart(const char* string, int correction);
void      printFileWithSlurs(HumdrumFile& infile, Array<int>& lines,
                              Array<int>& slurstate);
// global variables
Options   options;            // database for command-line arguments
int       testQ = 0;          // used with -t option
char      slurmarker = 'v';   // used with -s option
const char* filename = "";

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

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

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

   infile.clear();
   // if no command-line arguments read data file from standard input
   int numinputs = options.getArgCount();
   if (numinputs < 1) {
      infile.read(cin);
   } else {
      filename = options.getArg(1);
      infile.read(options.getArg(1));
   }
   processFile(infile);

}

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


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

void processFile(HumdrumFile& infile) {
   Array<int> lines;
   Array<int> pitches;
   Array<int> slurstate;
   getPitchesAndSlurs(infile, lines, pitches, slurstate);
   int i;
   int corr = 0;
   for (i=slurstate.getSize()-1; i>=0; i--) {
      if (slurstate[i] != 0) {
         corr++;
      }
      if ((i>0) && (slurstate[i] == SLURSTART)) {
         slurstate[i-1] = SLURSTOP;
      }
   }
   if (corr > 0) {
      slurstate[slurstate.getSize()-1] = SLURSTOP;
   }

   if (testQ) {
      if (corr) {
         cout << filename << ": " << corr << " phrase markers\n";
         exit(0);
      } else {
         exit(0);
      }
   }

   if (corr) {
      printFileWithSlurs(infile, lines, slurstate);
   } else {
      // no corrections needed
      cout << infile;
   }
}



//////////////////////////////
//
// printFileWithSlurs --
//

void printFileWithSlurs(HumdrumFile& infile, Array<int>& lines,
      Array<int>& slurstate) {
   int i;
   int lindex = 0;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() != E_humrec_data) {
         cout << infile[i] << "\n";
         continue;
      }
      if (lindex >= lines.getSize()) {
         cout << infile[i] << "\n";
         continue;
      }
      if (lines[lindex] == i) {
         if (slurstate[lindex]) {
            // print a slur correction
            printSlurCorrection(infile[i], slurstate[lindex]);
         } else {
            cout << infile[i] << "\n";
         }
         lindex++;
      } else {
         cout << infile[i] << "\n";
      }
   }
}



//////////////////////////////
//
// printSlurCorrection --
//

void printSlurCorrection(HumdrumRecord& line, int slurcorrection) {
   int i;
   for (i=0; i<line.getFieldCount(); i++) {
      if (i != 0) {
         // not processing anything but the first spine.
         cout << "\t" << line[i];
         continue;
      }
      if (slurcorrection == SLURSTOP) {
         printStringWithSlurEnding(line[0]);
      } else if (slurcorrection == SLURSTART) {
         printStringWithSlurStart(line[0], slurcorrection);
      } else {
         cout << line[0];
      }
 
   }
   cout << "\n";
}



//////////////////////////////
//
// printStringWithSlurStart --
//

void printStringWithSlurStart(const char* string, int correction) {
   int i;
   int len = strlen(string);
   cout << "{";
   for (i=0; i<len; i++) {
      if (string[i] == slurmarker) {
         continue;
      }
      if (string[i] == '{') {
         continue;
      }
      cout << string[i];
   }
}



//////////////////////////////
//
// printStringWithSlurEnding --
//

void printStringWithSlurEnding(const char* string) {
   int i;
   int len = strlen(string);
   for (i=0; i<len; i++) {
      if (string[i] == '}') continue;
      if (string[i] == slurmarker) continue;
      cout << string[i];
   }
   cout << "}";
}




//////////////////////////////
//
// getPitchesAndSlurs --
//

void getPitchesAndSlurs(HumdrumFile& infile, Array<int>& lines, 
      Array<int>& pitches, Array<int>& slurstate) {
   int i;
   int pitch;
   int slurs;

   lines.setSize(infile.getNumLines());
   lines.setSize(0);
   pitches.setSize(infile.getNumLines());
   pitches.setSize(0);
   slurstate.setSize(infile.getNumLines());
   slurstate.setSize(0);
 
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() != E_humrec_data) {
         continue;
      }
      if (strcmp(infile[i][0], ".") == 0) {
         // ignore null tokens.
         continue;
      }
      if (strchr(infile[i][0], 'r') != NULL) {
         // ignore rests 
         continue;
      }

      pitch = Convert::kernToBase40(infile[i][0]);
      if (strchr(infile[i][0], slurmarker) != NULL) {
         slurs = SLURSTART;
      } else {
         slurs = SLURNONE;
      }
      lines.append(i);
      pitches.append(pitch);
      slurstate.append(slurs);
   }

}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("t|test=b","check a file to see if it has unprocessed markers");
   opts.define("s|slurmarker=s:v", "character used for slurmarker");

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

   testQ = opts.getBoolean("test");
   slurmarker = (opts.getString("slurmarker"))[0];

}


//////////////////////////////
//
// 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: 4412db1a53c2f89e78e1b11c2adcc89c phraseadd.cpp [20050403]