/*
 * Start up graphics processing.  Anything that needs to be assigned, set up,
 * started-up, or otherwise initialized happens here.  This is called only at
 * the startup of the graphics driver.
 *
 * The external variables define the pixle limits of the graphics surface.  The
 * coordinate system used by the applications programs has the (0,0) origin
 * in the upper left-hand corner.  Hence,
 *    screen_left < screen_right
 *    screen_top  < screen_bottom
 */

#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#include <unistd.h>

#include <grass/gis.h>
#include <grass/glocale.h>
#include "psdriver.h"

#define DATE_FORMAT "%c"

struct ps_state ps;

static double width, height;
static int landscape;

struct paper {
    const char *name;
    double width, height;
    double left, right, bot, top;
};

static const struct paper papers[] = {
    /* name         width   height  left    right   bottom  top */
    {"a4", 8.268, 11.693, 0.5, 0.5, 1.0, 1.0},
    {"a3", 11.693, 16.535, 0.5, 0.5, 1.0, 1.0},
    {"a2", 16.54, 23.39, 1.0, 1.0, 1.0, 1.0},
    {"a1", 23.39, 33.07, 1.0, 1.0, 1.0, 1.0},
    {"a0", 33.07, 46.77, 1.0, 1.0, 1.0, 1.0},
    {"us-legal", 8.5, 14.0, 1.0, 1.0, 1.0, 1.0},
    {"us-letter", 8.5, 11.0, 1.0, 1.0, 1.0, 1.0},
    {"us-tabloid", 11.0, 17.0, 1.0, 1.0, 1.0, 1.0},
    {NULL, 0, 0, 0, 0, 0, 0}};

static void write_prolog(void)
{
    char prolog_file[GPATH_MAX];
    char date_str[256];
    FILE *prolog_fp;
    time_t t = time(NULL);
    struct tm *tm = localtime(&t);

    strftime(date_str, sizeof(date_str), DATE_FORMAT, tm);

    sprintf(prolog_file, "%s/etc/psdriver.ps", G_gisbase());

    prolog_fp = fopen(prolog_file, "r");
    if (!prolog_fp)
        G_fatal_error("Unable to open prolog file");

    if (ps.encapsulated)
        output("%%!PS-Adobe-3.0 EPSF-3.0\n");
    else
        output("%%!PS-Adobe-3.0\n");

    output("%%%%LanguageLevel: %d\n", 3);
    output("%%%%Creator: GRASS PS Driver\n");
    output("%%%%Title: %s\n", ps.outfile);
    output("%%%%For: %s\n", G_whoami());
    output("%%%%Orientation: %s\n", landscape ? "Landscape" : "Portrait");
    output("%%%%BoundingBox: %d %d %d %d\n", (int)floor(ps.left),
           (int)floor(ps.bot), (int)ceil(ps.right), (int)ceil(ps.top));
    output("%%%%CreationDate: %s\n", date_str);
    output("%%%%EndComments\n");

    output("%%%%BeginProlog\n");
    while (!feof(prolog_fp)) {
        char buf[256];

        if (!fgets(buf, sizeof(buf), prolog_fp))
            break;

        fputs(buf, ps.tempfp);
    }
    output("%%%%EndProlog\n");

    fclose(prolog_fp);
}

void write_setup(void)
{
    output("%%%%BeginSetup\n");

    output("%.1f %.1f translate\n", ps.left, ps.bot);

    if (landscape)
        output("90 rotate 0 1 -1 scale\n");
    else
        output("0 %.1f translate 1 -1 scale\n", height);

    output("%.1f %.1f BEGIN\n", width, height);

    output("%%%%EndSetup\n");
    output("%%%%Page: 1 1\n");
}

static double in2pt(double x)
{
    return x * 72;
}

static void swap(double *x, double *y)
{
    double tmp = *x;

    *x = *y;
    *y = tmp;
}

static void get_paper(void)
{
    const char *name = getenv("GRASS_RENDER_PS_PAPER");
    const struct paper *paper;
    int i;

    width = screen_width;
    height = screen_height;

    ps.left = 0;
    ps.right = width;
    ps.bot = 0;
    ps.top = height;

    if (landscape)
        swap(&ps.right, &ps.top);

    if (!name)
        return;

    for (i = 0;; i++) {
        paper = &papers[i];

        if (!paper->name)
            return;

        if (G_strcasecmp(name, paper->name) == 0)
            break;
    }

    ps.left = in2pt(paper->left);
    ps.right = in2pt(paper->width) - in2pt(paper->right);
    ps.bot = in2pt(paper->bot);
    ps.top = in2pt(paper->height) - in2pt(paper->top);

    width = ps.right - ps.left;
    height = in2pt(paper->height) - in2pt(paper->top) - in2pt(paper->bot);

    if (landscape)
        swap(&width, &height);

    ps.right = ps.left + width;
    ps.bot = ps.top + height;
}

int PS_Graph_set(void)
{
    const char *p;

    G_gisinit("PS driver");

    p = getenv("GRASS_RENDER_FILE");
    if (!p || strlen(p) == 0)
        p = FILE_NAME;

    ps.outfile = p;
    p = ps.outfile + strlen(ps.outfile) - 4;
    ps.encapsulated = (G_strcasecmp(p, ".eps") == 0);

    p = getenv("GRASS_RENDER_TRUECOLOR");
    ps.true_color = p && strcmp(p, "TRUE") == 0;

    p = getenv("GRASS_RENDER_PS_LANDSCAPE");
    landscape = p && strcmp(p, "TRUE") == 0;

    p = getenv("GRASS_RENDER_PS_HEADER");
    ps.no_header = p && strcmp(p, "FALSE") == 0;

    p = getenv("GRASS_RENDER_PS_TRAILER");
    ps.no_trailer = p && strcmp(p, "FALSE") == 0;

    G_verbose_message(_("ps: truecolor status %s"),
                      ps.true_color ? _("enabled") : _("disabled"));

    get_paper();

    ps.tempfile = G_tempfile();
    if (ps.no_header && access(ps.outfile, F_OK) == 0)
        G_rename_file(ps.outfile, ps.tempfile);

    ps.tempfp = fopen(ps.tempfile, ps.no_header ? "a" : "w");

    if (!ps.tempfp)
        G_fatal_error("Unable to open output file: %s", ps.outfile);

    if (!ps.no_header) {
        write_prolog();
        write_setup();
    }

    G_verbose_message(_("ps: collecting to file '%s'"), ps.outfile);
    G_verbose_message(_("ps: image size %dx%d"), screen_width, screen_height);

    fflush(ps.tempfp);

    return 0;
}

/*!
   \brief Get render file

   \return file name
 */
const char *PS_Graph_get_file(void)
{
    return ps.outfile;
}

void output(const char *fmt, ...)
{
    va_list va;

    va_start(va, fmt);
    vfprintf(ps.tempfp, fmt, va);
    va_end(va);
}
