//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Apr 26 12:57:44 PDT 2004
// Last Modified: Wed Apr 28 00:32:30 PDT 2004
// Filename:      ...sig/examples/all/phrasenum.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/phrasenum.cpp
// Syntax:        C++; museinfo
//
// Description:   Extracts the phrase locations in a monophonic song 
//                according to which note the phrases start on.
//

#include "humdrum.h"

#include <string.h>

#ifndef OLDCPP
   #include <iostream>
   using namespace std;
#else
   #include <iostream.h>
#endif

// includes needed for file/directory processing:
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>


// function declarations:
void      checkOptions(Options& opts, int argc, char** argv);
void      example(void);
void      usage(const char* command);
void      processFile(HumdrumFile& hfile, const char* filename);
void      printAnalysis(Array<int>& phrase, Array<int>& phrasestart,
                             Array<int>& notes, const char* filename);
void      processArgument(const char* path);
int       is_file           (const char* path);
int       is_directory           (const char* path);

// User interface variables:
Options   options;

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

int main(int argc, char** argv) {
   // process the command-line options
   checkOptions(options, argc, argv);

   int i;
   int numinputs = options.getArgCount();
   HumdrumFile hfile;

   for (i=0; i<numinputs || i==0; i++) {
      hfile.clear();

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

   return 0;
}

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


//////////////////////////////
//
// processArgument -- check if the argument is a file or a directory.
//    if a directory, then process all files/subdirectories in it.
//

void processArgument(const char* path) {
   HumdrumFile hfile;
   DIR* dir = NULL;
   char* fullname;
   struct dirent* entry;
   int namelen = 0;
   int valid = 0;

   if (is_file(path)) {
      namelen = strlen(path);
      valid = 0;
      if (strcmp(&(path[namelen-4]), ".thm") == 0) {
         valid = 1;
      } else if (strcmp(&(path[namelen-4]), ".krn") == 0) {
         valid = 1;
      } else if (strcmp(&(path[namelen-4]), ".THM") == 0) {
         valid = 1;
      } else if (strcmp(&(path[namelen-4]), ".KRN") == 0) {
         valid = 1;
      }
      if (!valid) {
         return;
      }
      hfile.read(path);
      processFile(hfile, path);
   } else if (is_directory(path)) {
      dir = opendir(path);
      if (dir == NULL) {
         return;
      }
      entry = readdir(dir);
      while (entry != NULL) {
         if (strncmp(entry->d_name, ".", 1) == 0) {
            entry = readdir(dir);
            continue;
         }

         fullname = new char[strlen(path) + 1 + strlen(entry->d_name) + 1];
         strcpy(fullname, path);
         strcat(fullname, "/");
         strcat(fullname, entry->d_name);
         processArgument(fullname);
         entry = readdir(dir);
      }
   }

   closedir(dir);
}


//////////////////////////////
//
// processFile -- print the analysis
//

void processFile(HumdrumFile& hfile, const char* filename) {
   Array<int> notes;
   Array<int> phrase;
   Array<int> phrasestart;
   notes.setSize(1000);
   notes.setSize(0);
   phrase.setSize(1000);
   phrase.setSize(0);
   phrasestart.setSize(1000);
   phrasestart.setSize(0);
   int pitch;
   int counter = 0;
   int i;
   int start;
   for (i=0; i<hfile.getNumLines(); i++) {
      switch (hfile[i].getType()) {
         case E_humrec_data:
            if (strcmp(hfile[i][0], ".") == 0) {
               break;
            }
            if (strchr(hfile[i][0], ']') != NULL) {
               break;
            }
            if (strchr(hfile[i][0], 'r') != NULL) {
               if (strchr(hfile[i][0], '{') != NULL) { 
                  counter++;
                  start = notes.getSize()+1;
                  phrasestart.append(start);
               }
               break;  // don't store rests
            }
            if (strchr(hfile[i][0], '_') != NULL) {
               break;
            }
            pitch = Convert::kernToBase40(hfile[i][0]);
            notes.append(pitch);
            if (strchr(hfile[i][0], '{') != NULL) { 
               counter++;
               start = notes.getSize();
               phrasestart.append(start);
            }
            phrase.append(counter);
      }
   }

   printAnalysis(phrase, phrasestart, notes, filename);

}


///////////////////////////////
//
// printAnalysis --
//

void printAnalysis(Array<int>& phrase, Array<int>& phrasestart,
      Array<int>& notes, const char* filename) {

   if ((filename != NULL) && (filename[0] != '\0')) {
      cout << filename << "\t";
   }

   // cout << "There are " << phrasestart.getSize() << " phrases.\n";
   // cout << "Starting on the note numbers (offset from 1):\n";
   int i;
   for (i=0; i<phrasestart.getSize(); i++) {
      cout << phrasestart[i];
      if (i<phrasestart.getSize()-1) {
         cout << " ";
      }
   }
   cout << "\t";

   // print the note pitch classes.
   // cout << "Notes\n";
   char buffer[1024] = {0};
   for (i=0; i<notes.getSize(); i++) {
      cout << Convert::base40ToKern(buffer, notes[i] % 40 + 4 * 40);
      if (i<notes.getSize()-1) {
         cout << " ";
      }
   }
   cout << "\n";
}



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

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

}



//////////////////////////////
//
// example --
//

void example(void) {


}



//////////////////////////////
//
// usage --
//

void usage(const char* command) {


}



//////////////////////////////
//
// is_file -- returns true if the string is a file.
//

int is_file(const char* path) {
   struct stat filestat;
   stat(path, &filestat);
   return S_ISREG(filestat.st_mode);
}



//////////////////////////////
//
// is_directory -- returns true if the string is a directory.
//

int is_directory(const char* path) {
   struct stat filestat;
   stat(path, &filestat);
   return S_ISDIR(filestat.st_mode);
}



// md5sum: 737b0c1c8f2a18127e054dd6708ae096 phrasenum.cpp [20050403]