//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Mar 24 16:40:42 PST 2005
// Last Modified: Thu Mar 24 16:40:44 PST 2005
// Filename:      ...sig/examples/all/irangeplot.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/irangeplot.cpp
// Syntax:        C++; museinfo
//
// Description:   Plot data generated by the irange program.
// 

#include "humdrum.h"
#define MAX_CODES 3

// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   storeData(HumdrumRecord& aRecord, int index, 
                                 Array<Array<int> >& rdata);
void   printData(HumdrumFile& infile, 
                                 Array<Array<int> >& rdata);
void   generateAnalysis(HumdrumFile& infile, 
                                 Array<Array<int> >& rdata);
void   analyzeLevel(Array<Array<double> >& codecount, 
                                 Array<Array<int> >& rdata, int level);
void   printRow(Array<Array<double> >& codecount);
void   plotData(Array<Array<int> >& rdata);

// global variables
Options      options;            // database for command-line arguments
int          debugQ     = 0;     // used with the --debug option
int          activityQ  = 0;     // used with the -a option
int          localQ     = 0;     // used with the -l option

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

int main(int argc, char* argv[]) {
   HumdrumFile infile;

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

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

   Array<double> values;
   values.setSize(0);

   Array<Array<int> > rdata;
   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));
      }

      generateAnalysis(infile, rdata);
      if (debugQ) {
         printData(infile, rdata);
      }
      plotData(rdata);

   }

   return 0;
}


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

//////////////////////////////
//
// plotData --
//

void plotData(Array >& rdata) {

   // print header
   cout  << "P3\n";
   int rows = rdata.getSize();
   int cols = rdata.getSize();
   cout  << rows << " " << cols << "\n";
   cout  << "255\n";

   Array<Array<double> > codecount(MAX_CODES);

   int i;
   for (i=0; i<codecount.getSize(); i++) {
      codecount[i].setSize(cols);
   }

   for (i=rows-1; i>=0; i--) {
      analyzeLevel(codecount, rdata, i);
      printRow(codecount);
   }

}



//////////////////////////////
//
// printRow -- 
//

void printRow(Array >& codecount) {
   int i;
   int j;
   for (i=0; i<codecount[0].getSize(); i++) {
      for (j=0; j<3; j++) {
         cout << (int)(256 * codecount[j][i]) << " ";
      }
   }
   cout << "\n";
}


//////////////////////////////
//
// analyzeLevel -- 
//

void analyzeLevel(Array<Array<double> >& codecount, Array<Array<int> >& rdata, 
      int level) {
   int i;
   int j;
   int k;

   for (i=0; i<codecount.getSize(); i++) {
      codecount[i].zero();
   }

   for (i=0; i<rdata.getSize(); i++) {
      if (i+level >= rdata.getSize()) {
         break;
      }

      for (j=0; j < level + 1; j++) {
         for (k=0; k<rdata[i+j].getSize(); k++) {
            if ((rdata[i+j][k] >= 0) && (rdata[i+j][k] < MAX_CODES)) {
               codecount [rdata[i+j][k]] [i] += 1.0;
            }
         }
      } 
   }
   
   Array<double> max(rdata.getSize());
   max.setAll(0.0);
   double globalmax = 0.0;
   Array<double> colormax(3);
   colormax.setAll(0.0);

   for (i=0; i<codecount.getSize(); i++) {  // small dimension
      for (j=0; j<rdata.getSize(); j++) {   // big dimension
         if (codecount[i][j] > colormax[i]) {
            colormax[i] = codecount[i][j];
         }
         if (codecount[i][j] > max[j]) {
            max[j] = codecount[i][j];
         }
         if (codecount[i][j] > globalmax) {
            globalmax = codecount[i][j];
         }
      }
   }

   if (globalmax <= 0.0) {
      return;
   }
   
   if (activityQ) {
      // nomalize one row of pixels according to the maximum
      // count of all range values on a level.
      // normalize the codecount data
      for (i=0; i<codecount.getSize(); i++) {
         for (j=0; j<codecount[0].getSize(); j++) {
            codecount[i][j] /= globalmax;
         }
      }
   } else if (localQ) {
      // normalize each pixel on a level according to the maximum 
      // count for that pixel (local normalization)
      for (i=0; i<codecount.getSize(); i++) {   // small
         for (j=0; j<codecount[i].getSize(); j++) {  // big
            if (max[j] > 0.0) {
               codecount[i][j] /= max[j];
            }
         }
      } 
   } else {
      for (i=0; i<codecount.getSize(); i++) {   // small
         for (j=0; j<codecount[i].getSize(); j++) {  // big
            if (colormax[i] > 0.0) {
               codecount[i][j] /= colormax[i];
            }
         }
      }
   }
}



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

void printData(HumdrumFile& infile, Array >& rdata) {
   int i;
   int j;
   for (i=0; i<rdata.getSize(); i++) {
      for (j=0; j<rdata[i].getSize(); j++) {
         cout << rdata[i][j];
         if (j < rdata[i].getSize() - 1) {
            cout << " ";
         }
      }
      cout << "\n";
   }
}



/*
int main(void) {
   int i = 1;
   increment(&i)
   print("i=%d\n", i);
   // i=2
}

void increment(int *i) {
   *i = *i + 1;
}

int main(void) {
   int i = 1;
   increment(i)
   print("i=%d\n", i);
   // i=2
}

void increment(int &i) {
   i = i + 1;
}

*/

/*
class ACLass {
   public:
      void setData(int adata);
   private:
      int data1;
};

void AClass::setData(int adata) {
    this->data1 = adata;
}

template<class foo>
class ACLass<foo> {
   public:
      void setData(int adata);
   private:
      int data1;
};

template<class foo>
void AClass<foo>::setData(int adata) {
    this->data1 = adata;
}

AClass<int>

*/


//////////////////////////////
//
// generateAnalysis -- Extract the H, M, L tokens from all **irange spines.
//

void generateAnalysis(HumdrumFile& infile, Array >& rdata) {
   rdata.setSize(infile.getNumLines());
   rdata.setSize(0);

   int i;
   int j;
   for (i=0; i < infile.getNumLines(); i++) {
      if (infile[i].getType() != E_humrec_data) {
         continue;
      }
      for (j=0; j < infile[i].getFieldCount(); j++) {
         if (strcmp(infile[i].getExInterp(j), "**irange") == 0) {
            storeData(infile[i], j, rdata);
         }
         break;
      }

   }
}



//////////////////////////////
//
// storeData --
//

#define TIM_UNKNOWN -1
#define TIM_LOW  0
#define TIM_MID  1
#define TIM_HIGH 2

void storeData(HumdrumRecord& aRecord, int index, Array >& rdata) {
   rdata.setSize(rdata.getSize()+1);
   rdata[rdata.getSize()-1].setSize(32);
   rdata[rdata.getSize()-1].setSize(0);
   int i;
   char buffer[1024] = {0};
   int item = 0;
   int tokencount = aRecord.getTokenCount(index);
   for (i=0; i<tokencount; i++) {
      aRecord.getToken(buffer, index, i);

      switch (buffer[0]) {
         case 'L': item = TIM_LOW;  break;
         case 'M': item = TIM_MID;  break;
         case 'H': item = TIM_HIGH; break;
         default:  item = TIM_UNKNOWN;
      }
      if (item != TIM_UNKNOWN) {
         rdata[rdata.getSize()-1].append(item);
      }
   }
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|activity=b",  "do activity normalization");   
   opts.define("l|local=b",     "do local normalization");   

   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, Mar 2005" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 24 Mar 2005" << 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");
   activityQ = opts.getBoolean("activity");
   localQ    = opts.getBoolean("local");

}



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

// md5sum: e0a551b9efec67c31ae88846c6d06ecc irangeplot.cpp [20080227]