#include "chord.h"

unsigned char versiontag[] = "\0$VER: AmiChord 1.2 (" __DATE__ ")";

FILE *source_fd;
FILE *toc_file = NULL;          /* optional table of contents file */

unsigned char
        text_line[MAXLINE],     /* Lyrics Buffer */
        chord[MAXTOKEN],        /* Buffer for the name of the chord */
        title1[MAXLINE];        /* Holds the first title line */

char
        source[MAXTOKEN],
        directive[MAXTOKEN],    /* Buffer for the directive */
        mesg[MAXTOKEN];

char
        i_input;                /* Input line pointer */
char
        *text_font = DEF_TEXT_FONT,     /* Default font for text */
        *chord_font = DEF_CHORD_FONT,   /* Default font for chord */
        *used_fonts[MAXFONTS][MAXLINE],
        *current_file,
        *chord_line[MAXLINE];

char *notes[7] =  { "C", "D", "E", "F", "G", "A", "B" };

int
        c,                      /* Current character in input file */
        i_chord,                /* Index to 'chord' */
        i_directive,            /* Index to 'directive' */
        i_text,                 /* Index to 'text_line' */
        in_chord,               /* Booleans indicating parsing status */
        lyrics_only = FALSE,
        auto_space = FALSE,     /* applies lyrics_only when no chords */
        left_foot_odd = -1,     /* -1 for all page numbers on the right */
                                /* 0 for odd page numbers on the right */
                                /* 1 for odd page numbers on the left */
        number_all = FALSE,     /* number the first page (set with -p 1) */
        page_label = 1,         /* page number for footers and index */
        i_chord_ov,             /* Overflow Booleans */
        i_directive_ov = FALSE,
        i_text_ov = FALSE,
        in_directive = FALSE,
        in_chordrc = FALSE,
        in_chorus = FALSE,
        has_directive = FALSE,
        has_chord = FALSE,
        title1_found = FALSE,
        debug_mode = FALSE,
        need_soc = FALSE,
        pagination = 1,         /* virtual pages per physical pages */
        transpose = 0,          /* transposition value */
        vpos,                   /* Current PostScript position, in points */
        hpos,
        start_of_chorus,        /* Vertical positions of the chorus */
        end_of_chorus,
        chord_size = DEF_CHORD_SIZE,    /* Default font size for 'chord_font' */
        text_size = DEF_TEXT_SIZE,      /* Default font size for 'text_font' */
        grid_size = DEF_GRID_SIZE,
        n_pages = 1,            /* total page counter */
        v_pages = 1,            /* virtual pages */
        n_lines = 1,            /* line number in input file */
        song_pages = 1,         /* song page counter */
        blank_space = 0;        /* consecutive blank line counter */

float
        chord_inc,
        scale = 1.0,            /* Current scale factor */
        rotation = 0.0;         /* Current rotation */

extern int nb_chord, first_ptr;
extern struct chord_struct chordtab[MAX_CHORD];

extern char *optarg;
extern int optind, opterr;

/* --------------------------------------------------------------------------------*/
use_chord_font() {
    printf ("CHORD_FONT setfont\n");
}

/* --------------------------------------------------------------------------------*/
set_chord_font() {
    printf ("/CHORD_FONT { /%s findfont %d scalefont } def\n", chord_font, chord_size);
    re_encode (chord_font);
}

/* --------------------------------------------------------------------------------*/
use_text_font() {
    printf ("TEXT_FONT setfont\n");
}

/* --------------------------------------------------------------------------------*/
set_text_font (int size) {
    printf ("/TEXT_FONT { /%s findfont %d scalefont } def\n", text_font, size);
    re_encode (text_font);
}

/* --------------------------------------------------------------------------------*/
do_help (char *command) {
    fprintf (stderr, "Usage: %s [options] file\n", command);
    fprintf (stderr, "Options:\n");
    fprintf (stderr, "      -c n               : set chord size [9]\n");
    fprintf (stderr, "      -C postsctipt_font : Set chord font\n");
    fprintf (stderr, "      -t n               : set text size [12]\n");
    fprintf (stderr, "      -T postsctipt_font : set text font\n");
    fprintf (stderr, "      -g n               : set chord grid size [30]\n");
    fprintf (stderr, "      -l                 : only print lyrics\n");
    fprintf (stderr, "      -h                 : This message\n");
    fprintf (stderr, "      -2                 : 2 pages per sheet\n");
    fprintf (stderr, "      -4                 : 4 pages per sheet\n");
    fprintf (stderr, "      -d                 : dumps default chords definitions\n");
    fprintf (stderr, "      -i file            : write table of contents to file\n");
    fprintf (stderr, "      -a                 : automatic single space lines without chords\n");
    fprintf (stderr, "      -p n               : starting page number [1]\n");
    fprintf (stderr, "      -R                 : odd pages numbers on right\n");
    fprintf (stderr, "      -L                 : odd pages numbers on left\n");
}

/* --------------------------------------------------------------------------------*/
do_chord (int i_text, char *chord) {
    if (transpose != 0)
        if (do_transpose (chord) != 0) {
            sprintf (mesg, "Don't know how to transpose [%s]", chord);
            error (mesg);
    } if (i_text < MAXLINE)
        chord_line[i_text] = chordtab[add_to_chordtab (chord)].chord_name;
}

/* --------------------------------------------------------------------------------*/
print_chord_line () {
    int i, j;                   /* Counter */

    for (j = 0; j < MAXLINE; j++) {
        if (chord_line[j] != NULL) {
            use_text_font ();
            printf ("(");
            for (i = 0; i < j; printf ("%c", text_line[i++]));
            printf (") stringwidth  pop %d add %d moveto\n", hpos, vpos);

            use_chord_font ();
            printf ("(%s) show\n", chord_line[j]);

            chord_line[j] = NULL;
        }
    }
}

/* --------------------------------------------------------------------------------*/
do_directive (char *directive) {
    int i;
    char *command, *comment;

    command = (char *) strtok (directive, ": ");

    if (!strcmp (command, "start_of_chorus") || !strcmp (command, "soc")) {
        /* start_of_chorus = vpos - blank_space; */
        need_soc = TRUE;
        in_chorus = TRUE;
    }
    else if (!strcmp (command, "end_of_chorus") || !strcmp (command, "eoc")) {
        if (in_chorus) {
            end_of_chorus = vpos;
            do_chorus_line ();
            in_chorus = FALSE;
        }
        else
            error ("Not in a chorus.");
    }
    else if (!strcmp (command, "textfont") || !strcmp (command, "tf")) {
        text_font = (char *) strtok (NULL, ": ");
    }
    else if (!strcmp (command, "chordfont") || !strcmp (command, "cf")) {
        chord_font = (char *) strtok (NULL, ": ");
        set_chord_font ();
    }
    else if (!strcmp (command, "chordsize") || !strcmp (command, "cs")) {
        i = atoi ((char *) strtok (NULL, ": "));
        if (i == 0)
            error ("invalid value for chord_size");
        else
            chord_size = i;
        set_chord_font ();
    }
    else if (!strcmp (command, "textsize") || !strcmp (command, "ts")) {
        i = atoi ((char *) strtok (NULL, ": "));
        if (i == 0)
            error ("invalid value for text_size");
        else
            text_size = i;
    }
    else if (!strcmp (command, "comment") || !strcmp (command, "c")) {
        comment = (char *) strtok (NULL, "\0");
        advance (blank_space);
        blank_space = 0;
        advance (text_size);
        text_line[i_text] = '\0';
        use_text_font ();
        printf (".9  setgray\n");
        printf ("%d  setlinewidth\n", text_size);
        printf ("newpath\n");
        printf ("%d %d moveto\n", hpos, vpos + text_size / 2 - 2);
        printf ("(%s) stringwidth rlineto\n", comment);
        printf ("stroke\n");
        printf ("%d %d moveto\n", hpos, vpos);
        printf ("0  setgray\n");
        printf ("1 setlinewidth\n");
        printf ("(%s) show\n", comment);
        i_text = 0;
    }
    else if (!strcmp (command, "new_song") || !strcmp (command, "ns")) {
        do_new_song ();
    }
    else if (!strcmp (command, "title") || !strcmp (command, "t")) {
        do_title ();
    }
    else if (!strcmp (command, "subtitle") || !strcmp (command, "st")) {
        do_subtitle ();
    }
    else if (!strcmp (command, "define") || !strcmp (command, "d")) {
        do_define_chord ();
    }
    else {
        sprintf (mesg, "Invalid Directive : [%s]", command);
        error (mesg);
        has_directive = FALSE;
    }
}

/* --------------------------------------------------------------------------------*/
do_chorus_line () {
    printf ("1  setlinewidth\n");
    printf ("newpath\n");
    printf ("%d %d moveto\n", L_MARGIN - 10, start_of_chorus);
    printf ("0 %d rlineto\n", -(start_of_chorus - end_of_chorus));
    printf ("closepath\n");
    printf ("stroke\n");
}

/* --------------------------------------------------------------------------------*/
do_translate(float vert, float horiz) {
    printf ("%f %f translate\n", vert , horiz );
    debug ("changing translation");
}

/* --------------------------------------------------------------------------------*/
do_new_song () {
    do_end_of_song ();
    nb_chord = first_ptr = 0;
    song_pages = 0;
    do_start_of_page ();
    init_known_chords ();
    read_chordrc ();
}

/* --------------------------------------------------------------------------------*/
init_ps () {
    printf ("%%!PS-Adobe-1.0\n");
    printf ("%%%%Title: A song\n");
    printf ("%%%%Creator: Martin Leclerc & Mario Dorion\n");
    printf ("%%%%Pages: (atend)\n");
    printf ("%%%%BoundingBox: 5 5 605 787\n");
    printf ("%%%%EndComments\n");
    printf ("/inch {72 mul } def\n");

    print_re_encode ();
    set_chord_font ();
    set_text_font (text_size);
    do_init_grid_ps ();
    init_known_chords ();

    printf ("%%%%EndProlog\n");

    printf ("%%%%Page: \"%d\" %d\n", n_pages, n_pages);
    printf ("%%%%BeginPageSetup\n");
    printf ("/pgsave save def\n");
    printf ("%f %f scale\n", scale, scale);
    printf ("%f rotate\n", rotation);
    printf ("%%%%EndPageSetup\n");

    vpos = TOP;
    hpos = L_MARGIN;
    if (pagination == 4)
        do_translate (0.0, 800.0);
    else if (pagination == 2)
        do_translate (-40.0, -800.0);

}

/* --------------------------------------------------------------------------------*/
do_end_of_song() {
    draw_chords();
    do_end_of_page();
}

/* --------------------------------------------------------------------------------*/
do_end_of_phys_page () {
    debug ("end_of_phys_page");
    printf ("pgsave restore\n");
    printf ("showpage\n");
    printf ("%%%%EndPage: \"%d\" %d\n", n_pages, n_pages);
}

/* --------------------------------------------------------------------------------*/
do_end_of_page () {
    printf ("1  setlinewidth\n");
    printf ("0  setgray\n");
    printf ("newpath\n");
    printf ("%d %d 10 sub moveto\n", L_MARGIN, BOTTOM);
    printf ("%d 0 rlineto\n", WIDTH - L_MARGIN * 2);
    printf ("stroke\n");
    if (number_all || song_pages > 1) {
        int pnum = (number_all ? page_label : song_pages);
        set_text_font (text_size - 2);
        use_text_font ();
        if (page_label % 2 == left_foot_odd) {  /* left side */
            printf ("1 inch %d 3 div moveto\n", BOTTOM);
            printf ("(Page %d) show\n", pnum);
        }
        else {                  /* right side */
            printf ("(Page %d) dup stringwidth pop\n", pnum);
            printf ("%d exch sub 1 inch sub %d 3 div moveto\n",
                    WIDTH, BOTTOM);
            printf ("show\n");
        } if (title1_found) {
            printf ("(%s) dup stringwidth pop 2 div\n", title1);
            printf ("%d 2 div exch sub %d 3 div moveto\n",
                    WIDTH, BOTTOM);
            printf ("show\n");
        } set_text_font (text_size);
    } if (in_chorus) {
        end_of_chorus = vpos;
        do_chorus_line ();
    } if (v_pages == pagination) {
        do_end_of_phys_page ();
        v_pages = 0;
    }
}

/* --------------------------------------------------------------------------------*/
do_start_of_page () {
    v_pages++;
    page_label++;

    if (v_pages == 1) {
        n_pages++;
        printf ("%%%%Page: \"%d\" %d\n", n_pages, n_pages);
        printf ("%%%%BeginPageSetup\n");
        printf ("/pgsave save def\n");
        printf ("%f %f scale\n", scale, scale);
        printf ("%f rotate\n", rotation);
        printf ("%%%%EndPageSetup\n");
    } if (pagination == 4) {
        if (v_pages == 1)
            do_translate (0.0, 800.0);
        else if (v_pages == 2)
            do_translate (600.0, 0.0);
        else if (v_pages == 3)
            do_translate (-600.0, -800.0);
        else if (v_pages == 4)
            do_translate (600.0, 0.0);
    } if (pagination == 2) {
        if (v_pages == 1)
            do_translate (-40.0, -800.0);
        else if (v_pages == 2)
            do_translate (500.0, 0.0);
    } vpos = TOP;
    hpos = L_MARGIN;
    song_pages++;
    if (in_chorus) {
        start_of_chorus = vpos;
    }
}

/* --------------------------------------------------------------------------------*/
advance (int amount) {
    vpos = vpos - amount;       /* Affect text positionning ! */
    if (vpos < BOTTOM) {
        do_end_of_page ();
        do_start_of_page ();
    }
}

/* --------------------------------------------------------------------------------*/
print_text_line () {
    int i;

    text_line[i_text] = '\0';

    for (i = 0; text_line[i] == ' '; i++);

    if (!(lyrics_only || auto_space && !has_chord)) {
        advance (blank_space);
        blank_space = 0;
        advance (chord_size + 1);

        if ((text_line[i] != '\0')
            && (vpos - text_size <= BOTTOM))
            advance (text_size);

        if (need_soc) {
            start_of_chorus = vpos + chord_size;
            need_soc = FALSE;
        } print_chord_line ();
    } if (text_line[i] == '\0') {
        blank_space += text_size - 2;
        debug ("blank line found ");
    }
    else {
        advance (blank_space);
        blank_space = 0;
        advance (text_size - 1);
        if (need_soc) {
            start_of_chorus = vpos + text_size;
            need_soc = FALSE;
        } use_text_font ();
        printf ("%d %d moveto\n", hpos, vpos);
        printf ("(%s) show\n", text_line);
    } i_text = 0;
    i_text_ov = FALSE;
    hpos = L_MARGIN;
    has_chord = FALSE;
}

/* --------------------------------------------------------------------------------*/
do_title () {
    char *buf;
    buf = (char *) strtok (NULL, "\0");
    set_text_font (text_size + 5);
    use_text_font ();
    printf ("(%s) dup stringwidth pop 2 div\n", buf);
    printf ("%d 2 div exch sub %d moveto\n", WIDTH, vpos);
    printf ("show\n");
    vpos = vpos - text_size - 5;
    strcpy (title1, buf);
    title1_found = TRUE;
    set_text_font (text_size);
    if (toc_file && song_pages == 1) {  /* generate index entry *//* careful to ignore any \ which may have been inserted */
        char *bp;
        for (bp = buf; *bp; bp++)
            if (*bp != '\\')
                putc (*bp, toc_file);
        fprintf (toc_file, " ... %d\n", page_label);
    }
}

/* --------------------------------------------------------------------------------*/
do_subtitle() {
    char *buf;
    buf = (char *)strtok(NULL, "\0");
    use_text_font();
    printf ("(%s) dup stringwidth pop 2 div\n", buf);
    printf ("%d 2 div exch sub %d moveto\n", WIDTH , vpos);
    printf ("show\n");
    vpos = vpos - text_size ;
}

/* --------------------------------------------------------------------------------*/
put_in_string ( unsigned char array[MAXLINE], int *p_index, unsigned char c,
                int max_index, int *p_ov_flag) {
    if (*p_index < max_index) {
        if ((int) c < 128) {
            array[(*p_index)++] = c;
        }
        else {
            array[(*p_index)++] = '\\';
            sprintf ((char *) &array[*p_index], "%o", c);
            (*p_index) += 3;
        }
    }
    else {
        if (!*p_ov_flag) {
            error ("Buffer Overflow");
            *p_ov_flag = TRUE;
        }
    }
}

/* --------------------------------------------------------------------------------*/
process_file (FILE * source_fd) {
    debug ("start of process_file");

    n_lines = 0;

    while ((c = getc (source_fd)) != EOF) {
        i_input++;
        switch ((char) c) {

        case '[':
            if (in_chord)
                error ("Opening a chord within a chord!");
            else
                in_chord = TRUE;
            i_chord = 0;
            break;

        case ']':
            if (in_chord) {
                in_chord = FALSE;
                chord[i_chord] = '\0';
                do_chord (i_text, chord);
                has_chord = TRUE;
                i_chord = 0;
                i_chord_ov = FALSE;
            }
            else
                error ("']' found with no matching '['");
            break;

        case '{':
            in_directive = TRUE;
            i_directive = 0;
            has_directive = TRUE;
            break;

        case '}':
            if (in_directive) {
                in_directive = FALSE;
                directive[i_directive] = '\0';
                for (; (c = getc (source_fd)) != '\n';);
                i_input = 0;
                do_directive (directive);
                has_directive = FALSE;
                n_lines++;
                i_directive = 0;
                i_directive_ov = FALSE;
            }
            else
                error ("'}' found with no matching '{'");
            break;

        case '\n':
            if (in_directive)
                error ("Line ends while in a directive !");
            if (in_chord)
                error ("Line ends while in a chord !");
            if (has_directive == FALSE) {
                if (in_chordrc)
                    error ("line is NOT a directive");
                else {
                    print_text_line ();
                }
            }
            else
                has_directive = FALSE;
            n_lines++;
            i_input = 0;
            in_directive = FALSE;
            in_chord = FALSE;
            break;

        case '(':
        case ')':
            if (in_directive) {
                put_in_string (directive, &i_directive, '\\', MAXTOKEN, &i_directive_ov);
                put_in_string (directive, &i_directive, c, MAXTOKEN, &i_directive_ov);
                break;
            }
            else if (in_chord) {        /* allow parens in chord names */
                put_in_string (chord, &i_chord, '\\', MAXLINE, &i_text_ov);
                put_in_string (chord, &i_chord, c, MAXLINE, &i_text_ov);
                break;
            }
            else {
                put_in_string (text_line, &i_text, '\\', MAXLINE, &i_text_ov);
                put_in_string (text_line, &i_text, c, MAXLINE, &i_text_ov);
                break;
        } /* This case HAS to be the last before the default statement !!! */ case '#':
            if (i_input == 1) {
                for (; (c = getc (source_fd)) != '\n';);
                n_lines++;
                i_input = 0;
                break;
        } default:
            if (in_chord) {
                if (c != ' ')
                    put_in_string (chord, &i_chord, c, MAXTOKEN, &i_text_ov);
            }
            else if (in_directive) {
                put_in_string (directive, &i_directive, c, MAXTOKEN, &i_directive_ov);
            }
            else {
                put_in_string (text_line, &i_text, c, MAXLINE, &i_text_ov);
            } break;
        }
    } print_text_line ();
}

/* --------------------------------------------------------------------------------*/
read_input_file () {
    current_file = source;
    source_fd = fopen (source, "r");
    if (source_fd == NULL) {
        fprintf (stderr, "Unable to open [%s]\n", source);
        exit (1);
    } process_file (source_fd);
    fclose (source_fd);
}

/* --------------------------------------------------------------------------------*/
read_chordrc () {
    char chordrc[MAXTOKEN];
    FILE *chordrc_fd;
    int n_lines_save;

    strcpy (chordrc, "s:.chordrc");
    current_file = chordrc;
    chordrc_fd = fopen (chordrc, "r");
    if (chordrc_fd != NULL) {
        n_lines_save = n_lines;
        n_lines = 1;
        in_chordrc = TRUE;
        process_file (chordrc_fd);
        in_chordrc = FALSE;
        n_lines = n_lines_save;
        fclose (chordrc_fd);
    } current_file = source;
}

/* --------------------------------------------------------------------------------*/
debug(char *dbg_str) {
    if (debug_mode)
        fprintf (stderr, "Debug: %s\n", dbg_str);
}

/* --------------------------------------------------------------------------------*/
print_version() {
    char *version = VERSION;
    char *patch_level = PATCH_LEVEL;
        
    printf ("chord version %s, patchlevel %s\n", version, patch_level);
}
/* --------------------------------------------------------------------------------*/
error(char *err_str) {
    fprintf(stderr, "WARNING: %s\n",err_str);
    fprintf(stderr, "         in file %s, line %d\n", current_file,n_lines);
}

/* --------------------------------------------------------------------------------*/
main (int argc, char **argv)
{
    int c, i;

    while ((c = getopt (argc, argv, "hlDd24Vc:t:g:C:T:x:aLRp:i:")) != -1)
        switch (c) {

        case 'h':
            do_help (argv[0]);
            exit (1);
            break;

        case 'D':
            debug_mode = TRUE;
            break;

        case 'd':
            init_known_chords ();
            dump_chords ();
            exit (0);

        case 'c':
            i = atoi (optarg);
            if (i == 0)
                error ("invalid value for chord_size");
            else
                chord_size = i;
            break;

        case 't':
            i = atoi (optarg);
            if (i == 0)
                error ("invalid value for text_size");
            else
                text_size = i;
            break;

        case 'x':
            i = atoi (optarg);
            if (i == 0)
                error ("invalid value for transposition");
            else
                transpose = i;
            break;

        case 'T':
            text_font = optarg;
            break;

        case 'C':
            chord_font = optarg;
            break;

        case 'g':
            i = atoi (optarg);
            if (i == 0)
                error ("invalid value for grid_size");
            else
                grid_size = i;
            break;

        case 'l':
            lyrics_only = TRUE;
            break;

        case 'V':
            print_version ();
            exit (0);

        case '2':
            pagination = 2;
            scale = scale2;
            rotation = 90.0;
            break;

        case '4':
            pagination = 4;
            scale = scale4;
            break;

        case 'i':
            if (!strcmp (optarg, "-"))
                toc_file = stderr;
            else
                toc_file = fopen (optarg, "w");
            if (toc_file == NULL) {
                perror (optarg);
                error ("unable to create index file");
            } break;

        case 'a':
            auto_space = TRUE;
            break;

        case 'p':
            page_label = atoi (optarg);
            number_all = TRUE;
            break;

        case 'L':
            left_foot_odd = 1;
            break;

        case 'R':
            left_foot_odd = 0;
            break;

        case '?':
            do_help (argv[0]);
            break;
        } /* File Processing */ init_ps ();

    read_chordrc ();

    chord_inc = chord_size * 1.5;

    if (optind == argc) {
        debug ("Reading stdin");
        strcpy (source, "stdin");
        process_file (stdin);
    }
    else {
        for (; optind < argc; optind++) {
            debug ("Processing a file");
            strcpy (source, argv[optind]);
            read_input_file ();
            if (optind < argc - 1)
                do_new_song ();
        }
    } do_end_of_song ();
    if (v_pages != 0) {
        do_end_of_phys_page ();
    } printf ("%%%%Trailer\n");
    printf ("%%%%Pages: %d 1\n", n_pages);
    printf ("%%%%EOF\n");
    if (toc_file)
        fclose (toc_file);
    exit (0);
    return (0);
}
