/* This program takes a notelist ("Note [ontime] [offtime] [pitch]")
   as input and implements the Longuet-Higgins/Steedman key-finding
   algorithm. */

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

FILE *in_file;
char line[50];
char noteword[10];
int total_duration;
int major_profile[] = {1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1};
int minor_profile[] = {1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1};
//int minor_profile[] = {1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0};
struct {
  int ontime;
  int offtime;
  int pitch;
  int pc;
} note[100000]; 

int numnotes=0;
int verbosity=0;

print_key(int k) {

    if(k==0) printf("C\n");
    if(k==1) printf("C#\n");
    if(k==2) printf("D\n");
    if(k==3) printf("Eb\n");
    if(k==4) printf("E\n");
    if(k==5) printf("F\n");
    if(k==6) printf("F#\n");
    if(k==7) printf("G\n");
    if(k==8) printf("Ab\n");
    if(k==9) printf("A\n");
    if(k==10) printf("Bb\n");
    if(k==11) printf("B\n");
    if(k==12) printf("Cm\n");
    if(k==13) printf("C#m\n");
    if(k==14) printf("Dm\n");
    if(k==15) printf("Ebm\n");
    if(k==16) printf("Em\n");
    if(k==17) printf("Fm\n");
    if(k==18) printf("F#m\n");
    if(k==19) printf("Gm\n");
    if(k==20) printf("Abm\n");
    if(k==21) printf("Am\n");
    if(k==22) printf("Bbm\n");
    if(k==23) printf("Bm\n");
}

main(argc, argv)
int argc;
char *argv[];
{
    int i, z=0, p, k, valid_keys, best_key, found;
    int valid[24];
    int prov_valid[24];

    in_file = NULL;
    if(argc > 1) in_file = fopen(argv[1], "r");
    if(in_file == NULL) in_file = stdin;
    
    while (fgets(line, sizeof(line), in_file) !=NULL) {         
	for (i=0; isspace(line[i]); i++);
	if(line[i] == '\0') continue;
	(void) sscanf (line, "%s", noteword);
	if (strcmp (noteword, "Note") == 0) { 
	    (void) sscanf (line, "%s %d %d %d", noteword, &note[z].ontime, &note[z].offtime, &note[z].pitch);
	    note[z].pc = note[z].pitch % 12;
	    z++;
	}
    }
    
    numnotes = z;
    
    for(k=0; k<24; k++) valid[k] = prov_valid[k] = 1;
    
    for(i=0; i<numnotes; i++) {
	
	for(k=0; k<12; k++) {
	    if( major_profile[((note[i].pc-k) + 24) %12] == 0) prov_valid[k] = 0;
	}

	for(k=12; k<24; k++) {
	    if( minor_profile[((note[i].pc-k) + 24) %12] == 0) prov_valid[k] = 0;
	}

	valid_keys=0;
	if(verbosity>0) printf("Valid keys at note %d (%d, %d): ", i, note[i].pitch, note[i].pc);
	for(k=0; k<24; k++) {
	    if(prov_valid[k]==1) {
		if(verbosity > 0) printf("%d; ", k);
		valid_keys++;
		best_key = k;
	    }
	}
	if(verbosity>0) printf("\n");
	if(valid_keys == 0) break;
	for(k=0; k<24; k++) valid[k] = prov_valid[k];
	if(valid_keys == 1) break;
	    
    }

    if(valid_keys == 1) {
	if(verbosity>0) printf("One valid key: %d\n", best_key);
	print_key(best_key);
    }

    else {
	if(i < numnotes) {
	    if(verbosity>0) printf("All keys eliminated before end of melody\n");
	}
	else {
	    if(verbosity > 0) printf("End of melody reached with multiple valid keys\n");	    
	}
	
	found=0;
	for(k=0; k<24; k++) {
	    if(valid[k] == 1 && note[0].pc == k%12) {
		found=1;
		break;
	    }
	}
	if(!found) {
	    for(k=0; k<24; k++) {
		if(valid[k] == 1 && (note[0].pc + 5) % 12 == k%12) {
		    found=1;
		    break;
		}
	    }
	}

	if(!found) {
	    if(verbosity>0) printf("First-note test yielded no valid key\n");
	    printf("X\n");
	}
	else {
	    if(verbosity>0) printf("First-note check yields a key: %d\n", k);
	    print_key(k);
	}

    }

}

