//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Feb 15 17:09:16 PST 2011
// Last Modified: Tue Feb 15 17:09:23 PST 2011
// Filename:      ...sig/examples/all/fixdyna.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/lazydynam.cpp
// Syntax:        C++; museinfo
//
// Description:   Fix crescendo and decrescendo symbols (wedges, hairpins).
// 

#include "humdrum.h"
#include "PerlRegularExpression.h"

#ifndef OLDCPP
   using namespace std;
#endif

class Coord {
   public:
      int i, j;
            Coord(void) { }
           ~Coord()     { }
      void  clear(void) { i = j = -1; }
};

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

// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      processFile(HumdrumFile& infile);
void      processDynamicSpine(HumdrumFile& infile, int track);
void      storeDynamSpine(Array<Array<Coord> >& data, HumdrumFile& infile, 
                              int track);


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


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

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

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

   const char* filename;
   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);
   cout << infile;

   return 0;
}

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


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

void processFile(HumdrumFile& infile) {
   int i, j;
   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isInterpretation()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (!infile[i].isExInterp(j, "**dynam")) {
            continue;
         }
         processDynamicSpine(infile, infile[i].getPrimaryTrack(j));
      }
      break;  // just procesing the first exclusive interpretation line.
   }
}



//////////////////////////////
//
// processDynamicSpine -- Only looking at first layer of spine.
//

void processDynamicSpine(HumdrumFile& infile, int track) {
   Array<Array<Coord> > data;
   storeDynamSpine(data, infile, track);
   int i;
   int ii, jj;
   int crescX       = 0;  // interpreted crescendo (cresc. word)
   int decrescX     = 0;  // interpreted decrescendo (decresc. word)
   int crescy       = 0;  // hidden crescendo
   int decrescy     = 0;  // hidden decrescendo
   int crescState   = 0;  // active crescendo
   int decrescState = 0;  // active decrescendo
   char buffer[1024] = {0};
   Array<char> buf;
   PerlRegularExpression pre;

   int xx, yy;

   if (debugQ) {
      cerr << "DATA SIZE = " << data.getSize() << endl;
   }

   for (i=0; i<data.getSize(); i++) {
      ii = data[i][0].i;
      jj = data[i][0].j;

      if (debugQ) {
         cerr << "PROCESSING I = " << i << " " << infile[ii][jj] << endl;
      }
      if (crescState) {
         if (debugQ) {
            cerr << "\tACTIVE CRESC" << endl;
         }
         // if currently a crescendo, then either continue or end
         if (strcmp(infile[ii][jj], ".") == 0) {
            // convert "." to "(" to continue the crescendo
            if (debugQ) {
               cerr << "\tChanging . to (" << endl;
            }
            strcpy(buffer, "(");
            if (crescX) {
               strcat(buffer, "X");
            }
            if (crescy) {
               strcat(buffer, "yy");
            }
            infile[ii].changeField(jj, buffer);
         } else {
            // end the crescendo
            xx = data[i-1][0].i;
            yy = data[i-1][0].j;
            if (debugQ) {
               cerr << "GOT HERE " << infile[xx][yy] << endl;
            }
            if ((xx < 0) || (yy < 0)) {
               continue;
            }
            // this is a dynamic object of some sort so terminate
            // the crescendo on the previous item
            if (strchr(infile[xx][yy], '(') != NULL) {
               if (debugQ) {
                  cerr << "GOT HERE " << infile[xx][yy] << endl;
               }
               // replace the '(' found on the previous data line
               buf.setSize(strlen(infile[xx][yy])+1);
               strcpy(buf.getBase(), infile[xx][yy]);
               pre.sar(buf, "\\(", "[", "");  // should only apply to last 
               if (crescX) {
                  pre.sar(buf, "\\[", "[", "");
                  crescX = 0;
               }
               if (crescy) {
                  pre.sar(buf, "\\[", "[", "");
                  crescy = 0;
               }
               infile[xx].changeField(yy, buf.getBase());
               crescState = 0;
               if (debugQ) {
                  cerr << "DEACTIVATING CRESC 1" << endl;
               }
            } else {
               // there is something other than ( on the last line:
               // append " ["
               strcpy(buffer, infile[xx][yy]);
               strcat(buffer, " [");
               //if (crescy) { 
               //   strcat(buffer, "yy");
               //   crescy = 0;
               //} if (crescX) { 
               //   strcat(buffer, "X");
               //   crescX = 0;
               //}
               infile[xx].changeField(yy, buffer);
               crescState = 0;
               if (debugQ) {
                  cerr << "DEACTIVATING CRESC 2" << endl;
               }
            }
         }

      } else if (decrescState) {

         // if currently a decrescendo, then either continue or end
         if (strcmp(infile[ii][jj], ".") == 0) {
            // convert "." to "(" to continue the decrescendo
            strcpy(buffer, ")");
            if (decrescX) {
               strcat(buffer, "X");
            }
            if (decrescy) {
               strcat(buffer, "yy");
            }
            infile[ii].changeField(jj, buffer);
         } else {
            // end the decrescendo
            xx = data[i-1][0].i;
            yy = data[i-1][0].j;
            if ((xx < 0) || (yy < 0)) {
               continue;
            }
            // this is a dynamic object of some sort so terminate
            // the decrescendo on the previous item
            if (strchr(infile[xx][yy], ')') != NULL) {
               // replace the '(' found on the previous data line
               buf.setSize(strlen(infile[xx][yy])+1);
               strcpy(buf.getBase(), infile[xx][yy]);
               pre.sar(buf, "\\)", "]", "");  // should only apply to last 
               //if (decrescX) {
               //   pre.sar(buf, "\\]", "]X", "");
               //   decrescX = 0;
               //}
               //if (decrescy) {
               //   pre.sar(buf, "\\]", "]y", "");
               //   decrescy = 0;
               //}
               infile[xx].changeField(yy, buf.getBase());
               decrescState = 0;
            } else {
               // there is something other than ( on the last line:
               // append " ["
               strcpy(buffer, infile[xx][yy]);
               strcat(buffer, " ]");
               if (decrescy) { 
                  strcat(buffer, "yy");
                  decrescy = 0;
               } if (decrescX) { 
                  strcat(buffer, "X");
                  decrescX = 0;
               }
               infile[xx].changeField(yy, buffer);
               decrescState = 0;
            }
         }

      } 

      // search for < or > marker to initiate de/cresc.
      if (strchr(infile[ii][jj], '<')  != NULL) { crescState   = 1; }
      if (strchr(infile[ii][jj], '>')  != NULL) { decrescState = 1; }
      if (strstr(infile[ii][jj], "<X") != NULL) { crescX       = 1; }
      if (strstr(infile[ii][jj], ">X") != NULL) { decrescX     = 1; }
      if (strstr(infile[ii][jj], "<yy") != NULL) { crescy      = 1; }
      if (strstr(infile[ii][jj], ">yy") != NULL) { decrescy    = 1; }
   }


   // handle case where cresc/decresc ends data with nothing after it.
   if (crescState) {

      // end the crescendo
      xx = data.last()[0].i;
      yy = data.last()[0].j;
      if (debugQ) {
         cerr << "GOT HERE " << infile[xx][yy] << endl;
      }
      if ((xx < 0) || (yy < 0)) {
         return;
      }
      // this is a dynamic object of some sort so terminate
      // the crescendo on the previous item
      if (strchr(infile[xx][yy], '(') != NULL) {
         if (debugQ) {
            cerr << "GOT HERE " << infile[xx][yy] << endl;
         }
         // replace the '(' found on the previous data line
         buf.setSize(strlen(infile[xx][yy])+1);
         strcpy(buf.getBase(), infile[xx][yy]);
         pre.sar(buf, "\\(", "[", "");  // should only apply to last 
         if (crescX) {
            pre.sar(buf, "\\[", "[", "");
            crescX = 0;
         }
         if (crescy) {
            pre.sar(buf, "\\[", "[", "");
            crescy = 0;
         }
         infile[xx].changeField(yy, buf.getBase());
         crescState = 0;
         if (debugQ) {
            cerr << "DEACTIVATING CRESC 1" << endl;
         }
      }

   } else if (decrescState) {

      // end the decrescendo
      xx = data.last()[0].i;
      yy = data.last()[0].j;
      if (debugQ) {
         cerr << "GOT HERE " << infile[xx][yy] << endl;
      }
      if ((xx < 0) || (yy < 0)) {
         return;
      }
      // this is a dynamic object of some sort so terminate
      // the decrescendo on the previous item
      if (strchr(infile[xx][yy], ')') != NULL) {
         if (debugQ) {
            cerr << "GOT HERE " << infile[xx][yy] << endl;
         }
         // replace the '(' found on the previous data line
         buf.setSize(strlen(infile[xx][yy])+1);
         strcpy(buf.getBase(), infile[xx][yy]);
         pre.sar(buf, "\\)", "]", "");  // should only apply to last 
         if (decrescX) {
            pre.sar(buf, "\\]", "]", "");
            decrescX = 0;
         }
         if (decrescy) {
            pre.sar(buf, "\\]", "]", "");
            decrescy = 0;
         }
         infile[xx].changeField(yy, buf.getBase());
         decrescState = 0;
         if (debugQ) {
            cerr << "DEACTIVATING DECRESC 1" << endl;
         }
      }

   }

}



//////////////////////////////
//
// storeDynamSpine -- make a list of the data spines for the given track
//

void storeDynamSpine(Array<Array<Coord> >& data, HumdrumFile& infile, 
      int track) {
   data.setSize(infile.getNumLines());
   data.setSize(0);
   Coord coord;

   int i, j;
   for (i=0; i<infile.getNumLines(); i++){
      if (!infile[i].isData()) {
         continue;
      }
      data.increase(1);
      data.last().setSize(0);
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (track != infile[i].getPrimaryTrack(j)) {
            continue;
         }
         coord.i = i;
         coord.j = j;
         data.last().append(coord);
      }
   }
}




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

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



//////////////////////////////
//
// 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: 75c005000c7e41eba457053aac3847a3 autodynam.cpp [20110220]