//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Dec  8 00:10:56 PST 2006
// Last Modified: Fri Dec  8 00:10:59 PST 2006
// Filename:      ...sig/examples/all/mazurkascript.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/mazurkascript.cpp
// Syntax:        C++; museinfo
//
// Description:   For displaying regions of mazurka script in a performance.
//

#include "humdrum.h"
#include "PixelColor.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      getData(HumdrumFile& infile, Array<Array<double> >& data);
void      printData(Array<Array<double> >& data);
void      doAnalysis(Array<double>& analysis, 
                             Array<Array<double> >& data, int k);
void      makePixel(PixelColor& pixel, double value);
void      printImage(Array<Array<double> >& data);

// User interface variables:
Options     options;
int         pickup       = 0;
int         spacer       = 3;
int         blocksize    = 5;
double      width        = 5.0;
int         bwq          = 0;
int         threeq       = 0;
int         moreq        = 0;
int         fastq        = 0;
int         slowq        = 0;
int         startmeasure = 1;


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

int main(int argc, char** argv) {
   HumdrumFile hfile;

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

   // figure out the number of input files to process
   int numinputs = options.getArgCount();

   Array<Array<double> > data;
   Array<Array<double> > analysis;
   data.setSize(1000);
   data.setSize(0);

   int k;

   for (int 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);
      } else {
         hfile.read(options.getArg(i+1));
      }

      getData(hfile, data);

      analysis.setSize(data[0].getSize());

      for (k=0; k<data[0].getSize();k++) {
         doAnalysis(analysis[k], data, k);
      }

      // printData(analysis);
      printImage(analysis);

      break;
   }

   return 0;
}


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

//////////////////////////////
//
// printImage --
//


void printImage(Array >& data) {

   Array<Array<PixelColor> > image;
   int performances = data.getSize();
   int measures = data[0].getSize();
   int rows = performances * blocksize + (performances - 1) * spacer;
   int columns = measures * blocksize;
   int i;
   int j;
   int k = 0;
   int m;
   int base;
   int startk;
   int startm;
//cout<<"THERE ARE "<< performances <<" PERFORMANCES TO COMAPRE" <<endl;
//cout <<"THERE ARE "<< measures << "measures in the performamce" << endl;


   i=k;

   PixelColor background;
   PixelColor pixel;
   background.setColor("white");

   // adjust the size of the image
   image.setSize(rows);
   image.allowGrowth(0);
   for (i=0; i<image.getSize(); i++) {
      image[i].setSize(columns);
   }
   //cout << "GOT HERE A" << endl;

   // fill in the background pixels
   for (i=0; i<performances-1; i++) {
      base = i * (blocksize + spacer) + blocksize;
      for (j=0; j<columns; j++) {
         for (k=0; k<spacer; k++) {
           // cout << "i = " << base+k << " j = " <<  j <<endl; 
           image[base+k][j] = background;
         }
      }
   }
   //cout << "GOT HERE B" << endl;


   // fill in the performance data
   for (i=0; i<performances; i++) {
      for (j=0; j<measures; j++) {
         makePixel(pixel, data[i][j]);
         startk = i * (blocksize + spacer);
         startm = j * blocksize;
         //cout << "START = " << startk << " , " << startm << endl;
         for (k=0; k<blocksize; k++) {
            for (m=0; m<blocksize; m++) {
               image[k+startk][m+startm] = pixel;
            }
         }
      }

   }
   //cout << "GOT HERE C" << endl;

   // print the image:
   cout << "P3\n";
   cout << columns << " " << rows << "\n";
   cout << "255\n";
   for (i=0; i<rows; i++) {
      for (j=0; j<columns; j++) {
         image[i][j].writePpm3(cout);
      }
      cout << "\n";
   }

}



//////////////////////////////
//
// doAnalysis --
//

void doAnalysis(Array& analysis, Array >& data, int k) {
   int i;
   double value;
   double value3;
   analysis.setSize(0);
   for (i=pickup; i<data.getSize()-2; i+= 3) {
      value = (data[i][k] - data[i+1][k]) / data[i+1][k] * 100.0;
      value3 = (data[i][k] - data[i+2][k]) / data[i+2][k] * 100.0;
      if (moreq) {
         if (fabs(value3) > fabs(value)) {
            value = value3;
         }
      } else if (threeq) {
         value = value3;   
      } else if (fastq) {
         if (data[i+2][k] > data[i+1][k]) {
            value = value3;
         }
      } else if (slowq) {
         if (data[i+2][k] < data[i+1][k]) {
            value = value3;
         }
      }
      analysis.append(value);
   }
}



//////////////////////////////
//
// printData --
//

void printData(Array >& data) {
   int i;
   int j;
   for (i=0; i<data.getSize(); i++) {
      for (j=0; j<data[i].getSize(); j++) {
         cout << data[i][j] << "\t";
      }
      cout <<"\n";
   }
}
   


//////////////////////////////
//
// getData --
//


void getData(HumdrumFile& infile, Array >& data) {
   int i;
   int j;
   int p;
   double value;

   data.setSize(0);
 
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() != E_humrec_data) {
         continue;
      }
      data.setSize(data.getSize()+1);
      p = data.getSize() - 1;
      data[p].setSize(100);
      data[p].setSize(0);
      for (j=0; j<infile[i].getFieldCount(); j++) {
         value = 0;
         sscanf(infile[i][j], "%lf", &value);
         data[p].append(value);
      }
   }
}




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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("p|pickup=i:0",         "number of pickup beats in measure 0"); 
   opts.define("m|measure=i:1",        "starting measure number"); 
   opts.define("more=b",               "display the beat with more change"); 
   opts.define("fastest=b",            "display the beat with fastest change"); 
   opts.define("slowest=b",            "display the beat with slowest change"); 
   opts.define("3=b",                  "also compare the third beat"); 
   opts.define("b|pixelsize=i:5",      "size of beat blocks"); 
   opts.define("s|spacer=i:3",         "blank pixels between perforances"); 
   opts.define("w|width=d:5.0",        "transition width for coloring"); 
   opts.define("bw=b",                 "display in black and white"); 

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

   pickup       = opts.getInteger("pickup");
   startmeasure = opts.getInteger("measure");
   spacer       = opts.getInteger("spacer");
   width        = opts.getDouble("width");
   bwq          = opts.getBoolean("bw");
   threeq       = opts.getBoolean("3");
   moreq        = opts.getBoolean("more");
   slowq        = opts.getBoolean("slowest");
   fastq        = opts.getBoolean("fastest");
}



//////////////////////////////
//
// makePixel --
//

#define MZSIG(x,w)      (1.0/(1.0+exp(-((double)x)/((double)w))))

void makePixel(PixelColor& pixel, double value) {
   int typeer = 0;
   if (value < 0.0) {
      typeer = 1;
   }

   if (bwq) {
      if (value < -width/100) {
         pixel.setRed(0);
         pixel.setGreen(0);
         pixel.setBlue(0);
      } else if (value > width/100) {
         pixel.setRed(180);
         pixel.setGreen(180);
         pixel.setBlue(180);
      } else {
         pixel.setRed(255);
         pixel.setGreen(255);
         pixel.setBlue(255);
      }
      return;
   }

   value = (MZSIG(value, width) - 0.5) * 2.0;
   if (value < 0.0) {
      value = -value;
   }

   int pval = (int)(value * 256 + 0.5);
   if (pval > 255) {
      pval = 255;
   }
   if (pval < 0) {
      pval = 0;
   }


   if (typeer == 0) {
      pixel.setRed(255);
      pixel.setGreen(255 - pval);
      pixel.setBlue(255 - pval);
   } else if (typeer == 1) {
      pixel.setRed(255 - pval);
      pixel.setGreen(255 - pval);
      pixel.setBlue(255);
   } else {
      pixel.setRed(0);
      pixel.setGreen(0);
      pixel.setBlue(0);
   }

}




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

void example(void) { }



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

void usage(const char* command) { }



// md5sum: 9a4f7368ddcde3339591959bbe0ba338 mazurkascript.cpp [20080518]