/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <mars.h>
#include <stdio.h>
#include <time.h>
/* #ifndef hp */
/* #include <getopt.h> */
/* #endif */
#include <sys/stat.h>

static int beau = 0; /* Beautify of ps_ output */
static int margin = 25;
static int size = 9;
static int count = 0;
static int tab = 4;
static int landscape = 0;
static int twopages = 0;

static int braket = 0;
static int ketbra = 0;
static int force = 0;

#define MAXBUF 1024

static char buffer[MAXBUF];
static char word[MAXBUF];
static int bufidx = 0;

static void ps_setfont(char);
static void ps_outchar(char);
static void ps_outtext(const char*);
static void ps_newline(void);
static void ps_newpage(void);
static void ps_space(const char*);
static void ps_flush(void);
static void ps_process(FILE*, const char*);

#define ROMAN ps_setfont('R')
#define BOLD ps_setfont('B')
#define ITALIC ps_setfont('I')


void keyword(const char*);

static char* ps[] = {
#include "beau.h"
};

static char* ps_bool[] = {
    "false",
    "true",
};

void ps_outtext(const char* p)
{
    int len = strlen(p);
    if (len + bufidx + 1 >= MAXBUF)
        ps_flush();

    strcpy(buffer + bufidx, p);
    bufidx += len;
    count += len;
}

/* Select a new font */

void ps_setfont(char f)
{
    static char last_font = 0;
    if (last_font != f) {
        ps_flush();
        last_font = f;
        printf("%c\n", f);
    }
}

void leading(const char* p)
{
    if (!beau)
        ps_space(p);
}

/* Output a string */

void ps_string(const char* p)
{
    putchar('(');
    while (*p) {
        switch (*p) {
            case '(':
            case ')':
            case '\\':
                putchar('\\');
                putchar(*p);
                break;

            default:
                if (isprint(*p))
                    putchar(*p);
                else
                    printf("\\%03o", *p);
                break;
        }
        p++;
    }
    putchar(')');
}

void ps_flush(void)
{
    if (!bufidx)
        return;
    ps_string(buffer);
    printf("S\n");
    *buffer = 0;
    bufidx = 0;
}

void ps_space(const char* w)
{
    int n = count / tab * tab + tab - count;
    int i;

    while (*w) {
        if (*w == '\t')
            for (i = 0; i < n; i++)
                ps_outchar(' ');
        else
            ps_outchar(' ');
        w++;
    }
}

void ps_outchar(char c)
{
    char buf[2];
    buf[0] = c;
    buf[1] = 0;
    ps_outtext(buf);
}

void ps_icon(const char* icon)
{
    printf("100 50 9 9 290 365 321 396\n");
    printf("start_icon\n");
    fflush(NULL);
    system("cat /cc/od/graphics/icons/MACRO.ps");
    printf("end_icon\n");
}

void ps_header(void)
{
    int i;
    for (i = 0; i < sizeof(ps) / sizeof(char*); i++)
        printf("%s\n", ps[i]);
    printf("%d %d %s %s IJ\n", margin, size, ps_bool[landscape], ps_bool[twopages]);
    /* ps_icon("MACRO"); */
}

void ps_newpage(void)
{
    ps_flush();
    count = 0;
    printf("NP\n");
    ROMAN;
}

void ps_newline(void)
{
    ps_flush();
    count = 0;
    printf("L\n");
}

void newpage(void)
{
    if (!beau)
        ps_newpage();
}


void process(FILE* f, const char* name)
{
    extern FILE* zzin;
    struct stat s;

    count = 0;
    ROMAN;
    zzin = f;

    if (fstat(fileno(f), &s) < 0)
        time(&s.st_mtime);

    ps_string(name);
    ps_string(ctime(&s.st_mtime));
    printf("NF\n");
    while (zzlex())
        ;
}

zzwrap()
{
    if (!beau) {
        count = 0;
        printf("EF\n");
        ROMAN;
    }
    return 1;
}
/*===============================================================*/


void dumpline()
{
    int i;
    char *p, *q;
    p = buffer;

    if (!*p)
        return;

    for (i = 0; i < tab; i++)
        putchar('\t');

    q = p + strlen(p) - 1;
    while (q != p && (*q == ' ' || *q == '\t'))
        *q-- = 0;
    printf("%s\n", p);
    strcpy(buffer, word);
    *word = 0;
}

void tabs(int n)
{
    if (beau)
        tab += n;
}


void newline(int sync)
{
    if (beau) {
        force = 1;
        if (sync)
            keyword("");
        dumpline();
    }
}


void keyword(const char* w)
{
    if (beau) {
        strcat(buffer, word);
        strcpy(word, w);
    }
    else {
        BOLD;
        ps_outtext(w);
        ROMAN;
    }
}

void inlineword(const char* w)
{
    char buf[2];
    if (beau) {
        printf("%s", w);
    }
    else
        switch (*w) {
            case '\t':
            case ' ':
                buf[0] = *w;
                buf[1] = 0;
                ps_space(buf);
                break;

            case '\n':
                ps_newline();
                break;

            default:
                ps_outtext(w);
                break;
        }
}

void plainword(const char* w)
{
    if (beau)
        keyword(w);
    else
        ps_outtext(w);
}

void space(const char* w)
{
    if (beau) {
        if (*buffer || *word)
            strcat(word, w);
    }
    else
        ps_space(w);
}

void bsync()
{
    if (beau)
        keyword("");
}

void bra()
{
    bsync();
    braket++;
}
void ket()
{
    bsync();
    braket--;
}

void cr(const char* w)
{
    if (beau) {
        bsync();
        if (*buffer) {
            newline(1);
            if (braket != ketbra) {
                tabs((braket - ketbra) * 2);
                ketbra = braket;
            }
        }
        else if (!force)
            putchar('\n');
        force = 0;
    }
    else
        ps_newline();
}

void comment(const char* w)
{
    if (beau) {
        keyword(w);
        cr("\n");
    }
    else {
        ITALIC;
        ps_outtext(w);
        ps_newline();
        ROMAN;
    }
}

void zzerror(const char* msg)
{
    extern int zzlineno;
    fprintf(stderr, "Line %d: %s\n", zzlineno, msg);
    exit(1);
}

void theend()
{
    if (*word || *buffer)
        newline(0);
}

void usage(const char* me)
{
    fprintf(stderr,
            "usage: %s -[+2l] -m margin -s size -t tabs [filename [...]] \n", me);
    fprintf(stderr, "          -+          : c++ mode\n");
    fprintf(stderr, "          -2          : two columns\n");
    fprintf(stderr, "          -l          : landscape\n");
    fprintf(stderr, "          -m margin   : margin size (default 25 points)\n");
    fprintf(stderr, "          -s font     : font size   (default 9 points)\n");
    fprintf(stderr, "          -t tabs     : tab size    (default 4 spaces)\n");
    exit(1);
}

#ifdef YYDEBUG
extern
#endif
    int zzdebug;

void main(int argc, char** argv)
{
    int c;
    char* name = NULL;
    *buffer = *word = 0;

    /* zzdebug = 1; */

    while ((c = getopt(argc, argv, "dbz:+m:s:l2t:i:n:f:g:")) != -1) {
        switch (c) {
            case 'd':
                zzdebug = 1; /* need to define YYDEBUG, also for beauy.c */
                break;

            case 'b':
                beau = 1;
                tab = 0;
                break;

            case '2':
                twopages = 1;
                break;

            case 'l':
                landscape = 1;
                break;

            case 'n':
                name = optarg;
                break;

            case 'm':
                margin = atoi(optarg);
                if (margin < 2)
                    margin = 2;
                break;

            case 's':
                size = atoi(optarg);
                if (size < 2)
                    size = 2;
                break;

            case 't':
                tab = atoi(optarg);
                if (tab < 2)
                    tab = 2;
                break;

            case 'f':
                freopen(optarg, "r", stdin);
                break;

            case 'g':
                freopen(optarg, "w", stdout);
                break;

            case '?':
                usage(argv[0]);
                break;
        }
    }

    if (beau) {
        beau_parse();
    }
    else {
        ps_header();

        if (optind == argc)
            process(stdin, name ? name : "stdin");
        else
            for (; optind < argc; optind++) {
                FILE* f = fopen(argv[optind], "r");
                if (!f) {
                    perror(argv[optind]);
                    exit(1);
                }
                process(f, mbasename(name ? name : argv[optind]));
                fclose(f);
            }
    }

    exit(0);
}
