/* Copyright (C) 1992, 1994, 1998, 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: scfdgen.c,v 1.2.6.1.2.1 2003/01/17 00:49:05 giles Exp $ */
/* Generate the CCITTFaxDecode tables */
#include "stdio_.h"		/* includes std.h */
#include "scf.h"
#include "malloc_.h"
#include "memory_.h"

typedef void (*cfd_node_proc) (P6(cfd_node *, cfd_node *,
				  uint, int, int, int));
typedef void (*cfd_enum_proc) (P4(cfd_node_proc,
				  cfd_node *, cfd_node *, int));
private void cfd_build_tree(P4(cfd_node *, cfd_enum_proc, int, FILE *));
private void cfd_enumerate_white(P4(cfd_node_proc,
				    cfd_node *, cfd_node *, int));
private void cfd_enumerate_black(P4(cfd_node_proc,
				    cfd_node *, cfd_node *, int));
private void cfd_enumerate_2d(P4(cfd_node_proc,
				 cfd_node *, cfd_node *, int));
private void cfd_enumerate_uncompressed(P4(cfd_node_proc,
					   cfd_node *, cfd_node *, int));

main()
{
    FILE *out = fopen("scfdtab.c", "w");
    cfd_node area[1 << max(cfd_white_initial_bits, cfd_black_initial_bits)];

    fputs("/* Copyright (C) 1992, 1993, 1998, 1999 Aladdin Enterprises.  All rights reserved. */\n\n", out);
    fputs("/* $Id: scfdgen.c,v 1.2.6.1.2.1 2003/01/17 00:49:05 giles Exp $ */\n", out);
    fputs("/* Tables for CCITTFaxDecode filter. */\n\n", out);
    fputs("/* This file was generated automatically.  It is governed by the same terms */\n", out);
    fputs("/* as the files scfetab.c and scfdgen.c from which it was derived. */\n", out);
    fputs("/* Consult those files for the licensing terms and conditions. */\n\n", out);
    fputs("#include \"std.h\"\n", out);
    fputs("#include \"scommon.h\"\t\t/* for scf.h */\n", out);
    fputs("#include \"scf.h\"\n\n", out);
    fputs("/* White decoding table. */\n", out);
    fputs("const cfd_node cf_white_decode[] = {\n", out);
    cfd_build_tree(area, cfd_enumerate_white, cfd_white_initial_bits, out);
    fputs("\n};\n\n", out);
    fputs("/* Black decoding table. */\n", out);
    fputs("const cfd_node cf_black_decode[] = {\n", out);
    cfd_build_tree(area, cfd_enumerate_black, cfd_black_initial_bits, out);
    fputs("\n};\n\n", out);
    fputs("/* 2-D decoding table. */\n", out);
    fputs("const cfd_node cf_2d_decode[] = {\n", out);
    cfd_build_tree(area, cfd_enumerate_2d, cfd_2d_initial_bits, out);
    fputs("\n};\n\n", out);
    fputs("/* Uncompresssed decoding table. */\n", out);
    fputs("const cfd_node cf_uncompressed_decode[] = {\n", out);
    cfd_build_tree(area, cfd_enumerate_uncompressed, cfd_uncompressed_initial_bits, out);
    fputs("\n};\n\n", out);
    fputs("/* Dummy executable code to pacify compilers. */\n", out);
    fputs("void scfdtab_dummy(P0());\n", out);
    fputs("void\nscfdtab_dummy()\n{\n}\n", out);
    fclose(out);
    return 0;
}

/* Initialize first-level leaves, count second-level nodes. */
private void
cfd_count_nodes(cfd_node * tree, cfd_node * ignore_extn,
		uint code, int code_length, int run_length, int initial_bits)
{
    if (code_length <= initial_bits) {
	/* Initialize one or more first-level leaves. */
	int sh = initial_bits - code_length;
	cfd_node *np = &tree[code << sh];
	int i;

	for (i = 1 << sh; i > 0; i--, np++)
	    np->run_length = run_length,
		np->code_length = code_length;
    } else {
	/* Note the need for a second level. */
	cfd_node *np = &tree[code >> (code_length - initial_bits)];

	np->code_length = max(np->code_length, code_length);
    }
}

/* Initialize second-level nodes. */
private void
cfd_init2_nodes(cfd_node * tree, cfd_node * extn,
		uint code, int code_length, int run_length, int initial_bits)
{
    int xbits = code_length - initial_bits;
    int xrep;
    cfd_node *np1, *np2;
    int i;

    if (xbits <= 0)
	return;
    np1 = &tree[code >> xbits];
    np2 = &extn[np1->run_length - (1 << initial_bits)];
    xrep = np1->code_length - code_length;
    i = 1 << xrep;
    np2 += (code & ((1 << xbits) - 1)) * i;
    for (; i > 0; i--, np2++)
	np2->run_length = run_length,
	    np2->code_length = xbits;
}

/* Enumerate all the relevant white or black codes. */
private void
cfd_enumerate_codes(cfd_node_proc proc, cfd_node * tree, cfd_node * extn,
		  int initial_bits, const cfe_run * tt, const cfe_run * mut)
{
    int i;
    const cfe_run *ep;

    for (i = 0, ep = tt; i < 64; i++, ep++)
	(*proc) (tree, extn, ep->code, ep->code_length, i, initial_bits);
    for (i = 1, ep = mut + 1; i < 41; i++, ep++)
	(*proc) (tree, extn, ep->code, ep->code_length, i << 6, initial_bits);
    (*proc) (tree, extn,
	     cf1_run_uncompressed.code, cf1_run_uncompressed.code_length,
	     run_uncompressed, initial_bits);
    (*proc) (tree, extn,
	     0, run_eol_code_length - 1,
	     run_zeros, initial_bits);
}
private void
cfd_enumerate_white(cfd_node_proc proc, cfd_node * tree, cfd_node * extn,
		    int initial_bits)
{
    cfd_enumerate_codes(proc, tree, extn, initial_bits,
			cf_white_runs.termination, cf_white_runs.make_up);
}
private void
cfd_enumerate_black(cfd_node_proc proc, cfd_node * tree, cfd_node * extn,
		    int initial_bits)
{
    cfd_enumerate_codes(proc, tree, extn, initial_bits,
			cf_black_runs.termination, cf_black_runs.make_up);
}

/* Enumerate the 2-D codes. */
private void
cfd_enumerate_2d(cfd_node_proc proc, cfd_node * tree, cfd_node * extn,
		 int initial_bits)
{
    int i;
    const cfe_run *ep;

    (*proc) (tree, extn, cf2_run_pass.code, cf2_run_pass.code_length,
	     run2_pass, initial_bits);
    (*proc) (tree, extn, cf2_run_horizontal.code, cf2_run_horizontal.code_length,
	     run2_horizontal, initial_bits);
    for (i = 0; i < countof(cf2_run_vertical); i++) {
	ep = &cf2_run_vertical[i];
	(*proc) (tree, extn, ep->code, ep->code_length, i, initial_bits);
    }
    (*proc) (tree, extn, cf2_run_uncompressed.code, cf2_run_uncompressed.code_length,
	     run_uncompressed, initial_bits);
    (*proc) (tree, extn, 0, run_eol_code_length - 1, run_zeros, initial_bits);
}

/* Enumerate the uncompressed codes. */
private void
cfd_enumerate_uncompressed(cfd_node_proc proc, cfd_node * tree, cfd_node * extn,
			   int initial_bits)
{
    int i;
    const cfe_run *ep;

    for (i = 0; i < countof(cf_uncompressed); i++) {
	ep = &cf_uncompressed[i];
	(*proc) (tree, extn, ep->code, ep->code_length, i, initial_bits);
    }
    for (i = 0; i < countof(cf_uncompressed_exit); i++) {
	ep = &cf_uncompressed_exit[i];
	(*proc) (tree, extn, ep->code, ep->code_length, i, initial_bits);
    }
}

/* Build and write out the table. */
private void
cfd_build_tree(cfd_node * tree, cfd_enum_proc enum_proc, int initial_bits,
	       FILE * f)
{
    cfd_node *np;
    const char *prev = "";
    int i, next;
    cfd_node *extn;

    memset(tree, 0, sizeof(cfd_node) << initial_bits);
    /* Construct and write the first level of the tree. */
    (*enum_proc) (cfd_count_nodes, tree, (cfd_node *) 0, initial_bits);
    next = 0;
    for (i = 0, np = tree; i < 1 << initial_bits; i++, np++) {
	if (np->code_length > initial_bits) {	/* second level needed */
	    np->run_length = next + (1 << initial_bits);
	    next += 1 << (np->code_length - initial_bits);
	}
	fprintf(f, "%s\t{ %d, %d }", prev, np->run_length, np->code_length);
	prev = ",\n";
    }
    /* Construct and write the second level. */
    extn = (cfd_node *) malloc(sizeof(cfd_node) * next);
    for (i = 0, np = extn; i < next; i++, np++)
	np->run_length = run_error,
	    np->code_length = 0;
    (*enum_proc) (cfd_init2_nodes, tree, extn, initial_bits);
    for (i = 0, np = extn; i < next; i++, np++)
	fprintf(f, ",\n\t{ %d, %d }", np->run_length, np->code_length);
    free((char *)extn);
}
