//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Jul  9 13:20:28 PDT 1998
// Last Modified: Thu Jul  9 13:20:32 PDT 1998
// Filename:      ...sig/doc/examples/museinfo/humdrum/sample/sample.cpp
// Syntax:        C++; humdrum
//
// Description:   Samples pitches according to the given pattern
//

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


// function declarations
void         checkOptions(Options& opts, int argc, char* argv[]);
void         example(void);
void         processRecords(HumdrumFile& infile, HumdrumFile& outfile);
void         usage(const char* command);


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

double* sampleDuration;
int     sdSize;

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


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();
      outfile.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
      processRecords(infile, outfile);

      outfile.write(cout);
   }

   return 0;
}


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


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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("r|rhythm|rhythms|=s:4");  // rhythm to sample notes at
   opts.define("d|divide=b");             // determine if to split input notes
   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, July 1998" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 9 July 1998" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << HUMDRUM_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }

   // handle specific program options
   Array<double> sDur;    // duration to take note sample at
   sDur.allowGrowth(1);
   sDur.setSize(0);
   char* aDuration;
   char* aString;
   aString = new char[strlen(opts.getString("rhythms")) + 1];
   strcpy(aString, opts.getString("rhythms"));
   aDuration = strtok(aString, " ,;\t\n:");
   double duration;
   while (aDuration != NULL) {
      duration = Convert::kernToDuration(aDuration);
      sDur.append(duration);
      aDuration = strtok(NULL, " ,;\t\n:");
   }
   sDur.allowGrowth(0);
   sdSize = sDur.getSize();
   sampleDuration = new double[sDur.getSize()];

   for (int i=0; i<sDur.getSize(); i++) {
      sampleDuration[i] = sDur[i];
      cout << sDur[i] << " ";
   }
   cout << endl;
}



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

void example(void) {
   cout <<
   "                                                                         \n"
   "# example usage of the sample program.                                   \n"
   "                                                                         \n"
   << endl;
}



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

void processRecords(HumdrumFile& infile, HumdrumFile& outfile) {
   char aString[128] = {0};
   char outputString[128] = {0};
   int pitch;
   double timetop = 0.0;
   double timebottom = 0.0;
   double duration = 0.0;
   double measureDuration = 0;
   double meterDuration = 0;
   double outputDuration = 0;
   int durationIndex = 0;
   double currDuration = 0;
   double noteDuration = 0;
   int phraseStart = 0;
   int phraseEnd = 0;
   int doPhraseStart = 1;
   int doPhraseEnd = 1;
   int phraseToggle = 0;

   HumdrumRecord currRecord;
   for (int i=0; i<infile.getNumLines(); i++) {
      if (options.getBoolean("debug")) {
         cout << "processing line " << (i+1) << " of input ..." << endl;
      }
      currRecord = infile[i];
      switch (currRecord.getType()) {
         case E_humrec_none:
         case E_humrec_empty:
         case E_humrec_comment:
         case E_humrec_global_comment:
            outfile.appendLine(currRecord);
            break;
         case E_humrec_data_comment:
            outfile.appendLine(currRecord);
            break;
         case E_humrec_data_tandem:
            if (Identify::kernTimeSignature(currRecord[0])) {
               timetop = Convert::kernTimeSignatureTop(currRecord[0]);
               timebottom = Convert::kernTimeSignatureBottomToDuration(
                     currRecord[0]);
               meterDuration = timetop * timebottom;
            }
            outfile.appendLine(currRecord);
            break;
         case E_humrec_data_kern_measure:
            if (timetop == 0) {
               timetop = 4.0;
               timebottom = 1.0;
            }
            measureDuration = 0;
            outfile.appendLine(currRecord[0]);
            break;
         case E_humrec_data:
            if (Identify::humdrumNullField(currRecord[0])) {
               break;
            }
            duration = Convert::kernToDuration(currRecord[0]);
            currDuration += duration;
            pitch = Convert::kernToBase40(currRecord[0]);
            noteDuration = 0;
            doPhraseEnd = 1;
            doPhraseStart = 1;
            if (outputDuration < currDuration) {
               while (outputDuration < currDuration) {
                  noteDuration += sampleDuration[durationIndex % sdSize];
                  outputDuration += sampleDuration[durationIndex % sdSize];
                  if (strchr(currRecord[0], '{') != NULL) {
                     phraseStart = 1;
                  }
                  if (strchr(currRecord[0], '}') != NULL) {
                     phraseEnd = 1;
                  }
                  durationIndex++;
                  if (options.getBoolean("divide")) {
                     strcpy(outputString, "");
                     if (phraseToggle == 0 && phraseStart && doPhraseStart) {
                        strcat(outputString, "{");
                        phraseStart = 0;
                        doPhraseStart = 0;
                        phraseToggle = 1;
                     }
                     strcat(outputString, Convert::durationToKernRhythm(
                           aString, sampleDuration[durationIndex % sdSize]));
                     strcat(outputString, Convert::base40ToKern(aString, 
                           pitch));
                     if (outputDuration >= currDuration && phraseEnd && 
                           doPhraseEnd && phraseToggle == 1) {
                        strcat(outputString, "}");
                        phraseEnd = 0;
                        doPhraseEnd = 0;
                        phraseToggle = 0;
                     }
                     outfile.appendLine(outputString);
                  }
               }
               if (!options.getBoolean("divide")) {
                  strcpy(outputString, "");
                  if (phraseStart && phraseToggle == 0) {
                     strcat(outputString, "{");
                     phraseStart = 0;
                     phraseToggle = 1;
                  }
                  strcat(outputString, Convert::durationToKernRhythm(aString, 
                        noteDuration));
                  strcat(outputString, Convert::base40ToKern(aString, pitch));
                  if (phraseEnd && phraseToggle == 1) {
                     strcat(outputString, "}");
                     phraseEnd = 0;
                     phraseToggle = 0;
                  }
                  outfile.appendLine(outputString);
               }
            } else {
               if (strchr(currRecord[0], '{') != NULL) {
                  phraseStart = 1;
               }
               if (strchr(currRecord[0], '}') != NULL) {
                  phraseEnd = 1;
               }
            }
            break;
         case E_humrec_data_interpretation:
            outfile.appendLine(currRecord[0]);
            break;
         default:
            cerr << "Error on line " << (i+1) << " of input" << endl;
            exit(1);
      }
   }
}



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

void usage(const char* command) {
   cout <<
   "                                                                         \n"
   "Analyzes **kern data and generates a rhythmic analysis which gives       \n"
   "the beat location of **kern data records in the measure.  Currently,     \n"
   "input spines cannot split or join.                                       \n"
   "                                                                         \n"
   "Usage: " << command << " [-a][-b base-rhythm][-s|-d][input1 [input2 ...]]\n"
   "                                                                         \n"
   "Options:                                                                 \n"
   "   -b = set the base rhythm for analysis to specified kern rhythm value. \n"
   "   -d = gives the duration of each kern record in beat measurements.     \n"
   "   -s = sum the beat count in each measure.                              \n"
   "   --options = list of all options, aliases and default values           \n"
   "                                                                         \n"
   << endl;
}