/*
 *	$Source: /u1/X/DECToolkit/src/RCS/AsciiSink.c,v $
 *	$Header: AsciiSink.c,v 1.1 86/12/17 09:00:16 swick Exp $
 */

#ifndef lint
static char *rcsid_AsciiSink_c = "$Header: AsciiSink.c,v 1.1 86/12/17 09:00:16 swick Exp $";
#endif	lint

#ifndef lint
static  char    *sccsid = "@(#)AsciiSink.c	1.6          12/11/86";
#endif lint
/*
 *			  COPYRIGHT 1986
 *		   DIGITAL EQUIPMENT CORPORATION
 *		       MAYNARD, MASSACHUSETTS
 *			ALL RIGHTS RESERVED.
 *
 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 *
 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
 * SET FORTH ABOVE.
 *
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting documentation,
 * and that the name of Digital Equipment Corporation not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission.
 */


/* File: AsciiSink.c */

#include <X/Xlib.h>
#include "Toolkit.h"
#include "TextDisplay.h"

/* Private Ascii TextSink Definitions */

static int bufferSize = 200;

typedef struct _AsciiSinkData {
    FontInfo *font;
    } AsciiSinkData;

static char *buf;
static initialized = 0;

/* Utilities */
/***
 *** given a character this function determines its width in pixels 
 ***/
static int CharWidth (data, x, c)
  AsciiSinkData *data;  /*** need the font info from this ***/
  int x;		/*** starting spot of character -needed for tabs ***/
  char c;		/*** character ***/
{
    int     width, nonPrinting;
    FontInfo *info = data->font;

    if (c == '\t')
	return (info->width * 8) - ((x - xMargin) % (info->width * 8));
    if (c == LF)
	c = SP;
    if (c < SP || c > info->lastchar)
	nonPrinting = TRUE;
    else
	nonPrinting = FALSE;
    if (info->fixedwidth != 0) {
	if (nonPrinting)
	    width = 2 * info->width;
	else
	    width = info->width;
    }
    else {
	if (nonPrinting)
	    width = info->widths['^' - info->firstchar] +
		info->widths[c + '@' - info->firstchar];
	else
	    width = info->widths[c - info->firstchar];
    }
    return width;
}

/* Sink Object Functions */
/***
 *** given the source and the positions within the source to be displayed,
 *** this routine puts the text onto the screen.  What should go on
 *** each line has already been determined by the source routines. 
 ***/
static int AsciiDisplayText (sink, source, w, x, y, pos1, pos2, highlight)
  TTextSink *sink;		/* pointer to sink for window */
  TTextSource *source;		/* pointer to source for window */
  Window w;			
  int x, y;			/* starting x and y pixel coordinates */
  TTextPosition pos1, pos2;	/* starting and ending byte offsets */
  int highlight;		/* true if reverse video desired */
{
    AsciiSinkData * data;
    int     ink, background;
    int     j, k, width;
    TTextPosition nextPos;
    TTextBlock blk;

    data = (AsciiSinkData *) sink->data;
    j = 0;
    while (pos1 < pos2) {
	pos1 = (*(source->read))
		     (source, pos1, &blk, pos2 - pos1); /* get the text*/
	ink = (highlight) ? WhitePixel : BlackPixel;	      /* foreground */
	background = (highlight) ? BlackPixel : WhitePixel;   /* background */
	
	/** for each character in the text to be displayed **/
	for (k = 0; k < blk.length; k++) {
	    if (j >= bufferSize - 5)
		buf = (char *) Trealloc(buf, bufferSize *= 2);
	    buf[j] = blk.ptr[k];		
	    if (buf[j] == LF)	
		buf[j] = ' ';
	    else if (buf[j] == '\t') {
		/** if it's a tab then display what you have so far,    **
		 ** color in the tab, and move x over to after the tab. **/
		XText(w, x, y, buf, j, data->font->id, ink, background);
		buf[j] = 0;
		x += XStringWidth(buf, data->font, 0, 0);
		width = CharWidth(data, x, '\t');
		XPixSet(w, x, y, width, data->font->height, background);
		x += width;
		j = -1;
	    }
	    else
		if (buf[j] < ' ') {
		    buf[j + 1] = buf[j] + '@';
		    buf[j] = '^';
		    j = j + 1;
		}
	    j = j + 1;
	}
    }
    XText(w, x, y, buf, j, data->font->id, ink, background);
}

/***
 *** Given two byte positions, find the distance in pixels between them. 
 ***/
static AsciiFindDistance (sink, source, fromPos, fromx, toPos,
	resWidth, resPos, resHeight)
  TTextSink *sink;
  TTextSource *source;
  TTextPosition fromPos;	/* First position. */
  int fromx;			/* Horizontal location of first position. */
  TTextPosition toPos;		/* Second position. */
  int *resWidth;		/* Distance between fromPos and resPos. */
  int *resPos;			/* Actual second position used. */
  int *resHeight;		/* Height required. */
{
    AsciiSinkData *data;
    register    TTextPosition index, lastPos;
    register char   c;
    TTextBlock blk;

    data = (AsciiSinkData *) sink->data;
    lastPos = (*(source->getLastPos)) (source);
    (*(source->read)) (source, fromPos, &blk, toPos - fromPos);
    *resWidth = 0;
    
    /*** for each byte between the 2 positions ***/
    for (index = fromPos; index != toPos && index < lastPos; index++) {
	/** get the character **/
	if (index - fromPos >= blk.length)
	    (*(source->read)) (source, index, &blk, toPos - fromPos);
	c = blk.ptr[index-fromPos];

	/** compute the width in pixels **/
	if (c == LF) {
	    *resWidth += CharWidth(data, fromx + *resWidth, SP);
	    index++;
	    break;
	}
	*resWidth += CharWidth(data, fromx + *resWidth, c);
    }
    if (index == lastPos && c != LF) index = lastPos + 1;
    *resPos = index;
    *resHeight = data->font->height;
}

/***
 *** given a starting positions and the x coordinate of the starting
 *** position, this function will return the position of the last byte to
 *** be on that line.  If stopatWordBreak is false, it waits for a \n. 
 ***/
static AsciiFindPosition(sink, source, fromPos, fromx, width, stopAtWordBreak, 
	resPos, resWidth, resHeight)
  TTextSink *sink;	
  TTextSource *source;
  TTextPosition fromPos; 	/* Starting position. */
  int fromx;			/* Horizontal location of starting position. */
  int width;			/* Desired width. */
  int stopAtWordBreak;		/* Whether the resulting position should be at
				   a word break. */
  TTextPosition *resPos;	/* RETURNED: end position for line. */
  int *resWidth;		/* RETURNED: Actual width in pixels used. */
  int *resHeight;		/* RETURNED: Height required. */
{
    AsciiSinkData *data;
    TTextPosition lastPos, index, whiteSpacePosition;
    int     lastWidth, whiteSpaceSeen, whiteSpaceWidth;
    char    c;
    TTextBlock blk;
    data = (AsciiSinkData *) sink->data;

    lastPos = (*(source->getLastPos)) (source);
    (*(source->read)) (source, fromPos, &blk, bufferSize);
    *resWidth = 0;
    whiteSpaceSeen = FALSE;

    /** for each character that will fit on the line **/
    for (index = fromPos; *resWidth <= width && index < lastPos; index++) {
	lastWidth = *resWidth;
	/** get the character **/
	if (index - fromPos >= blk.length)
	    (*(source->read)) (source, index, &blk, bufferSize);
	c = blk.ptr[index - fromPos];

	/** find the width of the character **/
	if (c == LF) {
	    *resWidth += CharWidth(data, fromx + *resWidth, SP);
	    index++;
	    break;
	}
	*resWidth += CharWidth(data, fromx + *resWidth, c);

	/** check for white space **/
	if ((c == SP || c == TAB) && *resWidth <= width) {
	    whiteSpaceSeen = TRUE;
	    whiteSpacePosition = index;
	    whiteSpaceWidth = *resWidth;
	}
    }

    /*** if width too big to fit on line, backup to last position ***/
    if (*resWidth > width && index > fromPos) {
	*resWidth = lastWidth;
	index--;
	if (stopAtWordBreak && whiteSpaceSeen) {
	    index = whiteSpacePosition + 1;
	    *resWidth = whiteSpaceWidth;
	}
    }
    *resPos = index;
    *resHeight = data->font->height;
}


static int AsciiResolveToPosition (sink, source, pos, fromx, width,
				   leftPos, rightPos)
  TTextSink *sink;
  TTextSource *source;
  TTextPosition pos;
  int fromx,width;		
  TTextPosition *leftPos, *rightPos;
{
    int     resWidth, resHeight;

    AsciiFindPosition(sink, source, pos, fromx, width, FALSE,
	    leftPos, &resWidth, &resHeight);
    if (*leftPos > source->getLastPos(source))
	*leftPos = source->getLastPos(source);
    *rightPos = *leftPos;
}

/***
 *** given font info and window height, this routine routines the max
 *** number of lines that will fit in the window.
 ***/
static int AsciiMaxLinesForHeight (sink, height)
  TTextSink *sink;
  int height;
{
    AsciiSinkData *data;

    data = (AsciiSinkData *) sink->data;
    return(height / data->font->height);
}


/*** 
 *** Given the font info and the number of lines, this routine returns
 *** the number of pixels needed for the height.
 ***/
static int AsciiMaxHeightForLines (sink, lines)
  TTextSink *sink;
  int lines;
{
    AsciiSinkData *data;

    data = (AsciiSinkData *) sink->data;
    return(lines*data->font->height);
}


/* Public routines */

int *TCreateAsciiSink (font)
  FontInfo *font;
{
    TTextSink *sink;
    AsciiSinkData *data;

    if (initialized == 0) {
	buf = (char *) Tmalloc(bufferSize);
	initialized = 1;
    }
    sink = (TTextSink *) Tmalloc(sizeof(TTextSink));
    sink->display = AsciiDisplayText;
    sink->findPosition = AsciiFindPosition;
    sink->findDistance = AsciiFindDistance;
    sink->resolve = AsciiResolveToPosition;
    sink->maxLines = AsciiMaxLinesForHeight;
    sink->maxHeight = AsciiMaxHeightForLines;
    sink->data = (int *) Tmalloc(sizeof(AsciiSinkData));
    data = (AsciiSinkData *) sink->data;
    data->font = font;
    return(int *) sink;
}

void TDestroyAsciiSink (sink)
  TTextSink *sink;
{
    AsciiSinkData *data;

    data = (AsciiSinkData *) sink->data;
    free(data);
    free(sink);
}

