static char *SccsId = "@(#)output.c 4.7 (TU-Delft) 04/16/93";
/**********************************************************

Name/Version      : makegln/4.7

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author            : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1988. All rights reserved.
**********************************************************/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "dmincl.h"
#include "aux/aux.h"
#include "makegln.h"

static int infoTotalInQueue = 0;
static int infoMaxInQueue = 0;
static int infoNowInTemp = 0;
static int infoMaxInTemp = 0;
static int infoTotalOut = 0;
static long infoMaxTempSize = 0;

# ifdef DEBUG_OUTPUT
# define MAX_IN_QUEUE	11
# else	/* not DEBUG_OUTPUT */
# define MAX_IN_QUEUE	20000
# endif	/* not DEBUG_OUTPUT */

static edge_t * head = NULL, *tail = NULL;
static DM_STREAM * outputStream = NULL;
static int  ready_in_queue = 0;
static edge_t * middle = NULL;
static FILE * tmp_fp = NULL;
static char *tmp_name;

static coor_t cursor_x = 0;
static coor_t cursor_y = 0;

extern bool_t optNoOutput;
extern bool_t optCompress;
static char * compressCmd = "compress";
static char * unCompressCmd = "uncompress";

/* local operations */
private void    queueWriteEdge ();
private edge_t *queueReadEdge ();
private void    initQueue ();
private void    flushQueue ();
private long    fileSize ();

openOutput (cellKey, mask)
DM_CELL * cellKey;
char   *mask;
{
    outputStream = dmOpenStream (cellKey, mprintf ("%s_gln", mask), "w");
    initQueue ();
}

closeOutput () {
    flushQueue ();
    dmCloseStream (outputStream, COMPLETE), outputStream = NULL;
}

selectForOutput (edge)
edge_t * edge;
{
    Debug (printEdge ("enque", edge));
    /*
     * Can combine ready and remain in one status field.
     * This can save memory.
     */
    edge -> ready = FALSE;
    edge -> remain = FALSE;
    if (tail) {
	tail -> next = edge;
	tail = edge;
    }
    else  {
	head = tail = edge;
    }
    infoTotalInQueue++;
    infoMaxInQueue = Max (infoMaxInQueue, infoTotalInQueue);
}

readyForOutput (edge)
edge_t * edge;
{
    Debug (printEdge ("ready", edge));
    edge -> ready = TRUE;
    if (edge -> remain == FALSE)
	ready_in_queue++;
}

scanAdvance (thisX)
coor_t thisX;
{
    edge_t * hlp, * hlp_prev;
    edge_t * e;

    Debug (fprintf (stderr, "ScanAdvance start\n"));

    while ((head != NULL) && (head -> remain == FALSE) && 
	   (head -> ready == TRUE)) {
	e = head, head = head -> next;
	Debug (printEdge ("out", e));
	ggln.xl = e -> xl;
	ggln.yl = e -> yl;
	ggln.xr = e -> xr;
	ggln.yr = e -> yr;
	if (!optNoOutput)
	    dmPutDesignData (outputStream, GEO_GLN);
	infoTotalOut++;
	ready_in_queue--;

	disposeEdge (e);
	infoTotalInQueue--;
	middle = head;
    }

    if (middle == NULL)
	middle = head;
    hlp_prev = hlp = middle;

    while (ready_in_queue > MAX_IN_QUEUE) {
	while (hlp && (hlp -> ready == FALSE || hlp -> remain == TRUE)) {
	    hlp -> remain = TRUE;
	    hlp_prev = hlp;
	    hlp = hlp -> next;
	}

	while (hlp && hlp -> ready == TRUE) {
	    if (hlp -> remain == FALSE) {
		queueWriteEdge (tmp_fp, hlp);
		hlp_prev -> next = hlp -> next;
		disposeEdge (hlp);
		infoTotalInQueue--;
		hlp = hlp_prev -> next;

		ready_in_queue--;
	    }
	    else {
		ASSERT (0);
		hlp_prev = hlp;
		hlp = hlp -> next;
	    }
	}
    }

    if (hlp == NULL) 
	tail = hlp_prev;
    middle = hlp_prev;

    if (head == NULL)
	tail = NULL;

    Debug (fprintf (stderr, "ScanAdvance end\n"));
}

/* queueWriteEdge - put an edge in tmp file
 *
 * Write differences instead of absolute coordinates,
 * to save space.
 * Differences are taken accros record boundaries,
 * this works very well because the edges are in scanline order.
 */
private void queueWriteEdge (fp, e)
FILE * fp;
edge_t * e;
{
    e -> xr -= e -> xl;;
    e -> yr -= e -> yl;

    e -> xl -= cursor_x;
    cursor_x += e -> xl;

    if (e -> xl != 0) cursor_y = 0; /* to prevent range overflow */
    e -> yl -= cursor_y;
    cursor_y += e -> yl;

    /* if (_dmPack (fp, "D D D D\n", e -> yl, e -> xr, e -> yr, e -> xl) != 0) { */
    if (pack4D (fp, e -> yl, e -> xr, e -> yr, e -> xl) != 0) {
	say ("tmp file write error, size %ld bytes",
	     (long) (optCompress ? fileSize (tmp_name) : ftell (fp)));
	perror (tmp_name);
	die ();
    }

    infoNowInTemp++;
    infoMaxInTemp = Max (infoMaxInTemp, infoNowInTemp);
}

/* queueReadEdge - get an edge from tmp file.
 * 
 * See the comments under queueWriteEdge for the format.
 * This routine performs an 'inverse' transformation
 * to the coordinates.
 */
private edge_t * queueReadEdge (fp)
FILE * fp;
{
    static edge_t e;

    if (unpack4D (fp, &(e.yl), &(e.xr), &(e.yr), &(e.xl)) == EOF) {
	return (NULL);
    }
    else {
	infoNowInTemp--;

	if (e.xl != 0) cursor_y = 0;
	e.xl += cursor_x;
	e.yl += cursor_y;
	cursor_x = e.xl;
	cursor_y = e.yl;

	e.xr += e.xl;
	e.yr += e.yl;

	if (e.yr == e.yl) e.slope = 0;
	else if (e.yr - e.yl == e.xr - e.xl) e.slope = 1;
	else if (e.yr - e.yl == e.xl - e.xr) e.slope = -1;
	else ASSERT (0);

	return (&e);
    }
}

private void initQueue ()
{
    Debug (fprintf (stderr, "InitQueue start\n"));

    ready_in_queue = 0;
    middle = NULL;
    tmp_name = tempnam (tempdir (), "mkgln");

    cursor_x = 0;
    cursor_y = 0;

    if (optCompress) {
	tmp_fp = popen (mprintf ("%s > %s", compressCmd, tmp_name), "w");
	if (!tmp_fp) {
	    say ("Temp file compression turned off");
	    optCompress = FALSE;
	}
    }

    if (!optCompress) {
	tmp_fp = cfopen (tmp_name, "w+");
	unlink (tmp_name);
    }

    Debug (fprintf (stderr, "InitQueue end\n"));
}


private void flushQueue () {
    edge_t * tmp_d;
    edge_t * tmp, *delete;

    Debug (fprintf (stderr, "FlushQueue start\n"));

    tick ("flush queue");

    cursor_x = 0;
    cursor_y = 0;

    if (optCompress) {
	pclose (tmp_fp);
	tmp_fp = NULL;
	infoMaxTempSize = Max (infoMaxTempSize, fileSize (tmp_name));
	tmp_fp = popen (mprintf ("%s < %s", unCompressCmd, tmp_name), "r");
	if (tmp_fp == NULL) {
	    say ("cannot uncompress");
	    perror (mprintf ("%s < %s", unCompressCmd, tmp_name));
	    die ();
	}
    }
    else {
	fflush (tmp_fp);
	infoMaxTempSize = Max (infoMaxTempSize, ftell (tmp_fp));
	fseek (tmp_fp, 0, 0);
    }

    tmp = head;
    tmp_d = queueReadEdge (tmp_fp);
    while (tmp || tmp_d) {
	while (tmp && (tmp_d == NULL || smaller (tmp, tmp_d))) {

	    Debug (printEdge ("rest", tmp));
	    ASSERT (tmp -> ready == TRUE);
	    if (tmp -> remain == FALSE)
		ready_in_queue--;
	    ggln.xl = tmp -> xl;
	    ggln.yl = tmp -> yl;
	    ggln.xr = tmp -> xr;
	    ggln.yr = tmp -> yr;
	    if (!optNoOutput)
		dmPutDesignData (outputStream, GEO_GLN);
	    infoTotalOut++;

	    delete = tmp;
	    tmp = tmp -> next;
	    disposeEdge (delete);
	    infoTotalInQueue--;
	}

	while (tmp_d && (tmp == NULL || smaller (tmp_d, tmp))) {
	    Debug (printEdge ("from file", tmp_d));
	    ggln.xl = tmp_d -> xl;
	    ggln.yl = tmp_d -> yl;
	    ggln.xr = tmp_d -> xr;
	    ggln.yr = tmp_d -> yr;
	    if (!optNoOutput)
		dmPutDesignData (outputStream, GEO_GLN);
	    infoTotalOut++;

	    tmp_d = queueReadEdge (tmp_fp);
	}
    }

    head = tail = NULL;

    if (optCompress) {
	pclose (tmp_fp);
    }
    else {
	fclose (tmp_fp);
    }

    tmp_fp = NULL;

    unlink (tmp_name);

    ASSERT (ready_in_queue == 0);
    ASSERT (infoNowInTemp == 0);

    tock ("flush queue");

    Debug (fprintf (stderr, "FlushQueue end\n"));
}

private long fileSize (name)
char * name;
{
    struct stat statbuf;
    stat (name, &statbuf);
    return ((long) statbuf.st_size);
}

outputEdge (e)
edge_t * e;
{
    ggln.xl = e -> xl;
    ggln.yl = e -> yl;
    ggln.xr = e -> xr;
    ggln.yr = e -> yr;
    if (!optNoOutput)
	dmPutDesignData (outputStream, GEO_GLN);
    infoTotalOut++;
}

/* Emergency exit for signal handling
 */
void outputCleanUp () {
    if (optCompress) {
	if (tmp_fp) pclose (tmp_fp);
	unlink (tmp_name);
    }
}

outputPrintInfo (fp)
FILE * fp;
{
    fprintf (fp, "\n");
    fprintf (fp, "\ttotal # output edges        : %d\n", infoTotalOut);
    fprintf (fp, "\tmax output edges in core    : %d\n", infoMaxInQueue);
    fprintf (fp, "\tmax # of edges in temp file : %d\n", infoMaxInTemp);
    fprintf (fp, "\tsize of tempfile for output : %ld\n", infoMaxTempSize);
}
