//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Nov 29 19:55:27 PST 2000
// Last Modified: Wed Nov 29 19:55:30 PST 2000
// Filename:      ...sig/examples/all/atakcent.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/atakcent.cpp
// Syntax:        C++; museinfo
//
// Description:   Determination of attack accentuation.
//
// 

#include "humdrum.h"

#include <string.h>
#include <ctype.h>
#include <stdio.h>

// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
int    doublecompare(const void* a, const void* b);
void   example(void);
void   generateSummary(HumdrumFile& infile, Array<int>& atakcent);
void   insertdata(double beat, int value, 
                                   Array<double>& beatlocation, 
                                   Array<int>& occurance, Array<int>& sum);
void   intialize(Array<double>& beatlocation, 
                                   Array<int>& occurance, Array<int>& sum);
void   printAnalysis(HumdrumFile& infile, Array<int>& atakcent);
void   printSummary(double lasttop, double lastbottom, 
                                   Array<double>& beatlocation, 
                                   Array<int>& occurance, Array<int>& sum);
void   usage(const char* command);

// global variables
Options      options;            // database for command-line arguments
int          debugQ     = 0;     // used with the --debug option
int          appendQ    = 0;     // used with the -a option
int          compoundQ  = 1;     // used with the -c option
int          summaryQ   = 0;     // used with the -s option

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

int main(int argc, char* argv[]) {
   HumdrumFile infile;
   Array<int>  atakcent;

   // 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));
      }

      infile.analyzeAttackAccentuation(atakcent);
      if (summaryQ) {
         generateSummary(infile, atakcent);
      } else {
         printAnalysis(infile, atakcent);
      }
   }

   return 0;
}


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


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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|append=b",    "append analysis to data in output");   
   opts.define("C|compound=b",  "don't try to use compound meters");   
   opts.define("s|summary=b",   "do a metrical summary");   

   opts.define("debug=b",  "trace input parsing");   
   opts.define("author=b",  "author of the program");   
   opts.define("version=b", "compilation information"); 
   opts.define("example=b", "example usage"); 
   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, Nov 2000" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: Nov 2000" << 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");
   appendQ = opts.getBoolean("append");

   if (opts.getBoolean("compound")) {
      compoundQ = 0;
   } else {
      compoundQ = 1;
   }

   summaryQ = opts.getBoolean("summary");

}



//////////////////////////////
//
// doublecompare -- compare two doubles for ordering
//

int doublecompare(const void* a, const void* b) {
   if (*((double*)a) < *((double*)b)) {
      return -1;
   } else if (*((double*)a) > *((double*)b)) {
      return 1;
   } else {
      return 0;
   }
}



//////////////////////////////
//
// example -- example usage of the maxent program
//

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


//////////////////////////////
//
// generateSummary --
//

void generateSummary(HumdrumFile& infile, Array& atakcent) {
   int line;
   Array<double> top;
   Array<double> bottom;
   double lasttop = -1.0;
   double lastbottom = -1.0;
   
   Array<double> beatlocation;
   Array<int> occurance;
   Array<int> sum;

   infile.analyzeRhythm();

   intialize(beatlocation, occurance, sum);
   infile.analyzeMeter(top, bottom);

   for (line=0; line<infile.getNumLines(); line++) {
      if (infile[line].getType() != E_humrec_data) {
         continue;
      }
      if (lasttop == -1.0) {
         lasttop = top[line];
      }
      if (lastbottom == -1.0) {
         lastbottom = bottom[line];
      }
      if ((lasttop != top[line]) || (lastbottom != bottom[line])) {
         printSummary(lasttop, lastbottom, beatlocation, occurance, sum);
         intialize(beatlocation, occurance, sum);
         lasttop = top[line];
         lastbottom = bottom[line];
      }
      insertdata(infile[line].getBeat(), atakcent[line], beatlocation, 
            occurance, sum);

   }
   printSummary(lasttop, lastbottom, beatlocation, occurance, sum);

}


//////////////////////////////
//
// insertdata --
//

void insertdata(double beat, int value, Array<double>& beatlocation, 
            Array<int>& occurance, Array<int>& sum) {
   int i;
   int foundQ = 0;
   int index = -1;
   for (i=0; i<beatlocation.getSize(); i++) {
      if (beatlocation[i] == beat) {
         foundQ = 1;
         index = i;
         break;
      } 
   }

   if (foundQ) {
      occurance[index]++;
      sum[index] += value;
   } else {
      int one = 1; 
      beatlocation.append(beat);
      occurance.append(one);
      sum.append(value);
   }

}



//////////////////////////////
//
// initialize -- 
//

void intialize(Array<double>& beatlocation, Array<int>& occurance, 
      Array<int>& sum) {

   beatlocation.setSize(0);
   occurance.setSize(0);
   sum.setSize(0);

   beatlocation.allowGrowth(1);
   occurance.allowGrowth(1);
   sum.allowGrowth(1);

}



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

void printAnalysis(HumdrumFile& infile, Array& atakcent) {
   int i;
   if (appendQ) {
      for (i=0; i<infile.getNumLines(); i++) {
         switch (infile[i].getType()) {
         case E_humrec_global_comment:
         case E_humrec_bibliography:
         case E_humrec_none:
         case E_humrec_empty:
            cout << infile[i].getLine() << "\n";
            break;
         case E_humrec_data:
            cout << infile[i].getLine() << "\t";
            cout << atakcent[i] << "\n";
            break;
         case E_humrec_data_comment:
            if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i].getLine() << "\t"
                    << infile[i][0] << "\n";
            } else {
               cout << infile[i].getLine() << "\t!\n";
            }
            break;
         case E_humrec_data_measure:
            if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i].getLine() << "\t"
                    << infile[i][0] << "\n";
            } else {
               cout << infile[i].getLine() << "\t=\n";
            }
            break;
         case E_humrec_data_interpretation:
            if (strncmp(infile[i][0], "**", 2) == 0) {
               cout << infile[i].getLine() << "\t";
               cout << "**atakcent" << "\n";
            } else if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i].getLine() << "\t"
                    << infile[i][0] << "\n";
            } else {
               cout << infile[i].getLine() << "\t*\n";
            }
            break;
         }
      }

   } else {

      for (i=0; i<infile.getNumLines(); i++) {
         switch (infile[i].getType()) {
         case E_humrec_global_comment:
         case E_humrec_bibliography:
         case E_humrec_none:
         case E_humrec_empty:
            cout << infile[i].getLine() << "\n";
            break;
         case E_humrec_data:
            cout << atakcent[i] << "\n";
            break;
         case E_humrec_data_comment:
            if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i][0] << "\n";
            } else {
               // do nothing
            }
            break;
         case E_humrec_data_measure:
            if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i][0] << "\n";
            } else {
               cout << "\t=\n";
            }
            break;
         case E_humrec_data_interpretation:
            if (strncmp(infile[i][0], "**", 2) == 0) {
               cout << "**atakcent" << "\n";
            } else if (infile[i].equalFieldsQ("**kern")) {
               cout << infile[i][0] << "\n";
            } else {
               // do nothing
            }
            break;
         }
      }
   }
}


//////////////////////////////
//
// printSummary --
//

void printSummary(double lasttop, double lastbottom, 
      Array<double>& beatlocation, Array<int>& occurance, Array<int>& sum) {
   if (beatlocation.getSize() == 0) {
      return; 
   }
   Array<double> sortbeat = beatlocation;
   qsort(sortbeat.getBase(), sortbeat.getSize(), sizeof(double),
      doublecompare);                              

   cout << "*M" << lasttop << "/" << 4.0/lastbottom << endl;

   int index, sorti;
   int i;
   for (sorti=0; sorti<sortbeat.getSize(); sorti++) {
      index = -1;
      for (i=0; i<beatlocation.getSize(); i++) {
         if (sortbeat[sorti] == beatlocation[i]) {
            index = i;
            break;
         }
      }
      if (index == -1) {
         cout << "Error printing summary" << endl; 
         exit(1);
      }
      
      cout << beatlocation[index] << "\t"
           << sum[index] << "\t"
           << occurance[index] << "\n";
   }

}



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

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


// md5sum: 6667708dceea865e345b49b8229d7ea8 atakcent.cpp [20050403]