/* Copyright (C) 1999 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: genht.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */
/* Generate C code for compiling halftones into ROM. */
#include "malloc_.h"
#include "stdio_.h"
#include "string_.h"
#include "gscdefs.h"
#include "gsmemory.h"
#include "gxbitmap.h"
#include "gxhttile.h"
#include "gxtmap.h"
#include "gxdht.h"
#include "gxdhtres.h"
#include "strimpl.h"
#include "sstring.h"

/*
 * This program translates PostScript halftone resources into C data
 * structures that can then be compiled into executables and put in shared,
 * read-only memory.  The syntax of the resource file is a subset of
 * PostScript, tightly constrained so that it can be parsed easily.  Blank
 * lines and PostScript comments are ignored, but otherwise each halftone
 * in the file (there may be more than one) must follow this format,
 * where ... indicates arbitrary text:
    (optionally:)
	/halftone_name ...
	/HalftoneType 5
    (zero or more times:)
	/halftone_name ...
	/HalftoneType 3
	/Width xxx
	/Height xxx
	/Thresholds ...
	--hex data terminated by a >--
	(zero or more times:)
	    /halftone_name 1 index
    (finally)
    ... defineresource
 * Lines that don't follow the above syntax may appear anywhere in the file
 * except in the middle of the hex data.  Width and Height must precede
 * Thresholds, but otherwise the 4 parameters may appear in any order.
 * White space at the beginning or end of a line is ignored, and any
 * amount of white space may separate the parameter name from its value.
 *
 * We envision that this format will eventually be extended to cover
 * HalftoneType 16 halftones with a single rectangle, allowing 16-bit
 * threshold values.
 */

/* Read a source file into memory. */
private char *
read_file(FILE *in, char *cname)
{
    int len, nread;
    char *cont;

    fseek(in, 0L, 2 /*SEEK_END*/);
    len = ftell(in);
    cont = malloc(len + 1);
    if (cont == 0) {
	fprintf(stderr, "Can't allocate %d bytes to read %s.\n",
		len + 1, cname);
	return 0;
    }
    rewind(in);
    nread = fread(cont, 1, len, in);
    cont[nread] = 0;
    return cont;
}

/* Parse a Halftone resource file into memory. */
private bool
parse_line(char **pstr, char **pline)
{
    char *str = *pstr;

top:
    while (*str && strchr(" \t\r\n", *str)) /* trim leading space */
	++str;
    if (*str == 0) {
	*pline = 0;
	return false;
    }
    *pline = str;
    while (*str && !strchr("\r\n", *str)) /* find EOL */
	++str;
    while (str > *pline && strchr(" \t", str[-1])) /* trim trailing space */
	--str;
    *str = 0;
    *pstr = str + 1;
    return true;
}
int
parse_halftone(gx_device_halftone_resource_t *phtr, byte **pThresholds,
	       char **pprefix, char **pcont)
{
    char *str;
    char *line;
    char *rname = 0;
    int HalftoneType = -1;
    int Width = -1, Height = -1;
    byte *Thresholds = 0;
    stream_AXD_state ss;

    /* Parse the file. */
    for (str = *pcont; parse_line(&str, &line);) {
	char *end;
	char *prefix;
	char terminator;

	if (line[0] == '%')
	    continue;
	if (strlen(line) >= 14 &&
	    !strcmp(line + strlen(line) - 14, "defineresource")
	    )
	    break;
	if (line[0] != '/')
	    continue;
	if (strlen(line) >= 8 &&
	    !strcmp(line + strlen(line) - 8, " 1 index")
	    )
	    continue;
	end = ++line;
	while (*end && !strchr(" \t<", *end)) /* find end of name */
	    ++end;
	terminator = *end;
	*end = 0;
	if (rname == 0) { /* first name is halftone name */
	    rname = malloc(strlen(line) + 1);
	    strcpy(rname, line);
	    continue;
	}
	if (terminator == 0)		/* name alone */
	    continue;
	++end;
	if (!strcmp(line, "HalftoneType")) {
	    if (sscanf(end, "%d", &HalftoneType) != 1) {
		fprintf(stderr, "Invalid HalftoneType syntax: %s\n", line - 1);
		return -1;
	    }
	    switch (HalftoneType) {
	    case 3:
		break;
	    case 5:
		if (*pprefix)
		    free(*pprefix);
		*pprefix = rname;
		rname = 0;
		break;
	    default:
		fprintf(stderr, "Invalid HalftoneType: %s\n", end);
		return -1;
	    }
	    continue;
	} else if (!strcmp(line, "Width")) {
	    if (sscanf(end, "%d", &Width) != 1 ||
		Width <= 0 || Width > 0x4000
		) {
		fprintf(stderr, "Invalid Width: %s\n", end);
		return -1;
	    }
	} else if (!strcmp(line, "Height")) {
	    if (sscanf(end, "%d", &Height) != 1 ||
		Height <= 0 || Height > 0x4000
		) {
		fprintf(stderr, "Invalid Height: %s\n", end);
		return -1;
	    }
	} else if (!strcmp(line, "Thresholds")) {
	    uint ignore;
	    uint num_levels = 256;
	    uint num_bits = Width * Height;
	    char *eol = end + strlen(end); /* skip rest of line */
	    stream_cursor_read r;
	    stream_cursor_write w;

	    if (Width < 0 || Height < 0) {
		fprintf(stderr, "Width and Height must precede Thresholds.\n");
		return -1;
	    }
	    phtr->num_levels = num_levels;
	    phtr->levels =
		malloc(num_levels * sizeof(*phtr->levels));
	    phtr->bit_data =
		malloc(num_bits * sizeof(ushort));
	    Thresholds = malloc(num_bits);
	    s_AXD_init_inline(&ss);
	    r.ptr = (const byte *)eol;
	    r.limit = (const byte *)eol + strlen(eol + 1);
	    w.ptr = Thresholds - 1;
	    w.limit = w.ptr + num_bits;
	    s_AXD_template.process((stream_state *)&ss, &r, &w, true);
	    str = (char *)r.ptr + 1;
	    break;
	}
    }

    /* Check for successful parsing. */
    if (rname == 0)
	return 1;		/* end of definitions */
    if (HalftoneType < 0)
	fprintf(stderr, "HalftoneType not found.\n");
    if (Width < 0)
	fprintf(stderr, "Width not found.\n");
    if (Height < 0)
	fprintf(stderr, "Height not found.\n");
    if (Thresholds == 0)
	fprintf(stderr, "Thresholds not found.\n");
    if (rname == 0 || Thresholds == 0)
	return -1;
    phtr->rname = rname;
    phtr->HalftoneType = HalftoneType;
    phtr->Width = Width;
    phtr->Height = Height;
    *pThresholds = Thresholds;
    *pcont = str;
    return 0;
}

/* Write a halftone as a C procedure. */
int
write_halftone(FILE *out, gx_device_halftone_resource_t *phtr,
	       const char *prefix, int index)
{
    int num_bits = phtr->Width * phtr->Height;
    int i;

    /* Write the initial comment. */
    fputs("\n/* ", out);
    if (prefix)
	fprintf(out, "%s.", prefix);
    fprintf(out, "%s */\n", phtr->rname);

    /* Write the levels array. */
    fprintf(out, "static const unsigned int levels_%d[] = {", index);
    for (i = 0; i < phtr->num_levels; ++i) {
	if (i % 10 == 0)
	    fputs("\n", out);
	fprintf(out, "%5u,", phtr->levels[i]);
    }
    fputs("\n0};\n", out);

    /* Write the bit_data array. */
    fprintf(out, "static const unsigned short bit_data_%d[] = {", index);
    for (i = 0; i < num_bits; ++i) {
	if (i % 10 == 0)
	    fputs("\n", out);
	fprintf(out, "%5u,", ((const ushort *)phtr->bit_data)[i]);
    }
    fputs("\n0};\n", out);

    /* Write the top-level structure. */
    fprintf(out, "static const gx_device_halftone_resource_t res_%d = {\n    \"%s\", %d, %d, %d, %d, levels_%d, bit_data_%d, %u\n};\n",
	    index, phtr->rname, phtr->HalftoneType, phtr->Width, phtr->Height,
	    phtr->num_levels, index, index,
	    ht_order_procs_short.bit_data_elt_size);
}

/* Main program */
main(int argc, char *argv[])
{
    char *iname;
    FILE *in;
    char *oname;
    FILE *out;
    int code;
    char *cont;
    char *line;
    gx_device_halftone_resource_t res;
    char *prefix = 0;
    byte *Thresholds;
    gx_ht_order order;
    int index, i;

    if (argc != 3) {
	fprintf(stderr, "Usage: genht ht_res.ps ht_data.c\n");
	exit(1);
    }
    iname = argv[1];
    oname = argv[2];
    in = fopen(iname, "rb");
    if (in == 0) {
	in = fopen(iname, "r");
	if (in == 0) {
	    fprintf(stderr, "Can't read %s.\n", iname);
	    exit(1);
	}
    }
    cont = read_file(in, iname);
    if (cont == 0)
	exit(1);
    fclose(in);
    out = fopen(oname, "w");
    if (out == 0) {
	fprintf(stderr, "Can't open %s for output.\n", oname);
	exit(1);
    }
    fprintf(out, "/*\n * This file %s was generated from %s by genht.\n * Do not edit this file.\n *\n", oname, iname);
    /* Copy initial comments from the input file. */
    while (parse_line(&cont, &line) && line[0] == '%')
	if (line[1] != '!')
	    fprintf(out, " * %s\n", line + 1);
    cont[-1] = '\n';
    cont = line;
    fputs(" */\n#include \"gxdhtres.h\"\n", out);
    for (index = 0;
	 (code = parse_halftone(&res, &Thresholds, &prefix, &cont)) == 0;
	 ++index) {
	order.width = res.Width;
	order.num_levels = res.num_levels;
	order.levels = (uint *)res.levels;
	order.num_bits = res.Width * res.Height;
	order.bit_data = (void *)res.bit_data;
	ht_order_procs_short.construct_order(&order, Thresholds);
	write_halftone(out, &res, prefix, index);
    }
    if (prefix == 0)
	prefix = res.rname;
    fputs("/* Check the prototype. */\n", out);
    fprintf(out, "DEVICE_HALFTONE_RESOURCE_PROC(gs_dht_%s);\n", prefix);
    fputs("\nconst gx_device_halftone_resource_t *const *\n", out);
    fprintf(out, "gs_dht_%s(void)\n{\n    static const gx_device_halftone_resource_t *const res[] = {\n\t",
	    prefix);
    for (i = 0; i < index; ++i)
	fprintf(out, "&res_%d, ", i);
    fputs("0\n    };\n    return res;\n}\n", out);
    fclose(out);
    if (code < 0)
	exit(1);
    return 0;
}

/* Stubs */
ENUM_PTRS_BEGIN_PROC(gs_no_struct_enum_ptrs)
{
    return 0;
    ENUM_PTRS_END_PROC
}
RELOC_PTRS_BEGIN(gs_no_struct_reloc_ptrs)
{
}
RELOC_PTRS_END
public_st_stream_state();
void
gx_ht_complete_threshold_order(gx_ht_order *porder)
{
}

/*
 * In order to avoid a linking step, we #include the required files here
 * rather than compiling them separately.
 */
#include "gxhtbit.c"
#include "scantab.c"
#include "sstring.c"

const gx_dht_proc gx_device_halftone_list[] = { 0 };
