#include "chord.h"

struct chord_struct chordtab[MAX_CHORD];
int nb_chord;
int first_ptr = 0;

/* char known_chords[256][CHORD_NAME_SZ]; */
struct kcs {
    char chord_name[CHORD_NAME_SZ];
    int displ;
    int s1, s2, s3, s4, s5, s6;
} known_chords[MAX_CHORD];

int nb_known_chord = 0;

extern int hpos, vpos;
extern int grid_size;
extern char mesg[MAXTOKEN];

do_init_grid_ps() {
    printf("%% routines for the drawing of the chords\n");
    printf("/grid { \n");
    printf("        gsave currentpoint\n");
    printf("        6 { 0 cagesizeX rlineto incr cagesize neg incr add rmoveto } repeat\n");
    printf("        moveto\n");
    printf("        5 { cagesize 0 rlineto 0 cagesize sub incr rmoveto } repeat\n");
    printf("        1 setlinewidth stroke grestore\n");
    printf("        gsave\n");
    printf("        0 cagesizeX rmoveto cagesize 0 rlineto\n");
    printf("        cagesize 30 div setlinewidth stroke\n");
    printf("        grestore        \n");
    printf("} def\n");
    printf("\n");
    printf("/dot {\n");
    printf("        gsave\n");
    printf("        /fret exch def /str exch def\n");
    printf("        /x 5  fret abs sub incr mul half-incr sub def\n");
    printf("        /y 6 str sub incr mul def\n");
    printf("        y x rmoveto\n");
    printf("        0 setgray\n");
    printf("        fret -1 eq\n");
    printf("          {\n");
    printf("          0 incr rmoveto\n");
    printf("          check neg  check neg rmoveto\n");
    printf("          check check rlineto check check rlineto\n");
    printf("          check neg check neg rmoveto check neg check rmoveto\n");
    printf("          check check neg rlineto check check neg rlineto\n");
    printf("          cagesize 50 div setlinewidth stroke\n");
    printf("          }\n");
    printf("          {  fret 0 ne\n");
    printf("            { currentpoint dot-size 0 360 arc fill}\n");
    printf("            { currentpoint\n");
    printf("              check 0 360 newpath arc\n");
    printf("              cagesize 50 div setlinewidth\n");
    printf("            }\n");
    printf("            ifelse \n");
    printf("            stroke \n");
    printf("          }\n");
    printf("          ifelse\n");
    printf("        grestore\n");
    printf("} def   \n");
    printf("\n");
    printf("/dots {\n");
    printf("        grid\n");
    printf("        /fret-displ exch def\n");
    printf("          1 1 6\n");
    printf("          {\n");
    printf("            /str exch def\n");
    printf("            /fret exch def\n");
    printf("            fret %d ne {str fret dot} if\n", FRET_NONE);
    printf("          } for\n");
    printf("\n");
    printf("        fret-displ 1 ne\n");
    printf("        {\n");
    printf("          gsave\n");
    printf("          0 cagesize 3 div sub cagesizeX incr sub rmoveto\n");
    printf("          /char 1 string def fret-displ char cvs show grestore \n");
    printf("        } if\n");
    printf("\n");
    printf("        /chord-name exch def\n");
    printf("        gsave displ-font setfont\n");
    printf("        0 cagesizeX cagesizeX 15 div add dot-size 3 mul add rmoveto\n");
    printf("        chord-name stringwidth pop 2 div cagesize 2 div exch sub 0 rmoveto\n");
    printf("        chord-name show chord-font setfont grestore\n");
    printf("} def\n");
    printf("\n");
    printf("/cagesize %d def\n",grid_size);
    printf("/incr cagesize 5 div def\n");
    printf("/cagesizeX incr 4 mul def\n");
    printf("/half-incr incr 2 div def\n");
    printf("/dot-size cagesize 15 div def\n");
    printf("/check cagesize 20 div def\n");
    printf("\n");
    printf("/chord-font /Times-Roman findfont cagesize 5 div scalefont def\n");
    printf("/displ-font /Times-Roman findfont cagesize 3 div scalefont def\n");
    printf("%% end of the routines for the drawing of the chords\n");
    printf("\n");
}

int add_to_chordtab (char *chord) {
    int i, prev_i, n;

    i = first_ptr;
    prev_i = -1;
    while (1) {
        if (nb_chord > 0 && i != -1) {
            n = strcmp (chord, chordtab[i].chord_name);
            if (n == 0)
                return (i);
            else if (n > 0) {
                prev_i = i;
                i = chordtab[i].next_ptr;
                continue;
            }
        } strcpy (chordtab[nb_chord].chord_name, chord);
        if (nb_chord != 0)
            chordtab[nb_chord].next_ptr = i;
        else
            chordtab[nb_chord].next_ptr = -1;
        if (prev_i != -1)
            chordtab[prev_i].next_ptr = nb_chord;
        if (i == first_ptr)
            first_ptr = nb_chord;
        if (nb_chord >= MAX_CHORD - 1) {
            fprintf (stderr, "too many chords, bumping %s\n",
                     chordtab[nb_chord - 1].chord_name);
            return (nb_chord - 1);
        } nb_chord++;

        fprintf (stderr, "dumping chordtab (start is %d)\n", first_ptr);
        for (i = 0; i < nb_chord; i++)
            fprintf (stderr, "       %d:     %10s -> %d\n",
                     i, chordtab[i].chord_name, chordtab[i].next_ptr);
        return (nb_chord - 1);
    }
}

moveto (int new_hpos, int new_vpos) {
    if (new_hpos + grid_size + L_MARGIN > WIDTH) {
        new_hpos = L_MARGIN;
        new_vpos -= 2 * grid_size;
    } if (new_vpos < BOTTOM) {
        do_end_of_page ();
        do_start_of_page ();
        new_vpos = TOP - 2 * grid_size;
    } printf ("%d %d moveto\n", new_hpos, new_vpos);
    hpos = new_hpos;
    vpos = new_vpos;
}

draw_chords () {
    int ptr, idx;

    moveto (WIDTH - grid_size - grid_size - L_MARGIN, vpos);

    ptr = first_ptr;
    if (nb_chord != 0) {
        while (ptr != -1) {
            moveto (hpos + 2 * grid_size, vpos);

            if ((idx = is_chord_known (chordtab[ptr].chord_name)) == -1) {
                sprintf (mesg, "chord \'%s\' has never been defined",
                         chordtab[ptr].chord_name);
                error (mesg);

                printf ("(%s) 0 0 0 0 0 0 1 dots\n",
                        chordtab[ptr].chord_name);
            }
            else
                printf ("(%s) %d %d %d %d %d %d %d dots\n",
                        known_chords[idx].chord_name,
                        known_chords[idx].s1,
                        known_chords[idx].s2,
                        known_chords[idx].s3,
                        known_chords[idx].s4,
                        known_chords[idx].s5,
                        known_chords[idx].s6,
                        known_chords[idx].displ);
            ptr = chordtab[ptr].next_ptr;
        }
    }
}

do_define_chord () {
    int dot_array[6];
    char *chord_name;
    char *temp_str;
    int fret_displ;
    int n;
    int hardtoplay = 0;

    for (n = 0; n < 6; n++)
        dot_array[n] = 0;

    chord_name = (char *) strtok (NULL, DELIM_STR);
    if (chord_name == NULL) {
        error ("syntax error in chord definition: no chord name");
        return (0);
    } temp_str = (char *) strtok (NULL, DELIM_STR);
    if (temp_str == NULL) {
        error ("syntax error in chord definition: no base fret position");
        return (0);
    } fret_displ = atoi (temp_str);

    for (n = 5; n >= 0; n--) {
        temp_str = (char *) strtok (NULL, DELIM_STR);

        if (temp_str != NULL) {
            if (!strcmp (temp_str, FRET_NONE_STR))
                dot_array[n] = FRET_NONE;
            else if (tolower (temp_str[0]) == tolower (FRET_X_STR))
                dot_array[n] = FRET_X;
            else {
                dot_array[n] = atoi (temp_str);
                if (dot_array[n] < 0)
                    dot_array[n] = FRET_NONE;
            } hardtoplay = hardtoplay || (dot_array[n] > LONG_FINGERS);
        }
        else {
            error ("syntax error in chord definition : too few arguments");
            return (0);
        }
    } if (hardtoplay) {
        sprintf (mesg,
                 "Warning: You'll need long fingers to play \"%s\" this way!", chord_name);
        error (mesg);
    } learn_chord (chord_name,
                   dot_array[0], dot_array[1], dot_array[2],
                   dot_array[3], dot_array[4], dot_array[5],
                   fret_displ);
}

learn_chord (char *chord, int s1, int s2, int s3, int s4, int s5, int s6, int displ) {
    int i, not_found;

    not_found = TRUE;
    for (i = 0; i < nb_known_chord && not_found; i++) {
        not_found = strcmp (chord, known_chords[i].chord_name);
    };

    if (not_found) {
        if (nb_known_chord >= MAX_CHORD - 1) {
            fprintf (stderr, "too many chords, can't learn %s\n", chord);
            return (0);
        } nb_known_chord++;
    }
    else {
        i--;
    } strcpy (known_chords[i].chord_name, chord);
    known_chords[i].displ = displ;
    known_chords[i].s1 = s1;
    known_chords[i].s2 = s2;
    known_chords[i].s3 = s3;
    known_chords[i].s4 = s4;
    known_chords[i].s5 = s5;
    known_chords[i].s6 = s6;

}

init_known_chords () {
    nb_known_chord = 0;

#define N FRET_NONE
#define X FRET_X
    learn_chord ("Ab", 1, 3, 3, 2, 1, 1, 4);
    learn_chord ("Abm", 1, 3, 3, 1, 1, 1, 4);   /* G#m is preferred */
    learn_chord ("Ab7", N, N, 1, 1, 1, 2, 1);
    learn_chord ("Abm7", N, N, 1, 1, 1, 1, 4);  /* G#m7 is preferred */
    learn_chord ("Absus", N, N, 1, 1, 2, 4, 1);
    learn_chord ("Ab+", N, N, 2, 1, 1, 0, 1);
    learn_chord ("Abdim", N, N, 0, 1, 0, 1, 1);         /* G#dim is preferred */
    learn_chord ("Abmaj7", N, N, 1, 1, 1, 3, 1);

    learn_chord ("A", N, 0, 2, 2, 2, 0, 1);
    learn_chord ("Am", N, 0, 2, 2, 1, 0, 1);
    learn_chord ("A7", N, 0, 2, 0, 2, 0, 1);
    learn_chord ("Am7", N, 0, 2, 2, 1, 3, 1);
    learn_chord ("Asus", N, N, 2, 2, 3, 0, 1);
    learn_chord ("A+", N, 0, 3, 2, 2, 1, 1);
    learn_chord ("Adim", N, N, 1, 2, 1, 2, 1);
    learn_chord ("Amaj7", N, 0, 2, 1, 2, 0, 1);

    learn_chord ("A#", N, 1, 3, 3, 3, 1, 1);    /* Bb is preferred */
    learn_chord ("A#m", N, 1, 3, 3, 2, 1, 1);   /* Bbm is preferred */
    learn_chord ("A#7", N, N, 1, 1, 1, 2, 3);   /* Bb7 is preferred */
    learn_chord ("A#m7", N, 1, 3, 1, 2, 1, 1);  /* Bbm7 is preferred */
    learn_chord ("A#sus", N, N, 3, 3, 4, 1, 1);         /* Bbsus is preferred */
    learn_chord ("A#+", N, N, 0, 3, 3, 2, 1);   /* Bb+ is preferred */
    learn_chord ("A#dim", N, N, 2, 3, 2, 3, 1);         /* Bbdim is preferred */
    learn_chord ("A#maj7", N, 1, 3, 2, 3, N, 1);        /* Bbmaj7 is preferred */

    learn_chord ("Bb", N, 1, 3, 3, 3, 1, 1);
    learn_chord ("Bbm", N, 1, 3, 3, 2, 1, 1);
    learn_chord ("Bb7", N, N, 1, 1, 1, 2, 3);
    learn_chord ("Bbm7", N, 1, 3, 1, 2, 1, 1);
    learn_chord ("Bbsus", N, N, 3, 3, 4, 1, 1);
    learn_chord ("Bb+", N, N, 0, 3, 3, 2, 1);
    learn_chord ("Bbdim", N, N, 2, 3, 2, 3, 1);
    learn_chord ("Bbmaj7", N, 1, 3, 2, 3, N, 1);

    learn_chord ("B", N, 2, 4, 4, 4, 2, 1);
    learn_chord ("Bm", N, 2, 4, 4, 3, 2, 1);
    learn_chord ("B7", 0, 2, 1, 2, 0, 2, 1);
    learn_chord ("Bm7", N, 1, 3, 1, 2, 1, 2);
    learn_chord ("Bsus", N, N, 3, 3, 4, 1, 2);
    learn_chord ("B+", N, N, 1, 0, 0, 4, 1);
    learn_chord ("Bdim", N, N, 0, 1, 0, 1, 1);
    learn_chord ("Bmaj7", N, 2, 4, 3, 4, N, 1);

    learn_chord ("C", N, 3, 2, 0, 1, 0, 1);
    learn_chord ("Cm", N, 1, 3, 3, 2, 1, 3);
    learn_chord ("C7", 0, 3, 2, 3, 1, 0, 1);
    learn_chord ("Cm7", N, 1, 3, 1, 2, 1, 3);
    learn_chord ("Csus", N, N, 3, 0, 1, 3, 1);
    learn_chord ("C+", N, N, 2, 1, 1, 0, 1);
    learn_chord ("Cdim", N, N, 1, 2, 1, 2, 1);
    learn_chord ("Cmaj7", N, 3, 2, 0, 0, 0, 1);

    learn_chord ("C#", N, N, 3, 1, 2, 1, 1);    /* Db is preferred */
    learn_chord ("C#m", N, N, 2, 1, 2, 0, 1);
    learn_chord ("C#7", N, N, 3, 4, 2, 4, 1);   /* Db7 is preferred */
    learn_chord ("C#m7", N, N, 2, 4, 2, 4, 1);
    learn_chord ("C#sus", N, N, 3, 3, 4, 1, 4);         /* Dbsus is preferred */
    learn_chord ("C#+", N, N, 3, 2, 2, 1, 1);   /* Db+ is preferred */
    learn_chord ("C#dim", N, N, 2, 3, 2, 3, 1);
    learn_chord ("C#maj7", N, 4, 3, 1, 1, 1, 1);        /* Dbmaj7 is preferred */

    learn_chord ("Db", N, N, 3, 1, 2, 1, 1);
    learn_chord ("Dbm", N, N, 2, 1, 2, 0, 1);   /* C#m is preferred */
    learn_chord ("Db7", N, N, 3, 4, 2, 4, 1);
    learn_chord ("Dbm7", N, N, 2, 4, 2, 4, 1);  /* C#m7 is prefered */
    learn_chord ("Dbsus", N, N, 3, 3, 4, 1, 4);
    learn_chord ("Db+", N, N, 3, 2, 2, 1, 1);   /* Db+ is preferred */
    learn_chord ("Dbdim", N, N, 2, 3, 2, 3, 1);
    learn_chord ("Dbmaj7", N, 4, 3, 1, 1, 1, 1);

    learn_chord ("D", N, N, 0, 2, 3, 2, 1);
    learn_chord ("Dm", N, N, 0, 2, 3, 1, 1);
    learn_chord ("D7", N, N, 0, 2, 1, 2, 1);
    learn_chord ("Dm7", N, N, 0, 2, 1, 1, 1);
    learn_chord ("Dsus", N, N, 0, 2, 3, 3, 1);
    learn_chord ("D+", N, N, 0, 3, 3, 2, 1);
    learn_chord ("Ddim", N, N, 0, 1, 0, 1, 1);
    learn_chord ("Dmaj7", N, N, 0, 2, 2, 2, 1);

    learn_chord ("D#", N, N, 3, 1, 2, 1, 3);    /* Eb is preferred */
    learn_chord ("D#m", N, N, 4, 3, 4, 2, 1);   /* Ebm is preferred */
    learn_chord ("D#7", N, N, 1, 3, 2, 3, 1);   /* Eb7 is preferred */
    learn_chord ("D#m7", N, N, 1, 3, 2, 2, 1);  /* Ebm7 is preferred */
    learn_chord ("D#sus", N, N, 1, 3, 4, 4, 1);         /* Ebsus is preferred */
    learn_chord ("D#+", N, N, 1, 0, 0, 4, 1);   /* Eb+ is preferred */
    learn_chord ("D#dim", N, N, 1, 2, 1, 2, 1);
    learn_chord ("D#maj7", N, N, 1, 3, 3, 3, 1);        /* Ebmaj7 is preferred */

    learn_chord ("Eb", N, N, 3, 1, 2, 1, 3);
    learn_chord ("Ebm", N, N, 4, 3, 4, 2, 1);
    learn_chord ("Eb7", N, N, 1, 3, 2, 3, 1);
    learn_chord ("Ebm7", N, N, 1, 3, 2, 2, 1);
    learn_chord ("Ebsus", N, N, 1, 3, 4, 4, 1);
    learn_chord ("Eb+", N, N, 1, 0, 0, 4, 1);
    learn_chord ("Ebdim", N, N, 1, 2, 1, 2, 1);         /* D#dim is preferred */
    learn_chord ("Ebmaj7", N, N, 1, 3, 3, 3, 1);

    learn_chord ("E", 0, 2, 2, 1, 0, 0, 1);
    learn_chord ("Em", 0, 2, 2, 0, 0, 0, 1);
    learn_chord ("E7", 0, 2, 2, 1, 3, 0, 1);
    learn_chord ("Em7", 0, 2, 2, 0, 3, 0, 1);
    learn_chord ("Esus", 0, 2, 2, 2, 0, 0, 1);
    learn_chord ("E+", N, N, 2, 1, 1, 0, 1);
    learn_chord ("Edim", N, N, 2, 3, 2, 3, 1);
    learn_chord ("Emaj7", 0, 2, 1, 1, 0, N, 1);

    learn_chord ("F", 1, 3, 3, 2, 1, 1, 1);
    learn_chord ("Fm", 1, 3, 3, 1, 1, 1, 1);
    learn_chord ("F7", 1, 3, 1, 2, 1, 1, 1);
    learn_chord ("Fm7", 1, 3, 1, 1, 1, 1, 1);
    learn_chord ("Fsus", N, N, 3, 3, 1, 1, 1);
    learn_chord ("F+", N, N, 3, 2, 2, 1, 1);
    learn_chord ("Fdim", N, N, 0, 1, 0, 1, 1);
    learn_chord ("Fmaj7", N, 3, 3, 2, 1, 0, 1);

    learn_chord ("F#", 2, 4, 4, 3, 2, 2, 1);    /* or Gb */
    learn_chord ("F#m", 2, 4, 4, 2, 2, 2, 1);   /* or Gbm */
    learn_chord ("F#7", N, N, 4, 3, 2, 0, 1);   /* or Gb7 */
    learn_chord ("F#m7", N, N, 2, 2, 2, 2, 1);  /* or Gbm7 */
    learn_chord ("F#sus", N, N, 4, 4, 2, 2, 1);         /* or Gbsus */
    learn_chord ("F#+", N, N, 4, 3, 3, 2, 1);   /* or Gb+ */
    learn_chord ("F#dim", N, N, 1, 2, 1, 2, 1);         /* or Gbdim */
    learn_chord ("F#maj7", N, N, 4, 3, 2, 1, 1);        /* Gbdim is preferred */

    learn_chord ("Gb", 2, 4, 4, 3, 2, 2, 1);    /* or F# */
    learn_chord ("Gbm", 2, 4, 4, 2, 2, 2, 1);   /* or F#m */
    learn_chord ("Gb7", N, N, 4, 3, 2, 0, 1);   /* or F#7 */
    learn_chord ("Gbm7", N, N, 2, 2, 2, 2, 1);  /* or F#m7 */
    learn_chord ("Gbsus", N, N, 4, 4, 2, 2, 1);         /* or F#sus */
    learn_chord ("Gb+", N, N, 4, 3, 3, 2, 1);   /* or F#+ */
    learn_chord ("Gbdim", N, N, 1, 2, 1, 2, 1);         /* or F#dim */
    learn_chord ("Gbmaj7", N, N, 4, 3, 2, 1, 1);

    learn_chord ("G", 3, 2, 0, 0, 0, 3, 1);
    learn_chord ("Gm", 1, 3, 3, 1, 1, 1, 3);
    learn_chord ("G7", 3, 2, 0, 0, 0, 1, 1);
    learn_chord ("Gm7", 1, 3, 1, 1, 1, 1, 3);
    learn_chord ("Gsus", N, N, 0, 0, 1, 3, 1);
    learn_chord ("G+", N, N, 1, 0, 0, 4, 1);
    learn_chord ("Gdim", N, N, 2, 3, 2, 3, 1);
    learn_chord ("Gmaj7", N, N, 1, 2, 3, 4, 2);

    learn_chord ("G#", 1, 3, 3, 2, 1, 1, 4);    /* Ab is preferred */
    learn_chord ("G#m", 1, 3, 3, 1, 1, 1, 4);
    learn_chord ("G#7", N, N, 1, 1, 1, 2, 1);   /* Ab7 is preferred */
    learn_chord ("G#m7", N, N, 1, 1, 1, 1, 4);
    learn_chord ("G#sus", N, N, 1, 1, 2, 4, 1);         /* Absus is preferred */
    learn_chord ("G#+", N, N, 2, 1, 1, 0, 1);   /* Ab+ is preferred */
    learn_chord ("G#dim", N, N, 0, 1, 0, 1, 1);
    learn_chord ("G#maj7", N, N, 1, 1, 1, 3, 1);        /* Abmaj7 is preferred */
#undef N
#undef X
}

is_chord_known (char *chord) {
    int i;

    for (i = 0; i < nb_known_chord; i++)
        if (!strcmp (chord, known_chords[i].chord_name))
            return (i);
    return (-1);
}

dump_fret (fretnum) {
    if (fretnum == FRET_NONE)
        printf ("  %2s", FRET_NONE_STR);
    else if (fretnum == FRET_X)
        printf ("  %2s", FRET_X_STR);
    else
        printf ("  %2d", fretnum);
}

dump_chords () {
    int i;

    printf ("#Chord         pos 1st 2nd 3rd 4th 5th 6th\n");
    for (i = 0; i < nb_known_chord; i++) {
        printf ("{define %s      %2d",
                known_chords[i].chord_name, known_chords[i].displ);
        dump_fret (known_chords[i].s6);
        dump_fret (known_chords[i].s5);
        dump_fret (known_chords[i].s4);
        dump_fret (known_chords[i].s3);
        dump_fret (known_chords[i].s2);
        dump_fret (known_chords[i].s1);
        printf ("}\n");
    }
}

