//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Mar 26 09:22:01 PDT 2011
// Last Modified: Sat Mar 26 09:22:04 PDT 2011
// Filename:      ...sig/examples/all/diaint.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/diaint.cpp
// Syntax:        C++; museinfo
//
// Description: Extract diatonic invervals from voice pairings.
//

#include "humdrum.h"

#ifndef OLDCPP
   #include <iostream>
#else
   #include <iostream.h>
#endif

#define EMPTY -100000
#define REST -1000

// function declarations
void  checkOptions(Options& opts, int argc, char* argv[]);
void  example(void);
void  usage(const char* command);
void  getIntervals(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile);
void  getKernTrackIndex(Array<int>& ktracks, HumdrumFile& infile);
void  getPitchList(Array<int>& pitchlist, 
                                Array<int>& pitchtrack, Array<int>& pitchcol, 
                                HumdrumFile& infile, int line);
void  generateIntervals(Array<Array<Array<Array<int> > > >& intervals, 
                                Array<int>& pitchlist, Array<int>& pitchtrack, 
                                Array<int>& rktracks, Array<int>& ktracks, 
                                HumdrumFile& infile, int line);
void  generateIntervalsForTrack(Array<Array<Array<Array<int> > > >& intervals, 
                                Array<int>& pitchlist, Array<int>& pitchtrack, 
                                Array<int>& rktracks, Array<int>& ktracks, 
                                HumdrumFile& infile, int line, int spine);
void  printIntervals(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile);
void  printMeasure(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile, int line);
void  printData(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile, int line);
void  printLocalComment(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile, int line);
void  printInterpretation(Array<Array<Array<Array<int> > > >& intervals, 
                                HumdrumFile& infile, int line);

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


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


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();

      // if no command-line arguments read data file from standard input
      if (numinputs < 1) {
         infile.read(cin);
      } else {
         infile.read(options.getArg(i+1));
      }

      Array<Array<Array<Array<int> > > > intervals;
      getIntervals(intervals, infile);

      printIntervals(intervals, infile);
       
   }

   return 0;
}


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


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

void checkOptions(Options& opts, int argc, char* argv[]) {
   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, Mar 2011" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: Mar 2011" << 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);
   }
}



//////////////////////////////
//
// printIntervals --
//

void printIntervals(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile) {

   int m, n, p;
   int value;
   int value2;
   int counter;

   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      cout << infile[i];
      if (infile[i].isLocalComment()) {
         cout << "\t!\n";
         continue;
      }
      if (infile[i].isMeasure()) {
         cout << "\t" << infile[i][0] << "\n";
         continue;
      }
      if (infile[i].isInterpretation()) {
         if (strncmp(infile[i][0], "**", 2) == 0) {
            cout << "\t**diaint\n";
         } else if (strcmp(infile[i][0], "*-") == 0) {
            cout << "\t*-\n";
         } else {
            cout << "\t*\n";
         }
         continue;
      }
      if (!infile[i].isData()) {
         cout << "\n"; 
         continue;
      }
      cout << "\t";

      counter = 0;
      for (m=0; m<intervals.getSize(); m++) {
         for (n=0; n<intervals[m].getSize(); n++) {
            for (p=0; p<intervals[m][n][i].getSize(); p++) {
               value = intervals[m][n][i][p];
               if (value == REST) {
                  continue;
               }
               value2 = Convert::base40IntervalToDiatonic(value);
               if (!octaveQ) {
                  if (value2 < 0) {
                     value2 = -((-value2% 7) + 1);
                  } else {
                     value2 = (value2% 7) + 1;
                  }
                  if (value2 == -1) {
                     // don't bother printing -1 for octave down
                     value2 = 1;
                  }
               }
               if (counter != 0) {
                  cout << " ";
               }
               cout << value2;
               counter++;
            }
         }
      }
      if (counter == 0) {
         cout << ".";
      }
      cout << endl;

/*  Split by **kern spine later:      if (infile[i].isInterpretation()) {
         printInterpretation(intervals, infile, i);
      } else if (infile[i].isMeasure()) {
         printMeasure(intervals, infile, i);
      } else if (infile[i].isData()) {
         printData(intervals, infile, i);
      } else if (infile[i].isLocalComment()) {
         printLocalComment(intervals, infile, i);
      } else {
         cout << infile[i] << "\n";
      }
*/

   }

}


//////////////////////////////
//
// printInterpretation --
//

void printInterpretation(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile, int line) {

}



//////////////////////////////
//
// printMeasure --
//
void printMeasure(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile, int line) {


}



//////////////////////////////
//
// printData --
//
void printData(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile, int line) {

}



//////////////////////////////
//
// printLocalComment --
//

void printLocalComment(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile, int line) {



}



//////////////////////////////
//
// processRecords -- looks at humdrum records and determines chord
//	sonority quality;
//
// intervals.dim          = number of kern spines
// intervals[i].dim       = number of subspines for each voice to right
// intervals[i][j].dim    = length of file
// intervals[i][j][k].dim = number of intervals
//

void getIntervals(Array<Array<Array<Array<int> > > >& intervals, 
      HumdrumFile& infile) {
   int i, j, k;

   Array<int> ktracks;
   getKernTrackIndex(ktracks, infile);

   Array<int> rktracks;
   rktracks.setSize(infile.getMaxTracks()+1);
   rktracks.setAll(-1);
   for (i=0; i<ktracks.getSize(); i++) {
      rktracks[ktracks[i]] = i;
   }

   intervals.setSize(ktracks.getSize());
   for (i=0; i<intervals.getSize(); i++) {
      intervals[i].setSize(ktracks.getSize() - i);
      for (j=0; j<intervals[i].getSize(); j++) {
         intervals[i][j].setSize(infile.getNumLines());
         for (k=0; k<intervals[i][j].getSize(); k++) {
            intervals[i][j][k].setSize(0);
         }
      }
   }

   Array<int> pitchlist;
   Array<int> pitchtrack;
   Array<int> pitchspine;
   

   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isData()) {
         continue;
      }
      // for each kern spine extract a list of pitches
      // which occur to the right of that pitch
      getPitchList(pitchlist, pitchtrack, pitchspine, infile, i);
      if (debugQ) {
         cout << infile[i] << "\t\t";
         for (j=0; j<pitchlist.getSize(); j++) {
            cout << pitchlist[j] << " ";
         }
         cout << endl;
      }
      generateIntervals(intervals, pitchlist, pitchtrack, rktracks, ktracks, 
            infile, i);
   }
}



//////////////////////////////
//
// generateIntervals --
//

void generateIntervals(Array<Array<Array<Array<int> > > >& intervals, 
   Array<int>& pitchlist, Array<int>& pitchtrack, Array<int>& rktracks, 
   Array<int>& ktracks, HumdrumFile& infile, int line) {

   int i;
   for (i=0; i<ktracks.getSize(); i++) {
      generateIntervalsForTrack(intervals, pitchlist, pitchtrack, rktracks,
            ktracks, infile, line, i);
   }
}



//////////////////////////////
//
// generateIntervalsForTrack --
//

void generateIntervalsForTrack(Array<Array<Array<Array<int> > > >& intervals, 
      Array<int>& pitchlist, Array<int>& pitchtrack, Array<int>& rktracks, 
      Array<int>& ktracks, HumdrumFile& infile, int line, int spine) {

   int& i = spine;
   int  j;
   int& k = line;
   int m;
   int interval;
   int track;

   int first = -1;
   for (m=0; m<pitchtrack.getSize(); m++) {
      if (pitchtrack[m] == spine + 1) {
         first = m;
         break;
      }
   }
   if (first < 0) {
      return;
   }
//   if (pitchtrack[first] < 0) {
//      // target note is a rest, so no intervals can be generated
//      return;
//   }

   for (m=first+1; m<pitchlist.getSize(); m++) {
      if (pitchlist[m] < 0) {
         // rest, so no interval can be generated
         // but try storing
         // continue;
         interval = REST;
      } else {
         if (pitchlist[first] < 0) {
            interval = REST;
         } else {
            interval = pitchlist[m] - pitchlist[first];
         }
      }
      track = pitchtrack[m];
      j  = rktracks[track] - pitchtrack[first];
      // probalby a problem with j if there is a chord.
      if (j >= intervals[i].getSize()) {
         // don't know why getting here exactly
         continue;
      }
      intervals[i][j][k].append(interval);
   }
}



//////////////////////////////
//
// getPitchList --
//

void getPitchList(Array<int>& pitchlist, Array<int>& pitchtrack, 
      Array<int>& pitchcol, HumdrumFile& infile, int line) {

   pitchlist.setSize(1000);
   pitchlist.setSize(0);
   pitchtrack.setSize(1000);
   pitchtrack.setSize(0);
   pitchcol.setSize(1000);
   pitchcol.setSize(0);

   int& i = line;
   int j;
   int column;
   int ii;
   int tcount;
   int jj;
   int track;
   int pitch;
   int k;
   char buffer[1024] = {0};

   for (j=0; j<infile[i].getFieldCount(); j++) {
      if (!infile[i].isExInterp(j, "**kern")) {
         continue;         
      }
      if (strchr(infile[i][j], 'r') != NULL) {
         continue;
      }
      track  = infile[i].getPrimaryTrack(j);
      column = j;
      ii = i;
      jj = j;
      if (strcmp(infile[i][j], ".") == 0) {
         ii = infile[i].getDotLine(j);
         jj = infile[i].getDotSpine(j);
         if (ii < 0 || jj < 0) {
            continue;
         }
      }
      tcount = infile[ii].getTokenCount(jj);
      for (k=0; k<tcount; k++) { 
         infile[ii].getToken(buffer, jj, k);
         if (strchr(buffer, 'r') != NULL) {
            pitch = REST;
         } else {
            pitch = Convert::kernToBase40(buffer);
         }
         pitchlist.append(pitch);
         pitchtrack.append(track);
         pitchcol.append(column);
      }
   }
}



//////////////////////////////
//
// getKernTrackIndex --
//

void getKernTrackIndex(Array& ktracks, HumdrumFile& infile) {
   ktracks.setSize(infile.getMaxTracks()+1);
   ktracks.setSize(0);
   int track;
   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, "**kern")) {
            continue;
         }
         track = infile[i].getPrimaryTrack(j);
         ktracks.append(track);
      }
      return;
   }
   return;
}



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

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



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

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



// md5sum: 4f4645780a3f2024ba92f8d62bcb5a77 diaint.cpp [20110327]