//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Feb 11 15:15:53 PST 2001
// Last Modified: Sat Feb 17 09:52:27 PST 2001
// Last Modified: Fri Apr  6 14:54:35 PDT 2001 (added revised algorithm)
// Filename:      ...sig/examples/all/keyscape.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/keyscape.cpp
// Syntax:        C++; museinfo
//
// Description:   generate a key landscape picture or numeric data.
// 

#include "humdrum.h"

#include <string.h>
#include <math.h>

// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   makeGrayscale(void);
void   processMatrix(Array<ArrayInt>& matrix);
void   printMatrix(Array<ArrayInt>& matrix);
void   printTriangle(Array<ArrayInt>& matrix);
void   printPPM(Array<ArrayInt>& matrix);
void   printAveragePPM(Array<ArrayInt>& matrix);

// global variables
Options      options;            // database for command-line arguments
int          debugQ     = 0;     // used with the --debug option
int          compoundQ  = 1;     // used with the -c option
double       empirical1 = -4;    // used with the --e1 option
double       empirical2 = -3;    // used with the --e2 option
double       sx         = 0.578; // used with the --sx option
double       sy         = 1.0;   // used with the --sx option
int          ppmQ       = 0;     // used with the --ppm option
int          ppmWidth   = 1000;  // used with the -m option
int          ppmHeight  = 500;   // used with the -t option
int          linearQ    = 0;     // used with the -l option
int          monochromeQ = 0;    // used with the -b option
int          matrixQ    = 0;     // used with the -x option
int          mirrorQ    = 0;     // used with the --mirror option
int          maxdivisions = -1;  // used with the -d option
int          averageQ   = 0;     // used with the -a option
int          algorithm  = 0;     // used with the -2 option


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

// Pitch to Color translations

int red[40] = {
	0, 9, 18, 0, 63, 63, 63, 73, 82, 0, 218, 237, 255, 255, 255,
	255, 255, 255, 218, 182, 0, 45, 54, 63, 63, 63, 0, 109, 118,
	127, 145, 164, 0, 255, 255, 255, 255, 255, 73, 36
   };

int green[40] = {
	255, 246, 237, 0, 123, 109, 95, 86, 77, 0, 9, 4, 0, 18, 36, 218,
	237, 255, 255, 255, 0, 209, 200, 191, 177, 164, 0, 50, 41, 31,
	27, 22, 0, 91, 109, 127, 145, 164, 255, 255
   };

int blue[40] = {
	0, 36, 73, 0, 255, 255, 255, 255, 255, 0, 73, 36, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 182, 218, 255, 255, 255, 0, 255, 255, 255, 219,
	182, 0, 0, 0, 0, 0, 0, 0, 0
   };


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

int main(int argc, char* argv[]) {
   HumdrumFile infile;           // input Humdrum Format file
   Array<int>  rhylev;           // storage for metrical level analysis
   Array<double> tonicscores;    // storage for root scores
   tonicscores.setSize(0);
   Array<ArrayInt> matrix;       // storage for root analysis results

   // 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.analyzeRhythm("4");   // force the beat to be quarter notes for now
      infile.analyzeMetricLevel(rhylev);

      double duration = infile.getTotalDuration();
      int totalrows = (int)duration;
      if (maxdivisions > 0 && totalrows > maxdivisions) {
         totalrows = maxdivisions;
      }

      int j, k;
      matrix.setSize(totalrows);
      for (k=0; k<totalrows; k++) {
         matrix[k].setSize(totalrows);
         matrix[k].allowGrowth(0);
         for (j=0; j<totalrows; j++) {
            matrix[k][j] = -1;
         }
      }

      double startbeat;     // absolute start beat of analysis
      double stopbeat;      // absolute ending beat of analysis
      for (j=0; j<totalrows; j++) {
         for (k=0; k<=j; k++) {
            startbeat = duration/(j+1) * k;
            stopbeat = duration/(j+1) * (k+1);
            matrix[j][k] = infile.analyzeChordProbabilityDur(tonicscores, 
               startbeat, stopbeat, rhylev, empirical1, empirical2, sx, sy);
         }
      }

      if (matrixQ) {
         printMatrix(matrix);
      } else if (ppmQ) {
         if (averageQ) {
            printAveragePPM(matrix);
         } else {
            printPPM(matrix);
         }
      } else {
         printTriangle(matrix);
      }
   }

   return 0;
}


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

//////////////////////////////
//
// printPPM -- 
//

void printPPM(Array& matrix) {
   int row, column;
   int maxrow = ppmHeight;
   int maxcolumn = ppmWidth;
   int size = matrix.getSize();
   int i, j;
   int root;

   cout << "P3\n";
   cout << maxcolumn << " " << maxrow << "\n";
   cout << "255\n";

   for (row=0; row<maxrow; row++) {
      if (linearQ) {
         i = (int)(size * (double)row/maxrow);
      } else {
         i = size - (int)(size *
               log10((double)(maxrow-row))/log10((double)(maxrow+1))) - 1;
      }
      for (column=0; column<maxcolumn; column++) {
         j = (int)(i * (double)column/maxcolumn);
         root = matrix[i][j];
         if (root >= 0) {
            cout << red[root]   << " "
                 << green[root] << " "
                 << blue[root]  << "  ";
         } else {
            cout << " 0 0 0  ";
         }
      }
      cout << "\n";
   }
}



//////////////////////////////
//
// printAveragePPM -- 
//

void printAveragePPM(Array& matrix) {
   int row, column;
   int maxrow = ppmHeight;
   int maxcolumn = ppmWidth;
   int size = matrix.getSize();
   int i, j;
   int root;

   cout << "P3\n";
   cout << maxcolumn << " " << maxrow << "\n";
   cout << "255\n";

   Array<double> redsum(maxcolumn);
   Array<double> greensum(maxcolumn);
   Array<double> bluesum(maxcolumn);
   redsum.allowGrowth(0);
   greensum.allowGrowth(0);
   bluesum.allowGrowth(0);
   redsum.zero();
   greensum.zero();
   bluesum.zero();

   for (row=0; row<maxrow; row++) {
      if (linearQ) {
         i = (int)(size * (double)row/maxrow);
      } else {
         i = size - (int)(size * log10((double)(maxrow-row)) /
               log10((double)(maxrow+1))) - 1;
      }
      for (column=0; column<maxcolumn; column++) {
         j = (int)(i * (double)column/maxcolumn);
         root = matrix[i][j];
         if (root >= 0) {
            redsum[column] += red[root];
            greensum[column] += green[root];
            bluesum[column] += blue[root];
         }
      }
   }

   for (i=0; i<redsum.getSize(); i++) {
      redsum[i]   /= maxrow;
      greensum[i] /= maxrow;
      bluesum[i]  /= maxrow;
   }

   for (row=0; row<maxrow; row++) {
      for (column=0; column<maxcolumn; column++) {
         cout << (int)redsum[column] << " "
              << (int)greensum[column] << " "
              << (int)bluesum[column] << "  ";
      }
      cout << "\n";
   }

}



//////////////////////////////
//
// printTriangle --
//

void printTriangle(Array& matrix) {
   int i, j;
   int size = matrix.getSize();
   for (i=0; i<size; i++) {
      cout << i+1;
      for (j=0; j<=i; j++) {
         cout << "\t" << matrix[i][j];
      }
      cout << "\n";
   }
}



//////////////////////////////
//
// printMatrix --
//

void printMatrix(Array& matrix) {
   processMatrix(matrix);
   int i, j;
   int size = matrix.getSize();
   for (i=0; i<size; i++) {
      for (j=0; j<size; j++) {
         cout << matrix[i][j];
         if (j < size-1) {
            cout << "\t";
         }
      }
      cout << "\n";
   }
}



//////////////////////////////
//
// processMatrix --
//

void processMatrix(Array& matrix) {
   int i, j;
   int size = matrix.getSize();
   for (i=0; i<size; i++) {
      for (j=0; j<i; j++) {
         if (mirrorQ) {
            matrix[i-j-1][i] = matrix[i][j];
         } else {
            matrix[j][i] = matrix[i][j];
         }
      }
   }
}





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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("C|compound=b",  "don't try to use compound meters");   
   opts.define("r|rhythm=i:4",  "rhythm to measure root at");   
   opts.define("e1|empirical1=d:-4",  "empirical rhythm value 1");
   opts.define("e2|empirical2=d:-3",  "empirical rhythm value 2");
   opts.define("sx=d:0.578", "scale factor for the x-pitch space axis");
   opts.define("sy=d:1.0", "scale factor for the y-pitch space axis");
   opts.define("2=b",      "0 = old algorithm, 1 = new algorithm");

   opts.define("ppm=b", "generate a ppm graphic lanscape image");
   opts.define("w|width=i:1000", "width of ppm image");
   opts.define("t|height=i:500", "height of ppm image");

   opts.define("l|linear=b", "linear plot instead of logarithmic");
   opts.define("b|monochrome=b", "use black and white instead of color");
   opts.define("x|matrix=b", "generate a matrix rather than a table");
   opts.define("mirror=b","generate mirror matrix rather than cyclical matrix");
   opts.define("d|divisions=i:-1", "the maximum number of divisions");
   opts.define("a|average=b","generate average key picture");

   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, Dec 2000" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 6 Dec 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);
   }
   if (opts.getBoolean("2")) {
      algorithm = 1;
   } else {
      algorithm = 0;
   }

   debugQ = opts.getBoolean("debug");

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

   empirical1 = opts.getDouble("empirical1");
   empirical2 = opts.getDouble("empirical2");
   sx = opts.getDouble("sx");
   sy = opts.getDouble("sy");

   ppmQ         = opts.getBoolean("ppm");
   ppmWidth     = opts.getInteger("width");
   ppmHeight    = opts.getInteger("height");

   linearQ      = opts.getBoolean("linear");
   matrixQ      = opts.getBoolean("matrix");
   mirrorQ      = opts.getBoolean("mirror");
   maxdivisions = opts.getInteger("divisions");
   averageQ     = opts.getBoolean("average");

   if (opts.getBoolean("monochrome")) {
      makeGrayscale();
   }
}



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

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



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

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



//////////////////////////////
//
// makeGrayscale --
//

void makeGrayscale(void) {
   for (int i=0; i<40; i++) {
      red[i]   = (int)((i+1.0)/40.0);
      green[i] = red[i];
      blue[i]  = red[i];
   }
}





// md5sum: 6b9ff0e87377713fd0f1968347abfb40 keyscape.cpp [20080227]