//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Jun 13 18:46:49 PDT 2001
// Last Modified: Wed Jun 13 18:46:49 PDT 2001
// Filename:      ...museinfo/examples/all/parsepmx.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/parsepmx.cpp
// Syntax:        C++; museinfo
//
// Description:   Basic PMX Parser.
//

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

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


// line type defines
#define LINE_UNKNOWN   -1
#define LINE_BLANK      0
#define LINE_COMMENT    1
#define LINE_BOUNDARY   2
#define LINE_DEFINE     3
#define LINE_SETUP      4
#define LINE_INSTRUMENT 5
#define LINE_CLEF       6

// other defines:
#define SPACES          " \t\n\015"

// function declarations:
void      parseSetup(fstream& infile);
int       getline(char* buffer, fstream& file);
void      displayOriginalLine(const char* buffer);
int       identifyline(const char* buffer, int stage);
void      printCommentary(const char* buffer);
void      printLineType(int type);
void      parseSetupLine(const char* buffer);
void      printSetupVariables(void);
void      parseBody(fstream& infile);
void      parseInstrumentLine(const char *inbuffer);
void      parseClefLine(const char *inbuffer);


// global variables
int stage = 0;    // part of file being read: 0=start, 1=def, 2=setup, 3=body
int line  = 0;      // number of the line being read.
int namecount = 0;  // number of instrument names read


// setup variables read from header:
int setupcount = 0;              // number of setup variables read
double setup[12] = {0};          // storage for setup variables
double& nv         = setup[0];   // number of staves in a system
double& noinst     = setup[1];   // number of instruments on a system
double& mtrnuml    = setup[2];   // logical meter numerator
double& mtrdenl    = setup[3];   // logical meter denominator
double& mtrnump    = setup[4];   // printed meter numerator
double& mtrdenp    = setup[5];   // printed meter denominator
double& xmtrnum0   = setup[6];   // number of beats in the pickup measure
double& isig       = setup[7];   // key signature
double& npages     = setup[8];   // number of pages to create
double& nsyst      = setup[9];   // total number of systems to create
double& musicsize  = setup[10];  // point size of staves
double& fracindent = setup[11];  // indentation of the first system


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

int main(int argc, char** argv) {
   if (argc != 2) {
      cout << "Usage: " << argv[0] << " input-pmx-file" << endl;
      exit(1);
   }

   #ifndef OLDCPP
      fstream infile(argv[1], ios::in);
   #else
      fstream infile(argv[1], ios::in | ios::nocreate);
   #endif

   if (!infile.is_open()) {
      cout << "Error: cannot open " << argv[1] << " for reading" << endl;
      exit(1);
   }
   
   parseSetup(infile);
   printCommentary("This is the end of the SETUP section");
   printCommentary("Now begins the body of the PMX file" );
   parseBody(infile);
   
   return 0;
}


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


//////////////////////////////
//
// parseBody -- read the musical information section
//

void parseBody(fstream& infile) {



}



//////////////////////////////
//
// parseSetup -- read up to the end of the setup section of the music.
//

void parseSetup(fstream& infile) {
   char inbuffer[2048] = {0};
   char message[1024] = {0};
   int done = 0;
   int lineid = 0;

   while (!done) {
      line = getline(inbuffer, infile);
      displayOriginalLine(inbuffer);
      if (infile.eof()) {
         cout << "Error: EOF when not finished parsing file" << endl;
         exit(1);
      }

      lineid = identifyline(inbuffer, stage);
      printLineType(lineid);
      switch(lineid) {
         case LINE_BOUNDARY:
            stage++;
            sprintf(message, "Going to stage %d", stage);
            printCommentary(message);
            break;
         case LINE_SETUP:
            parseSetupLine(inbuffer);
            break;
         case LINE_INSTRUMENT:
            parseInstrumentLine(inbuffer);
            break;
         case LINE_CLEF:
            parseClefLine(inbuffer);
            break;
      }

      if (stage >= 5) {
         done = 1;
         break;
      }
   }

}



//////////////////////////////
//
// parseClefLine -- doesn't store the clefs for now.
//

void parseClefLine(const char *inbuffer) {
   char tbuffer[2048] = {0};
   int len = strlen(inbuffer);
   strcpy(tbuffer, inbuffer);
   char * ptr = strtok(tbuffer, SPACES);

   len = strlen(ptr);
   if (len != (int)nv) {
      cout << "Error on line " << line << ": incorrect clef count" << endl;
      exit(1);
   }
   stage = 5;
}



//////////////////////////////
//
// parseInstrumentLine --
//

void parseInstrumentLine(const char *inbuffer) {
   char message[1024] = {0};
   namecount++;
   sprintf(message, "Instrument name %d: %s", namecount, inbuffer);
   printCommentary(message);
   if (namecount >= noinst) {
      stage = 4;  
      printCommentary("Next line is expected to be the clef indicator line");
   }
}


//////////////////////////////
//
// parseSetupLine -- 
//

void parseSetupLine(const char* buffer) {
   char tbuffer[2048] = {0};
   // int length = strlen(buffer);
   char message[1024] = {0};
   strcpy(tbuffer, buffer);
   char* ptr = strtok(tbuffer, SPACES);
   while (ptr != NULL) {
      if (ptr[0] == '%') {
         return;
      }
      if (setupcount > 11) {
         cout << "Error on line " << line << ":  too many setup values" << endl;
         exit(1);
      }
      setup[setupcount] = strtod(ptr, NULL);
      setupcount++;
      sprintf(message, "Read setup variable number %d", setupcount);
      printCommentary(message);
      if (setupcount >= 12) {
         stage = 3;
         printSetupVariables();
         printCommentary("Finished reading setup variables");
         printCommentary("Expecting next non-comment line to be inst. names");
      }

      if (strchr(ptr, '%') != NULL) {
         return;
      }
      ptr = strtok(NULL, SPACES);
   }
}



//////////////////////////////
//
// identifyline -- identify what type of line this is.
//

int identifyline(const char* buffer, int stage) {
   char tbuffer[2048] = {0};
   int len = strlen(buffer);
   int plen;

   if (len == 0 && stage == 3) {
      return LINE_INSTRUMENT;
   }

   if (len == 0) {
      return LINE_BLANK;
   }

   strcpy(tbuffer, buffer);
   char* ptr = strtok(tbuffer, SPACES);
   if (ptr == NULL) {
      if (stage == 3) {
         return LINE_INSTRUMENT;
      } else {
         return LINE_BLANK;
      }
   }

   // determine if this is a comment line
   plen = strlen(ptr);
   if (plen == 0) {
      cout << "Error reading line: " << line << endl;
      exit(1);
   }
   if (ptr[0] == '%') {
      return LINE_COMMENT;
   }

   if (stage == 4) {
      return LINE_CLEF;
   }

   if (stage == 3) {
      return LINE_INSTRUMENT;
   }

   if (strcmp(ptr, "---") == 0) {
      return LINE_BOUNDARY;
   } 

   if (stage == 1) {
      return LINE_DEFINE;
   }

   if (stage == 2 || stage == 0) {
      if (isdigit(ptr[0]) || ptr[0] == '-') {
         return LINE_SETUP;
         stage = 2;
      }
   }

   return LINE_UNKNOWN;

}



//////////////////////////////
//
// displayOriginalLine -- print the original line with a special start char.
//

void displayOriginalLine(const char* buffer) {
   cout << "===>\t" << buffer << endl;
}



//////////////////////////////
//
// getline -- get a line from the file and increment the current
//     line number
//

int getline(char* buffer, fstream& file) {
   static int linecounter = 0;
   file.getline(buffer, 1000, '\n');
   linecounter++;
   return linecounter;
}



//////////////////////////////
//
// printCommentary -- print a parser comment about the currentline
//

void printCommentary(const char* buffer) {
   cout << "    <+++\t" << buffer << endl;
}



//////////////////////////////
//
// printLineType -- print the type of line being processed
//

void printLineType(int type) {
   switch (type) {
      case LINE_UNKNOWN:   printCommentary("Unknown line type");   break;
      case LINE_BLANK:     printCommentary("Empty line");          break;
      case LINE_COMMENT:   printCommentary("Comment line");        break;
      case LINE_BOUNDARY:  printCommentary("Boundary line");       break;
      case LINE_DEFINE:    printCommentary("Definition line");     break;
      case LINE_SETUP:     printCommentary("Setup line");          break;
      case LINE_INSTRUMENT:printCommentary("Instrument name line");break;
      case LINE_CLEF:      printCommentary("Clef line");           break;
      default:             printCommentary("UNDOCUMENTED LINE");   break;
   }
}



//////////////////////////////
//
// printSetupVariables --
//

void printSetupVariables(void) {
   char message[1024] = {0};
   sprintf(message, "1:  nv         = %f  (number of staves per system)", nv);
   printCommentary(message);
   sprintf(message, "2:  noinst     = %f  (number of instruments per system)", noinst);
   printCommentary(message);
   sprintf(message, "3:  mtrnuml    = %f  (logical meter numerator)", mtrnuml);
   printCommentary(message);
   sprintf(message, "4:  mtrdenl    = %f  (logical meter denominator)", mtrdenl);
   printCommentary(message);
   sprintf(message, "5:  mtrnump    = %f  (printing meter denominator)", mtrnump);
   printCommentary(message);
   sprintf(message, "6:  mtrdenp    = %f  (printing meter denominator)", mtrdenp);
   printCommentary(message);
   sprintf(message, "7:  xmtrnum0   = %f  (pickup measure beats)", xmtrnum0);
   printCommentary(message);
   sprintf(message, "8:  isig       = %f  (key signature)", isig);
   printCommentary(message);
   sprintf(message, "9:  npages     = %f  (number of pages)", npages);
   printCommentary(message);
   sprintf(message, "10: nsyst      = %f  (total number of systems)", nsyst);
   printCommentary(message);
   sprintf(message, "11: musicsize  = %f  (music size)", musicsize);
   printCommentary(message);
   sprintf(message, "12: fracindent = %f  (first line indent)", fracindent);
   printCommentary(message);
}



// md5sum: e30a832d7bd503b7a4a2f56a4fdc7bac parsepmx.cpp [20050403]