/* A note, a gamme, and there it is ! printed into the guitar's manche
 * Yes, my english rox !
 * Sed - november 2001
 */

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

char *modes_names[] =
{
  "major",
  "major_harmonic",
  "minor_natural",
  "minor_harmonic",
  "minor_melodic",
  "pentatonic_major",
  "pentatonic_minor",
  "pentatonic_kumoi",
  "ton_ton",
  0
};

int modes[][12] =
{
  {1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1},	/* major */
  {1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1},	/* major harmonic */
  {1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0},	/* minor natural */
  {1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1},	/* minor harmonic */
  {1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1},	/* minor melodic */
  {1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0},	/* pentatonic_major */
  {1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},	/* pentatonic_minor */
  {1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0},	/* pentatonic_kumoi */
  {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},	/* ton_ton */
};

int guitar[6] =
{
  7,	/* Mi */
  0,	/* La */
  5,	/* Ré */
  10,	/* Sol */
  2,	/* Si */
  7,	/* Mi */
};

char *usage_message =
"Give a note (A, B, C, D, E, F, G) or (la, si, do, re, mi, fa, sol) and\n"
"a key (nothing, # or b), and a gamme (see valid modes below), and the programme\n"
"will output the corresponding cases of the guitar.\n"
"	examples : F# major, Sib minor_melodic\n"
"A first argument may be the number of cases of the guitar, so for 12 cases,\n"
"you would do -c 12 (it must be the first argument).\n";

void print_usage(char *m)
{
  int i;

  fprintf(stderr, "%s :\n%s", m, usage_message);

  fprintf(stderr, "valid modes are :\n");
  for (i = 0; modes_names[i]; i++)
    fprintf(stderr, "\t%s\n", modes_names[i]);
}

void usage(char *m)
{
  print_usage(m);
  exit(0);
}

void error(char *p, char *m)
{
  print_usage(p);
  fprintf(stderr, "\n%s\n", m);
  exit(1);
}

/*
La  1 Si  1/2 Do 1 Re 1 Mi 1/2 Fa 1 Sol 1 La

LA = 0
SI = 2
DO = 3
RE = 5
MI = 7
FA = 8
SOL = 10
 */

typedef struct {
  char *name[2];
  int val;
} note_name;

note_name notes[7] =
{
{{"la", "a"}, 0},		/* A */
{{"si", "b"}, 2},		/* B */
{{"do", "c"}, 3},		/* C */
{{"re", "d"}, 5},		/* D */
{{"mi", "e"}, 7},		/* E */
{{"fa", "f"}, 8},		/* F */
{{"sol", "g"}, 10}		/* G */
};

int get_note(char *note)
{
  int i;
  int len;
  int ret;

  for (i=0; i<7; i++)
    if (!strncasecmp(note, notes[i].name[0], len=strlen(notes[i].name[0])) ||
	!strncasecmp(note, notes[i].name[1], len=strlen(notes[i].name[1])))
      break;

  if (i == 7)
    return -1;

  ret = notes[i].val;

  if (note[len]=='#' && note[len+1] == 0)
    ret++;
  else if (note[len]=='b' && note[len+1] == 0)
    ret--;
  else if (note[len] != 0)
    return -1;

  if (ret == -1)
    ret = 11;

  return ret;
}

int get_mode(char *mode)
{
  int i;

  for (i=0; modes_names[i]; i++)
    if (!strcasecmp(mode, modes_names[i]))
      return i;

  return -1;
}

int indice(int *t, int k)
{
  int i;
  int ret=0;

  for (i=0; i<=k; i++)
    if (t[i] == 1)
      ret++;

  return ret;
}

void print_gamme(int note, int mode, int nb_cases)
{
  int i;
  int j, k;
  int *t = modes[mode];
  char s[6];
  int nb;

  for (i = 5; i>=0; i--) {
    for (j = note-12-guitar[i], k=0; j<nb_cases; j++) {
      k++; if (k==12) k=0;
      if (j<-1) continue;
      strcpy(s, j==-1 ? "     " : "-----");
      if (t[k]) {
	if (k==0) {
	  nb=sprintf(s+1, "*%d*", indice(t, k));
	  snprintf(s+1+nb, 5-nb, j == -1 ? "   " : "---");
	} else {
	  nb=sprintf(s+2, "%d", indice(t, k));
	  snprintf(s+2+nb, 4-nb, j == -1 ? "   " : "---");
	}
      }
      fprintf(stdout, "%s|", s);
    }
    fprintf(stdout, "\n");
  }
}

int main(int nb, char **v)
{
  int i;
  int note;
  int mode;
  int nb_cases=24;

  /* is a -h or a --help here ? */
  for (i = 1; i < nb; i++)
    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage(v[0]);

  if (nb<2)
    error(v[0], "error ! you must give a note");

  if (!strcmp(v[1], "-c")) {
    if (nb<3)
      error(v[0], "error with -c option");

    nb_cases = atoi(v[2]);

    if (nb_cases < 1 || nb_cases > 10000)
      error(v[0], "error with -c option (number of cases is in [1..10000])");

    nb-=2;
    v+=2;
  }

  if (nb<2)
    error(v[-2], "error ! you must give a note");

  note = get_note(v[1]);

  if (nb<3)
    mode = 0;
  else
    mode = get_mode(v[2]);

  if (note == -1)
    error(v[-2], "error ! wrong note given");

  if (mode == -1)
    error(v[-2], "error ! wrong mode given");

  fprintf(stdout, "Gamme in %s, %s\n", v[1], v[2] ? v[2] : "major");
  fprintf(stdout, "This gamme has %d degrees\n", indice(modes[mode], 11));
  print_gamme(note, mode, nb_cases);

  return 0;
}
