//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Mar  4 07:02:41 PST 2011
// Last Modified: Fri Mar  4 07:02:43 PST 2011
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/hexa.cpp
// Syntax:        C++; museinfo
//
// Description:   Describe the hexachord position of notes.
//

#include <math.h>
#include <time.h>    /* for current time/date */

#ifndef OLDCPP
   #include <iostream>
   #include <fstream>
   #include <sstream>
   #define SSTREAM stringstream
   #define CSTRING str().c_str()
   using namespace std;
#else
   #include <iostream.h>
   #include <fstream.h>
   #ifdef VISUAL
      #include <strstrea.h>
   #else
      #include <strstream.h>
   #endif
   #define SSTREAM strstream
   #define CSTRING str()
#endif

#include "string.h"

#include "humdrum.h"
#include "PerlRegularExpression.h"
#include "MuseData.h"
#include "MuseDataSet.h"
#include "CheckSum.h"
#include "SigString.h"



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

// function declarations:
void  checkOptions(Options& opts, int argc, char** argv);
void  example(void);
void  usage(const char* command);
void  extractPitches(Array<Array<Array<int> > >& pitches, 
                                HumdrumFile& infile);
void  printData(Array<Array<Array<int> > >& pitches, 
                                HumdrumFile& infile);
void  printLine(HumdrumFile& infile, int line, 
                                Array<Array<Array<int> > >& pitches, 
                                int offset);
void  printDataToken(Array<Array<Array<int> > >& pitches, 
                                int line, int col, char prefix, int interval);
void  printKernTokenAndData(HumdrumFile& infile, int line, int col, 
                                Array<Array<Array<int> > >& pitches,
                                int offset);
char  hexPos(int interval);
void  printSingleColumnLine(HumdrumRecord& arecord, int offset);

// User interface variables:
Options options;
int    debugQ      = 0;           // used with --debug option


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

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

   // initial processing of the command-line options
   checkOptions(options, argc, argv);

   if (options.getArgCount() < 1) {
      infile.read(cin);
   } else {
      infile.read(options.getArg(1));
   }
   infile.analyzeRhythm("4");

   Array<Array<Array<int> > > pitches;
   extractPitches(pitches, infile);

   printData(pitches, infile);

   return 0;
}


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


//////////////////////////////
//
// printData --
//

void printData(Array > >& pitches, HumdrumFile& infile) {
   int i;
   int offset = 75;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isData() || infile[i].isLocalComment() ||
          infile[i].isInterpretation() || infile[i].isMeasure()) {
         printLine(infile, i, pitches, offset);
      } else {
         printSingleColumnLine(infile[i], offset);
      }
   }
}



//////////////////////////////
//
// printSingleColumnLine --  add extract space between staves to allow
//    for printing of analysis data.
//

void printSingleColumnLine(HumdrumRecord& arecord, int offset) {
   PerlRegularExpression pre;
   pre.search(arecord[0], "(.*muse2ps.*)(v[0-9,]+)(.*)", "");
   if (strlen(pre.getSubmatch(2)) == 0) {
      cout << arecord[0] << "\n";
      return;
   }
   Array<char> pos;
   pos.setSize(strlen(pre.getSubmatch(2))+1);
   strcpy(pos.getBase(), pre.getSubmatch());
   Array<Array<char> > tokens;
   PerlRegularExpression pre2;
   pre2.getTokens(tokens, "[v,\\s]+", pre.getSubmatch());
   int value;

   cout << pre.getSubmatch(1);
   cout << "v";
   int i;
   for (i=0; i<tokens.getSize(); i++) {
      value = atoi(tokens[i].getBase());
      value += offset;
      cout << value;
      if (i<tokens.getSize()) {
         cout << ",";
      }
   }

   cout << pre.getSubmatch(3);
   cout << "\n";
}



//////////////////////////////
//
// printLine -- 
//

void printLine(HumdrumFile& infile, int line, 
      Array<Array<Array<int> > >& pitches, int offset) {
   int& i = line;
   int j;
   int started = 0;
   for (j=0; j<infile[i].getFieldCount(); j++) {
      if (!infile[i].isExInterp(j, "**kern")) {
         if (started) {
           cout << "\t";
         }
         started = 1;
         cout << infile[i][j];
         continue;
      } else {
         if (started) {
           cout << "\t";
         }
         started = 1;
         printKernTokenAndData(infile, i, j, pitches, offset);
      }
   }
   cout << "\n";
}



//////////////////////////////
//
// printKernTokenAndData --
//

void printKernTokenAndData(HumdrumFile& infile, int line, int col, 
      Array<Array<Array<int> > >& pitches, int offset) {
   int& i = line;
   int& j = col;
   int m;
   PerlRegularExpression pre;
   Array<char> strang;
   strang.setSize(strlen(infile[i][j])+1);
   strcpy(strang.getBase(), infile[i][j]);

   if (pre.search(strang, "^(!LO:TX.*Y=)(\\d+)(.*)")) {
      // text printed below the staff, but need to drop it so that
      // it does not collide with the text (which cannot be adjsted
      // in muse2ps).
      cout << pre.getSubmatch(1);
      int value = atoi(pre.getSubmatch(2));
      // value = -50;
      cout << value;
      cout << ":hide";   // just hide text so that it is not displayed
      cout << pre.getSubmatch(3);
   } else {
      cout << strang;
   }

   if (infile[i].isLocalComment()) {
      for (m=0; m<3; m++) {
         cout << "\t!";
      }
      return;
   }
   if (infile[i].isMeasure()) {
      for (m=0; m<3; m++) {
         cout << "\t" << infile[i][j];
      }
      return;
   }
   if (infile[i].isInterpretation()) {
      if (strncmp(infile[i][j], "**", 2) == 0) {
         for (m=0; m<3; m++) {
            cout << "\t**hexa";
         }
         return;
      }
      if (strcmp(infile[i][j], "*-") == 0) {
         for (m=0; m<3; m++) {
            cout << "\t*-";
         }
         return;
      }
      for (m=0; m<3; m++) {
         cout << "\t*";
      }
      return;
   }

   char  prefix[3] = {'f', 'c', 'g'};
   int interval[3] = {17, 0, 23};
   for (m=0; m<3; m++) {
      cout << "\t";
      printDataToken(pitches, line, col, prefix[m], interval[m]);
   }
}



//////////////////////////////
//
// printDataToken --
//

void printDataToken(Array<Array<Array<int> > >& pitches, int line, int col,
      char prefix, int interval) {

   int& i = line;
   int& j = col;
   int k;
   char pos;

   if (pitches[i][j].getSize() == 0) {
      cout << ".";
      return;
   }


   for (k=0; k<pitches[i][j].getSize(); k++) {
      if (k > 0) {
         cout << " ";
      }
      pos = hexPos((pitches[line][col][k] - (2 + interval)) % 40);
      cout << prefix << pos;
   }
}



//////////////////////////////
//
// hexPos --
//

char hexPos(int interval) {
   switch (interval) {
      case 0:  return '1';
      case 6:  return '2';
      case 12: return '3';
      case 17: return '4';
      case 23: return '5';
      case 29: return '6';
   }
   return 'X';
}



//////////////////////////////
//
// extractPitches --
//

void extractPitches(Array > >& pitches, HumdrumFile& infile) {
   pitches.setSize(infile.getNumLines());
   PerlRegularExpression pre;
   int i, j, k;
   int value;
   int tokencount;
   char buffer[1024] = {0};
   for (i=0; i<infile.getNumLines(); i++) {
      pitches[i].setSize(infile[i].getFieldCount());
      for (j=0; j<pitches[i].getSize(); j++) {
         pitches[i][j].setSize(0);
      }
      if (!infile[i].isData()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (!infile[i].isExInterp(j, "**kern")) {
            continue;
         }
         if (strcmp(infile[i][j], ".") == 0) {
            // ignore null tokens
            continue;
         }
         if (strchr(infile[i][j], 'r') != NULL) {
            // ignore rests
            continue;
         }
         if (strchr(infile[i][j], ']') != NULL) {
            // ignore endings of ties
            continue;
         }
         if (strchr(infile[i][j], '_') != NULL) {
            // ignore middle notes of ties
            continue;
         }
         tokencount = infile[i].getTokenCount(j);
         for (k=0; k<tokencount; k++) {
            infile[i].getToken(buffer, j, k);
            if (!pre.search(buffer, "[a-g]", "i")) {
               // ignore subtoken: missing a pitch name.
               continue;
            }
            value = Convert::kernToBase40(buffer);
            pitches[i][j].append(value);
         }
      }
   }
}



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

void checkOptions(Options& opts, int argc, char** argv) {
   opts.define("d|debug=b",    "Debugging information");

   opts.define("author=b",    "Program author");
   opts.define("version=b",   "Program version");
   opts.define("example=b",   "Program examples");
   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, March 2011" << "\n";
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 4 March 2011" << "\n";
      cout << "compiled: " << __DATE__ << "\n";
      cout << MUSEINFO_VERSION << "\n";
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }

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



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

void example(void) {


}



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

void usage(const char* command) {

}


// md5sum: 8ac674bbee352e5eee29d3d12df21e8a hexa.cpp [20110308]