#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "../util/error.h"
#include "../util/memory.h"

#define Alib_IMPORT
#include "Alib.h"


/*
#define FLOAT_SLOPE
	Comment-out this macro if you want "int" precision arithmetic on
	line slopes: faster but some garbage on the screen may appear.
*/

/**
 * NOTE. Max no. of ColorSegment per ScanLine. Its value depends on the
 * complexity of the image. Increase if you get the "Color Segment pool
 * overflow" error. In the worst case this value is the width of the window.
 */
#define MAX_CS_PER_LINE 128

#define NotAnElement            ((Alib_ZInfo *) -1)

#define MaxDepth                0xFFFFFFFF

/**
 * Max no. of colors, that is the length of the CLUT.
 */
#define Alib_MaxPixelValue      (17*17*17)

/*
 * Bit masks for the "flags" fields of the window. Currently set in the
 * constructor, no client access to them.
 */
#define Alib_ModeDepthCueing    32  /* Perform depth cueing */
#define Alib_ModeDepthCueParsed 64  /* Pixel has been assigned to dc color */


struct Alib_ZInfo {
	Alib_Pixel    color;    /* color of this polygon */
	unsigned int  depth;    /* depth of this polygon */
	Alib_ZInfo   *next;     /* next polygon in plane sweep set */
	Alib_ZInfo   *prev;     /* previous polygon in plane sweep set */
};


/**
 *  Polygons are built from edges.
 */
typedef struct Edge {
	short     y2;            /* Ending y location */
#ifdef FLOAT_SLOPE
	float     x1;            /* Starting x location */
	float     Dx;            /* Inverse slope of edge line */
#else
	long      x1;            /* Starting x location */
	long      Dx;            /* Inverse slope of edge line */
#endif
	Alib_ZInfo    *p;        /* depth and color information */
	struct Edge *nexte;      /* next edge on this edge list */
	struct Edge *next;       /* next edge on active edge list */
} Edge;


/**
 *  Pointer to a list of Edges.
 */
typedef struct EdgeList {
	Edge     *head;          /* pointer to first edge in list */
} EdgeList;


/**
 *  Row of adjacent pixels all of the same color.
 */
typedef struct ColorSegment {
	short     x;             /* starting x location */
	unsigned short length;   /* number of pixels of this color */
	Alib_Pixel     color;    /* color of this segment */
} ColorSegment;


typedef struct {
	Alib_Segment *head;      /* pointer to a vector of BufferedSegment's */
	int       count;         /* number of seg's currently in use */
} SegmentGroup;


/**
 *  Vector of ColorSegment's.
 */
typedef struct ScanLine {
	ColorSegment *head;     /* first element in the list */
	ColorSegment *tail;     /* last element in the list */
	int       count;
} ScanLine;


struct Alib_Window {
	gui_Type *gui;
	int       width, height;        /* dimensions of this window */
	Alib_Rect rect;                 /* (0,0)-(width,height) */
	Alib_Rect clip;                 /* clipping bounds for current drawing */
	int       flags;                /* (for internal use only) */
	int       bsegSize;             /* Count of elements in each bseg element */
	SegmentGroup *bseg;
	int       ymin, ymax;           /* range of EdgeList's with polygons */
	ScanLine *scanLine;             /* a vector with height elements */
	ScanLine *lastScanLine;         /* a vector with height elements */
	EdgeList *edges;                /* a vector with height elements */
	EdgeList *lines;                /* a vector with height elements */
	
	Edge     *edgePool;             /* a pool of polygon edges */
	unsigned int EPSize;            /* number of entries in edgePool */
	unsigned int EPTop;             /* index of first free edge */
	
	unsigned int curPool;           /* selects csPool (0 or 1) */
	
	ColorSegment *csPool0;
	unsigned int CSSize0;           /* number of entries in csPool0 */
	unsigned int CSTop0;            /* index of first free color seg */
	
	ColorSegment *csPool1;
	unsigned int CSSize1;           /* number of entries in csPool1 */
	unsigned int CSTop1;            /* index of first free color seg */
	
	unsigned long depth;            /* polygon depth while plotting */
	Alib_ZInfo  *zpool;
	long         zsize;             /* capacity of zpool */
	long         ztop;              /* index next available zpool entry */
	
	double       visibility;        /* visibility range (m) */
	VColor_Type      *haze_color;        /* fade-out color with distance */
};


void Alib_setPoint(Alib_Point *p, int x, int y)
{
	p->x = x;
	p->y = y;
}


void Alib_setRect(Alib_Rect *r, int ax, int ay, int bx, int by)
{
	if( ax >= bx || ay >= by ){
		r->a.x = 0;
		r->a.y = 0;
		r->b.x = 0;
		r->b.y = 0;
	} else {
		r->a.x = ax;
		r->a.y = ay;
		r->b.x = bx;
		r->b.y = by;
	}
}


int Alib_isEmptyRect(Alib_Rect *r)
{
	if( (r->a.x >= r->b.x) || (r->a.y >= r->b.y) )
		return 1;
	else
		return 0;
}


void Alib_expandRect(Alib_Rect *r, int dx, int dy)
{
	Alib_setRect(r, r->a.x - dx, r->a.y - dy, r->b.x + dx, r->b.y + dy);
}


static int min(int x, int y)
{
	if( x <= y )
		return x;
	else
		return y;
}


static int max(int x, int y)
{
	if( x >= y )
		return x;
	else
		return y;
}


void Alib_intersectRect(Alib_Rect *r1, Alib_Rect *r2, Alib_Rect *res)
{
	Alib_setRect(res,
		max(r1->a.x, r2->a.x), max(r1->a.y, r2->a.y),
		min(r1->b.x, r2->b.x), min(r1->b.y, r2->b.y));
}


/**
 * Allocate internal buffers based on current width,height, then
 * set counters and indeces to these buffers.
 */
static void
awindow_alloc_internals(Alib_Window *w)
{
	int i;

	w->edges = memory_allocate(w->height * sizeof(EdgeList), NULL);
	w->lines = memory_allocate(w->height * sizeof(EdgeList), NULL);
	w->EPSize = 64 * 1024;
	w->edgePool = memory_allocate(w->EPSize * sizeof(Edge), NULL);
	memset(w->edgePool, 0, w->EPSize * sizeof(Edge));

	w->scanLine = memory_allocate(w->height * sizeof(ScanLine), NULL);
	w->lastScanLine = memory_allocate(w->height * sizeof(ScanLine), NULL);

	w->CSSize0 = w->height * MAX_CS_PER_LINE;
	w->csPool0 = memory_allocate(w->CSSize0 * sizeof(ColorSegment), NULL);
	memset(w->csPool0, 0, w->CSSize0 * sizeof(ColorSegment));

	w->CSSize1 = w->height * MAX_CS_PER_LINE;
	w->csPool1 = memory_allocate(w->CSSize1 * sizeof(ColorSegment), NULL);
	memset(w->csPool1, 0, w->CSSize1 * sizeof(ColorSegment));

	w->EPTop = w->CSTop0 = w->CSTop1 = w->curPool = 0;

	w->ymin = w->height;
	w->ymax = 0;

	for (i = 0; i < w->height; ++i) {
		w->scanLine[i].count = 0;
		w->scanLine[i].head = NULL;
		w->scanLine[i].tail = NULL;
		w->lastScanLine[i].count = 0;
		w->lastScanLine[i].head = NULL;
		w->lastScanLine[i].tail = NULL;
		w->edges[i].head = NULL;
		w->lines[i].head = NULL;
	}
}


/**
 * Release all the internal data buffers, but DO NOT set counters nor
 * indices and does not free nor initialize: bseg[], zpool, aPixel.
 */
static void
awindow_free_internals(Alib_Window *w)
{
	memory_dispose(w->edges);
	memory_dispose(w->lines);
	memory_dispose(w->edgePool);
	memory_dispose(w->scanLine);
	memory_dispose(w->lastScanLine);
	memory_dispose(w->csPool0);
	memory_dispose(w->csPool1);
}


static void
Alib_destruct(void *p)
{
	Alib_Window *w = (Alib_Window *) p;
	int i;

	awindow_free_internals(w);

	for (i = 0; i < w->bsegSize; ++i){
		if( w->bseg[i].count >= 0 ){
			memory_dispose(w->bseg[i].head);
		}
	}
	memory_dispose(w->bseg);

	memory_dispose(w->zpool);
}


Alib_Window * Alib_new(gui_Type *gui)
{
	Alib_Window  *w;
	int       i;
	
	int width = gui_getWidth(gui);
	int height = gui_getHeight(gui);

	if( width < 1 )
		width = 1;

	if( height < 1 )
		height = 1;

	w = memory_allocate(sizeof(Alib_Window), Alib_destruct);
	memset(w, 0, sizeof(Alib_Window));

	w->gui = gui;
	w->width = width;
	w->height = height;
	Alib_setRect(&w->rect, 0, 0, w->width, w->height);
	w->clip = w->rect;
	w->flags = 0;

	w->zsize = 32768;
	w->ztop = 0;
	w->zpool = memory_allocate(sizeof(Alib_ZInfo) * w->zsize, NULL);
	w->depth = MaxDepth;

	awindow_alloc_internals(w);

	w->bsegSize = Alib_MaxPixelValue + 1;
	w->bseg = memory_allocate(sizeof(SegmentGroup) * w->bsegSize, NULL);
	for (i = 0; i < w->bsegSize; ++i)
		w->bseg[i].count = -1;
	
	w->visibility = 20.0 * 1853.0;
	w->haze_color = VColor_getByName("#ccc", 0);

	return w;
}


void
Alib_resize(Alib_Window * w, int width, int height)
{
	if( width < 1 )
		width = 1;

	if( height < 1 )
		height = 1;

	if( w->width == width  &&  w->height == height ){
		return;
	}

	awindow_free_internals(w);

	w->width = width;
	w->height = height;
	Alib_setRect(&w->rect, 0, 0, width, height);
	Alib_intersectRect(&w->clip, &w->rect, &w->clip);

	awindow_alloc_internals(w);
}


void Alib_setDepthCueing(Alib_Window *w, int value)
{
	if( value )
		w->flags |= Alib_ModeDepthCueing;
	else
		w->flags &= ~Alib_ModeDepthCueing;
}


static ColorSegment *
CSOverflow(void)
{
	static int warning_raised = 0;

	if( ! warning_raised ){

		fprintf(stderr, "Color Segment pool overflow.\n"
			"Try increasing the constant MAX_CS_PER_LINE in " __FILE__ "\n");

		warning_raised = 1;
	}

	return NULL;
}


static ColorSegment *
mallocCS(Alib_Window * w)
{
	switch (w->curPool) {
	case 0:
		return (w->CSTop0 == w->CSSize0) ?
			CSOverflow() : &(w->csPool0[(w->CSTop0)++]);
	case 1:
		return (w->CSTop1 == w->CSSize1) ?
			CSOverflow() : &(w->csPool1[(w->CSTop1)++]);
	default:
		error_internal("w->curPool=%d", w->curPool);
	}
}


static __inline void
freeLastCS(Alib_Window * w)
{
	switch (w->curPool) {
	case 0:
		w->CSTop0--;
		break;
	case 1:
		w->CSTop1--;
		break;
	default:
		error_internal("w->curPool=%d", w->curPool);
	}
}


/**
 * Draw a line segment ranging from pixel (x0,y) to (x1-1,y) so that the
 * segment is exactly (x1-x0) pixels long. Does nothing if the length is
 * zero or negative, i.e. x1-x0<=0. Successive calls on the same scan line
 * must be ordered by x0 value.
 */
static void
DrawScanLine(Alib_Window * w, int y, int x0, int x1, Alib_Pixel color)

{
	ColorSegment *p;
	int lastx;

	assert( 0 <= y && y < w->height );

	if( x0 >= x1 )
		return;

	if (w->scanLine[y].count == 0) {

/*
 *  This is the first segment on this scan line.
 */

		p = mallocCS(w);
		if (p != NULL) {
			p->x = x0;
			p->length = x1 - x0;
			p->color = color;
			w->scanLine[y].head = w->scanLine[y].tail = p;
			w->scanLine[y].count = 1;
		}

	}

/*
 *  The scan line wasn't empty.  Check the previous scan line entry for
 *  any overlap with this one.
 */

	else {
		p = w->scanLine[y].tail;
		lastx = p->x + p->length;

		if (lastx > x0) {

/*
 *  The segments overlap.
 *
 *  If the overlapping segments have the same color, then simply
 *  extend the previous segment's length.
 */

			if (p->color == color)
				p->length = x1 - p->x;

/*
 *  The overlapping segment's colors are different.  Shorten the previous
 *  segment and allocate an entry for the current one.  If the shortened
 *  segment is eliminated, use it to store this segment's information.
 */

			else {
				p->length = x0 - p->x;
				if (p->length > 0) {
					if ((p = mallocCS(w)) != NULL) {
						++w->scanLine[y].count;
						w->scanLine[y].tail = p;
					}
				}

/*
 *  If the shortened segment's length went to zero, we may need to merge
 *  this segment with the last one.
 */

				if (((p - 1)->color == color) &&
					(w->scanLine[y].count > 1)) {
					freeLastCS(w);
					p = w->scanLine[y].tail = p - 1;
					--w->scanLine[y].count;
					p->length = x1 - p->x;
				}
				else {
					p->x = x0;
					p->length = x1 - x0;
					p->color = color;
				}
			}
		}

/*
 *  The segments do not overlap.
 *
 *  If the segments are adjacent and the colors are the same, extend the
 *  last segment.
 *
 *  Otherwise, create a new segment and append it to this line.
 */

		else if ((lastx == x0) && (p->color == color))
			p->length = x1 - p->x;

		else if ((p = mallocCS(w)) != NULL) {
			++w->scanLine[y].count;
			w->scanLine[y].tail = p;
			p->x = x0;
			p->length = x1 - x0;
			p->color = color;
		}

	}

}


#define QDrawScanLine(w, y, x0, x1, c) \
	{                               \
		if (lastStart == -1) {      \
			lastStart = x0;         \
			lastColor = c;          \
		}                           \
		else if (c != lastColor) {  \
			DrawScanLine (w, y, lastStart, x0, lastColor);  \
			lastStart = x0;         \
			lastColor = c;          \
		}                           \
	}


/**
 * Remove entries from the active edge table that should no longer
 * be drawn.  Increment the x value of all others.  Add all entries
 * that begin on this row.
 */
static void
updateActiveEdgeList(Alib_Window * w, Edge ** head, Edge ** lineHead, int y)
{

	int inserted;
	Edge *p, *q, *last, *newhead, *newtail, *q1, *q2;

/*
 *  Remove stale edges from the active list and increment x values of the rest.
 *
 *  We get a bit tricky here.  The active edge table must be ordered by x1
 *  value to allow for the plane sweep method to work.  The relative edge
 *  sequence may change as each edge's x1 value changes.  We'll build an
 *  updated, sorted "partial" edge list as we increment x1 values.  An edge that
 *  no longer belongs after its predecessor in the list is temporarily moved
 *  to this row's new edge list so that it can be inserted in it's proper
 *  position.
 */

	newhead = newtail = NULL;

	for (p = *head; p != NULL; p = p->next) {

		if (p->y2 != y) {

			p->x1 += p->Dx;

			if (newhead == NULL) {
				newhead = newtail = p;
			}
			else if (p->x1 >= newtail->x1) {
				newtail->next = p;
				newtail = p;
			}
			else {
				p->nexte = w->edges[y].head;
				w->edges[y].head = p;
			}

		}

	}

	*head = newhead;
	if (newtail != NULL)
		newtail->next = NULL;

/*
 *  Add any new entries to the active line list.
 */

	for (q = w->lines[y].head; q != NULL; q = q->nexte) {
		q->next = *lineHead;
		*lineHead = q;
	}

/*
 *  Add edges corresponding to each active line.
 */

	newhead = newtail = NULL;

	for (q = *lineHead; q != NULL; q = q->next) {
		if (y < q->y2) {
			q1 = q + 1;
			q2 = q + 2;
			q1->x1 = q->x1;
			q2->x1 = q->x1 = q->x1 + q->Dx;
			q1->y2 = q2->y2 = y + 1;
			q1->nexte = q2;
			q2->nexte = w->edges[y].head;
			w->edges[y].head = q1;
			if (newhead == NULL) {
				newhead = newtail = q;
			}
			else {
				newtail->next = q;
				newtail = q;
			}
		}
	}

	*lineHead = newhead;
	if (newtail != NULL)
		newtail->next = NULL;

/*
 *  Insert all new edges for this row in sorted order onto the active edge list
 */

	for (q = w->edges[y].head; q != NULL; q = q->nexte) {

		if (*head == NULL) {
			*head = q;
			q->next = NULL;
		}
		else {
			inserted = 0;
			for (p = *head, last = NULL; p != NULL;) {
				if (q->x1 <= p->x1) {
					if (last == NULL)
						*head = q;
					else
						last->next = q;
					q->next = p;
					inserted = 1;
					break;
				}
				last = p;
				p = p->next;
			}
			if (inserted == 0) {
				last->next = q;
				q->next = NULL;
			}
		}

	}

}


#define RIGHT_SIDE_CORRECTION

/**
 * Draw all polygons on this scan line by the plane sweep method.
 */
static void
planeSweep(Alib_Window * w, Edge * head, int y)
{

	Alib_ZInfo    *ps = NULL;       /* polygon set head */
	Alib_ZInfo    *q, *r, *lastr = NULL;
	Edge     *p;                            /* current edge */
	register int x0, x1 = 0, lastStart = -1;
	Alib_Pixel     lastColor = 0;
	unsigned long lastDepth = MaxDepth;

	if (head == NULL) {
		w->scanLine[y].count = 0;
		return;
	}

	for (p = head; p->next != NULL; p = p->next) {

#ifdef FLOAT_SLOPE
		x0 = p->x1;
		x1 = p->next->x1;
#else
		x0 = p->x1 >> 16;
		x1 = p->next->x1 >> 16;
#endif

/*
 *  Polygons are ordered on the ps list by depth.  We use a special flag
 *  to determine quickly if the current polygon is or is not an element of the
 *  currently active polygon set.  Since the polygon set is ordered by depth,
 *  the first element determines the color that's drawn.
 */

		q = p->p;
		if (q->next == NotAnElement) {
			if (ps == NULL) {
				ps = q;
				q->next = q->prev = NULL;
			}
			else {
				for (r = ps; r != NULL; r = r->next) {
					if (q->depth < r->depth) {
						if (r->prev == NULL)
							ps = q;
						else
							r->prev->next = q;
						q->next = r;
						q->prev = r->prev;
						r->prev = q;
						break;
					}
					lastr = r;
				}
				if (r == NULL) {
					q->next = lastr->next;
					lastr->next = q;
					q->prev = lastr;
				}
			}

		}
		else {
			if (q->prev == NULL)
				ps = q->next;
			else
				q->prev->next = q->next;
			if (q->next != NULL)
				q->next->prev = q->prev;
			q->next = NotAnElement;
		}

/*
 *  If the polygon set is non-null, then there is some line segment that
 *  should be plotted.  We'll perform one small correction here:  if the depth
 *  of the last adjacent polygon segment was less than the depth of this one,
 *  then add one to the x0 value.  This prevents the right side of a polygon
 *  from looking different than the left side.
 */

#ifdef RIGHT_SIDE_CORRECTION
		if (ps != NULL) {
			if (lastDepth < ps->depth) {
				if ((++x0) <= x1) {
					QDrawScanLine(w, y, x0, x1, ps->color);
					lastDepth = ps->depth;
				}
			}
			else {
				QDrawScanLine(w, y, x0, x1, ps->color);
				lastDepth = ps->depth;
			}
		}
		else {
			lastDepth = MaxDepth;
			if (lastStart != -1) {
				DrawScanLine(w, y, lastStart, x1, lastColor);
				lastStart = -1;
			}
		}
#else
		if (ps != NULL)
			DrawScanLine(w, y, x0, x1, ps->color);
#endif

	}

	if (lastStart != -1)
		DrawScanLine(w, y, lastStart, x1, lastColor);

	if (ps != NULL) {
#ifdef DEBUG
		if (ps->next != NULL)
			fprintf(stderr, "More then 1 element left at end of planeSweep\n");
#endif
		ps->next = NotAnElement;
	}

}


/**
 * Convert the edge table to scan line segments.
 */
static void
EdgeTableToScanLine(Alib_Window * w)
{

	int       y;
	Edge     *active;               /* head of active edge list */
	Edge     *activeLines;          /* head of active line list */

	active = activeLines = NULL;

	if( !(0 <= w->ymin && w->ymin <= w->ymax && w->ymax < w->height) )
		error_internal("ymin = %d, ymax = %d, height = %d",
			w->ymin, w->ymax, w->height);

	for (y = w->ymin; y <= w->ymax; ++y) {

		updateActiveEdgeList(w, &active, &activeLines, y);

		planeSweep(w, active, y);

	}
}



static void
AllocBufferedSegments(Alib_Window * w, Alib_Pixel c)
{
	assert( 0 <= c && c < w->bsegSize );
	w->bseg[c].head = memory_allocate(w->bsegSize * sizeof(Alib_Segment), NULL);
	w->bseg[c].count = 0;
}


static void OutputSegments(gui_Type *gui, Alib_Segment *segments, int nsegments,
		int color_index)
{
	while(nsegments > 0) {
		gui_drawLine(gui, segments->x1, segments->y1, segments->x2, segments->y2, color_index);
		segments++;
		nsegments--;
	}
}


static void
OutputSegment(Alib_Window *w, int yp, int x0p, int x1p, Alib_Pixel c)
{
	int             i;
	Alib_Segment    *p;

	assert( 0 <= c && c < w->bsegSize );
	if (w->bseg[c].count == -1)
		AllocBufferedSegments (w, c);
	i = ++(w->bseg[c].count);
	p = &(w->bseg[c].head[i-1]);
	p->x1 = x0p;
	p->x2 = x1p;
	p->y1 = p->y2 = yp;
	if (i == w->bsegSize) {
		p = w->bseg[c].head;
		OutputSegments(w->gui, p, w->bsegSize, c);
		w->bseg[c].count = 0;
	}
}


static void
FlushBufferedSegments(Alib_Window * w)
{
	int i;

	for (i = 0; i < w->bsegSize; ++i) {
		if (w->bseg[i].count > 0) {
			OutputSegments(w->gui, w->bseg[i].head, w->bseg[i].count, i);
			w->bseg[i].count = 0;
		}
	}
}


/**
 * Determine the differences between this frame and the previous one
 * and plot the differences.
 */
static void
ScanLineDifference(Alib_Window * w)
{

	ColorSegment *cur, *last;
	int len, curX = 0, curLength = 0, curCount, lastCount, y;

	for (y = 0; y < w->height; ++y) {

		cur = w->scanLine[y].head;
		curCount = w->scanLine[y].count;

		if (curCount > 0) {
			curX = cur->x;
			curLength = cur->length;
		}

		last = w->lastScanLine[y].head;
		lastCount = w->lastScanLine[y].count;

		while (curCount != 0) {

			if (lastCount == 0) {
				OutputSegment(w, y, curX, curX + curLength - 1, cur->color);
				++cur;
				curX = cur->x;
				curLength = cur->length;
				--curCount;
			}

			else if (curX == last->x) {

				if (curLength == last->length) {

/*
 *  The trivial case:  Both segments have the same length.  If the colors are
 *  different, then output the correct new color.
 */

					if (cur->color != last->color) {
						OutputSegment(w, y, curX, curX + curLength - 1, cur->color);
					}
					++last;
					++cur;
					curX = cur->x;
					curLength = cur->length;
					--curCount;
					--lastCount;
				}

				else if (curLength < last->length) {

/*
 *  The current segment is shorter than the last one.  Output a new line segment
 *  if the colors differ; delete the current segment and update the last one's
 *  x value and length.
 */
					if (cur->color != last->color) {
						OutputSegment(w, y, curX, curX + curLength - 1, cur->color);
					}
					last->x += curLength;
					last->length -= curLength;
					++cur;
					curX = cur->x;
					curLength = cur->length;
					--curCount;
				}

/*
 *  The current segment is longer than the last one.  Output, if necessary;
 *  delete the last segment and update the current segment's x value and length.
 */

				else {
					if (cur->color != last->color) {
						OutputSegment(w, y, curX, curX + last->length - 1, cur->color);
					}
					curX += last->length;
					curLength -= last->length;
					++last;
					--lastCount;
				}

			}

/*
 *  The current segment starts before any previous segments.
 */

			else if (curX < last->x) {
				if (curX + curLength > last->x)
					len = last->x - curX;
				else
					len = curLength;
				OutputSegment(w, y, curX, curX + len - 1, cur->color);
				curX += len;
				curLength -= len;
				if (curLength == 0) {
					++cur;
					curX = cur->x;
					curLength = cur->length;
					--curCount;
				}
			}

/*
 *  The previous segment begins before the first current segment.
 */

			else {
				if (last->x + last->length > curX)
					len = curX - last->x;
				else
					len = last->length;

				last->x += len;
				last->length -= len;
				if (last->length == 0) {
					++last;
					--lastCount;
				}
			}
		}
	}
}


/*
 *  Randomly changing drawing colors in X would require that we add an XChangeGC
 *  request before virtually every line segment draw.
 *  Once the scan line segment output has been optimized by ScanLineDifference,
 *  we can add a further optimization to reduce the number of XChangeGC
 *  requests that are required.  We'll add a structure to group drawing
 *  requests by color.  When some watermark is past, an XChangeCG request may
 *  be issued, followed by all requests for that color.  This request buffer
 *  will be flushed with a call to FrameComplete().
 */

static void
FrameComplete(Alib_Window * w)
{

	ScanLine *tmp;
	int i;

/*
 *  Convert the edge table to scan line segments.
 */

	EdgeTableToScanLine(w);

/*
 *  Determine the differences between this frame and the previous one
 *  and plot the differences.
 */

	ScanLineDifference(w);

/*
 *  This frame now becomes the previous frame; clear the new current frame.
 */

	tmp = w->lastScanLine;
	w->lastScanLine = w->scanLine;
	w->scanLine = tmp;

	for (i = 0; i < w->height; ++i) {
		w->scanLine[i].count = 0;
		w->edges[i].head = NULL;
		w->lines[i].head = NULL;
	}

/*
 *  Release the allocated color segments for what was the last frame.
 */

	if (w->curPool == 0) {
		w->curPool = 1;
		w->CSTop1 = 0;
	}
	else if (w->curPool == 1) {
		w->curPool = 0;
		w->CSTop0 = 0;
	}
	else {
		w->curPool = 0;
		w->CSTop0 = 0;
	}

/*
 *  Release the allocated elements of the edge pool.
 */

	w->EPTop = 0;

	w->ymin = w->height;
	w->ymax = 0;

/*
 *  Perform any graphics-dependent finish-up
 */

	FlushBufferedSegments(w);
}


void
Alib_invalidate(Alib_Window * w)
{
	int i;

	for (i = 0; i < w->height; ++i) {
		w->lastScanLine[i].count = 0;
	}
}


Alib_ZInfo * Alib_nextZInfo(Alib_Window * w, Alib_Pixel color)
{
	if (w->ztop >= w->zsize)
		error_internal("Z-information pool overflow", 0);

	Alib_ZInfo *z = &(w->zpool[(w->ztop)++]);
	z->depth = --w->depth;
	z->color = color;
	return z;
}


void
Alib_drawSegments(Alib_Window * w, Alib_Segment * seg, int nseg, Alib_Pixel color)
{
	Alib_ZInfo *z = Alib_nextZInfo(w, color);

	for (; nseg > 0; --nseg) {
		Alib_draw3DLine(w, seg->x1, seg->y1, seg->x2, seg->y2, z);
		++seg;
	}
}


void
Alib_drawLine(Alib_Window *w, int x1, int y1, int x2, int y2, Alib_Pixel color)
{
	Alib_ZInfo *z = Alib_nextZInfo(w, color);
	Alib_draw3DLine(w, x1, y1, x2, y2, z);
}


#define TICKtoRAD(a)    (a * M_PI / (180.0 * 64.0))
#define INCR            (30 * 64)

void Alib_drawArc(Alib_Window *w, int x, int y, int width, int height,
	int angle1, int angle2, Alib_Pixel color)
{
	/*
		FIXME: needs optimization avoiding to calculate all that cos/sin.
		FIXME: the no. of sides should depend on the radius.
	*/

	double W, H, xc, yc;
	int incr, x1, x2, y1, y2;

	Alib_ZInfo *z = Alib_nextZInfo(w, color);

	W = width / 2.0;
	H = height / 2.0;
	xc = x + width / 2.0;
	yc = y + height / 2.0;

	if (angle2 < 0) {
		incr = -INCR;
		angle2 = -angle2;
	}
	else
		incr = INCR;

	if (angle2 > 360 * 64)
		angle2 = 360 * 64;

	x1 = (int) (xc + (W * cos(TICKtoRAD(angle1))));
	y1 = (int) (yc - (H * sin(TICKtoRAD(angle1))));

	while (angle2 != 0) {
		angle1 += incr;
		angle2 -= INCR;
		if (angle2 < 0) {
			angle1 -= angle2;
			angle2 = 0;
		}
		x2 = (int) (xc + (W * cos(TICKtoRAD(angle1))));
		y2 = (int) (yc - (H * sin(TICKtoRAD(angle1))));
		Alib_drawLine(w, x1, y1, x2, y2, z->color);
		x1 = x2;
		y1 = y2;
	}
}


void
Alib_drawRect(Alib_Window *w, Alib_Rect *r, Alib_Pixel color)
{
	Alib_Segment seg[4];

	if( Alib_isEmptyRect(r) )
		return;

	seg[0].x1 = r->a.x;
	seg[0].y1 = r->a.y;
	seg[0].x2 = r->b.x - 1;
	seg[0].y2 = r->a.y;

	seg[1].x1 = r->b.x - 1;
	seg[1].y1 = r->a.y;
	seg[1].x2 = r->b.x - 1;
	seg[1].y2 = r->b.y - 1;

	seg[2].x1 = r->b.x - 1;
	seg[2].y1 = r->b.y - 1;
	seg[2].x2 = r->a.x;
	seg[2].y2 = r->b.y - 1;

	seg[3].x1 = r->a.x;
	seg[3].y1 = r->b.y - 1;
	seg[3].x2 = r->a.x;
	seg[3].y2 = r->a.y;

	Alib_drawSegments(w, seg, 4, color);
}


void
Alib_fillRect(Alib_Window *w, Alib_Rect *r, Alib_Pixel color)
{
	if( Alib_isEmptyRect(r) )
		return;

	Alib_ZInfo *z = Alib_nextZInfo(w, color);
	Alib_fill3DRect(w, r, z);
}


void
Alib_fillPolygon(Alib_Window *w, Alib_Point * pts, int npts, Alib_Pixel color)
{
	Alib_ZInfo *z = Alib_nextZInfo(w, color);
	Alib_fill3DPolygon(w, pts, npts, z);
}


void
Alib_setClipRect(Alib_Window *w, Alib_Rect *r)
{
	Alib_intersectRect(&w->rect, r, &w->clip);
}


/**
 * Build an edge table based on edge coherence as described by
 * Rod Salmon and Mel Slater, 1987.
 */
static void
MakeET(Alib_Window * w, Alib_Point * pts, int npts, Alib_ZInfo * zinfo)
{

	int i, t, addedEdge = 0;
	int x1, y1, x2, y2;
	int ymin, ymax;
	Edge *e;

	ymin = w->height;
	ymax = 0;

	zinfo->next = NotAnElement;

/*
 *  Process each edge in this polygon
 */

	for (i = 1; i <= npts; ++i) {

/*
 *  Determine the end-points of this edge (x1, y1), (x2, y2).
 */

		if (i == npts) {
			x2 = pts[0].x;
			y2 = pts[0].y;
		}
		else {
			x2 = pts[i].x;
			y2 = pts[i].y;
		}
		x1 = pts[i - 1].x;
		y1 = pts[i - 1].y;

		if (y1 > y2) {

			t = x1;
			x1 = x2;
			x2 = t;
			t = y1;
			y1 = y2;
			y2 = t;

		}

		if (y1 != y2) {

			if (w->EPTop == w->EPSize) {
				fprintf(stderr, "Edge Pool Overflow\n");
				return;
			}

			e = &(w->edgePool[(w->EPTop)++]);

			e->y2 = y2;
#ifdef FLOAT_SLOPE
			e->x1 = x1;
			e->Dx = (x2 - x1) / (double) (y2 - y1);
#else
			e->x1 = (x1 << 16) + 0x8000;
			e->Dx = ((x2 - x1) << 16) / (y2 - y1);
#endif

			e->p = zinfo;
			e->nexte = w->edges[y1].head;
			w->edges[y1].head = e;

			addedEdge = 1;

			if (y1 < ymin)
				ymin = y1;

			if (y2 > ymax)
				ymax = y2;
		}

	}

	if (addedEdge) {

		if (w->ymin > ymin)
			w->ymin = ymin;

		if (w->ymax < ymax)
			w->ymax = ymax;
	}
}


void
Alib_draw3DPoint(Alib_Window * w, int x, int y, Alib_ZInfo * zinfo)
{
	Edge *e, *e1;

	if (x < w->clip.a.x || x >= w->clip.b.x
	|| y < w->clip.a.y || y >= w->clip.b.y)
		return;

	zinfo->next = NotAnElement;

	if (w->EPTop + 2 > w->EPSize) {
		fprintf(stderr, "Edge Pool Overflow\n");
		return;
	}

	e = &(w->edgePool[(w->EPTop)++]);
	e1 = &(w->edgePool[(w->EPTop)++]);

	e->y2 = e1->y2 = y + 1;
#ifdef FLOAT_SLOPE
	e->x1 = e1->x1 = x;
	e->Dx = 0.0;
#else
	e->x1 = e1->x1 = (x << 16) + 0x8000;
	e->Dx = 0;
#endif
	e->p = e1->p = zinfo;

	e1->nexte = w->edges[y].head;
	e->nexte = e1;
	w->edges[y].head = e;

	if (y < w->ymin)
		w->ymin = y;

	if (y > w->ymax)
		w->ymax = y;
}


void
Alib_drawDifferences(Alib_Window *w)
{
	w->ztop = 0;
	w->depth = MaxDepth;
	FrameComplete(w);
}


/* FIXME: remove static limit */
#define MAX_VERT 1000

void Alib_fill3DPolygon(Alib_Window * w, Alib_Point * pts, int npts, Alib_ZInfo * zinfo)
{
	int x1, y1, x2, y2, i, np, nr;
	Alib_Point *a, *b, q;
	int a_in, b_in;
	double t;
	Alib_Point p[MAX_VERT], r[MAX_VERT];

	if( npts < 3 ){
		return;
	}

	if( npts > MAX_VERT )
		error_internal("too many vertices", 0);

	x1 = w->clip.a.x;
	y1 = w->clip.a.y;
	x2 = w->clip.b.x;
	y2 = w->clip.b.y;

	/*
		Clipping of pts[0..npts-1] against the right side.
		Generating p[0..np-1].
	*/

	np = 0;
	a = &pts[npts-1];
	a_in = (a->x <= x2);
	for( i=0; i < npts ; i++ ){
		b = &pts[i];
		b_in = (b->x <= x2);
		if( a_in ){
			if( b_in ){
				p[np++] = *b;
				a = b;
				a_in = 1;
			} else {
				q.x = x2;
				t = (x2 - a->x) / (double)(b->x - a->x);
				q.y = a->y + t*(b->y - a->y) + 0.5;
				p[np++] = q;
				a = b;
				a_in = 0;
			}
		} else {
			if( b_in ){
				q.x = x2;
				t = (x2 - a->x) / (double)(b->x - a->x);
				q.y = a->y + t*(b->y - a->y) + 0.5;
				p[np++] = q;
				p[np++] = *b;
				a = b;
				a_in = 1;
			} else {
				a = b;
				a_in = 0;
			}
		}
	}

	if( np < 3 )
		return;

	/*
		Clipping of p[0..np-1] against the left side.
		Generating r[0..nr-1].
	*/

	nr = 0;
	a = &p[np-1];
	a_in = (a->x >= x1);
	for( i=0; i < np ; i++ ){
		b = &p[i];
		b_in = (b->x >= x1);
		if( a_in ){
			if( b_in ){
				r[nr++] = *b;
				a = b;
				a_in = 1;
			} else {
				q.x = x1;
				t = (x1 - a->x) / (double)(b->x - a->x);
				q.y = a->y + t*(b->y - a->y) + 0.5;
				r[nr++] = q;
				a = b;
				a_in = 0;
			}
		} else {
			if( b_in ){
				q.x = x1;
				t = (x1 - a->x) / (double)(b->x - a->x);
				q.y = a->y + t*(b->y - a->y) + 0.5;
				r[nr++] = q;
				r[nr++] = *b;
				a = b;
				a_in = 1;
			} else {
				a = b;
				a_in = 0;
			}
		}
	}

	if( nr < 3 )
		return;

	/*
		Clipping of r[0..nr-1] against the bottom side.
		Generating p[0..np-1].
	*/

	np = 0;
	a = &r[nr-1];
	a_in = (a->y <= y2);
	for( i=0; i < nr ; i++ ){
		b = &r[i];
		b_in = (b->y <= y2);
		if( a_in ){
			if( b_in ){
				p[np++] = *b;
				a = b;
				a_in = 1;
			} else {
				t = (y2 - a->y) / (double)(b->y - a->y);
				q.x = a->x + t*(b->x - a->x) + 0.5;
				q.y = y2;
				p[np++] = q;
				a = b;
				a_in = 0;
			}
		} else {
			if( b_in ){
				t = (y2 - a->y) / (double)(b->y - a->y);
				q.x = a->x + t*(b->x - a->x) + 0.5;
				q.y = y2;
				p[np++] = q;
				p[np++] = *b;
				a = b;
				a_in = 1;
			} else {
				a = b;
				a_in = 0;
			}
		}
	}

	if( np < 3 )
		return;

	/*
		Clipping of p[0..np-1] against the top side.
		Generating r[0..nr-1].
	*/

	nr = 0;
	a = &p[np-1];
	a_in = (a->y >= y1);
	for( i=0; i < np ; i++ ){
		b = &p[i];
		b_in = (b->y >= y1);
		if( a_in ){
			if( b_in ){
				r[nr++] = *b;
				a = b;
				a_in = 1;
			} else {
				t = (y1 - a->y) / (double)(b->y - a->y);
				q.x = a->x + t*(b->x - a->x) + 0.5;
				q.y = y1;
				r[nr++] = q;
				a = b;
				a_in = 0;
			}
		} else {
			if( b_in ){
				t = (y1 - a->y) / (double)(b->y - a->y);
				q.x = a->x + t*(b->x - a->x) + 0.5;
				q.y = y1;
				r[nr++] = q;
				r[nr++] = *b;
				a = b;
				a_in = 1;
			} else {
				a = b;
				a_in = 0;
			}
		}
	}

	if( nr < 3 )
		return;

	MakeET(w, r, nr, zinfo);
}


void
Alib_fill3DRect(Alib_Window *w, Alib_Rect *r, Alib_ZInfo * zinfo)
{
	Alib_Point     pts[4];
	Alib_Rect f;

	Alib_intersectRect(r, &w->clip, &f);

	if( Alib_isEmptyRect(&f) )
		return;

	pts[0] = f.a;
	pts[1].x = f.b.x - 1;
	pts[1].y = f.a.y;
	pts[2].x = f.b.x - 1;
	pts[2].y = f.b.y - 1;
	pts[3].x = f.a.x;
	pts[3].y = f.b.y - 1;

	MakeET(w, pts, 4, zinfo);
}


typedef int outcode_t;

#define NONE    0
#define Top    1
#define Bottom 2
#define Left   8
#define Right  4

static outcode_t
CompOutCode(Alib_Window * w, int x, int y)
{

	/*
			 
		+-----------------------------+-> x
		|                             |
		|             Bottom          |
		|      ....................   |
		|      .                  .R  |
		|     L.     w->clip      .i  |
		|     e.                  .g  |
		|     f.     rectangle    .h  |
		|     t.                  .t  |
		|      ....................   |
		|              Top            |
		+-----------------------------+
		|                              (w->width,w->height)
		v

		y
	
		Please note that if the point lies exactly on the Right border
		or the Top border it is considered outside the clipping
		rectangle.

	*/

	outcode_t code = NONE;

	if (y >= w->clip.b.y) {
		code |= Top;
	}
	else if (y < w->clip.a.y) {
		code |= Bottom;
	}

	if (x >= w->clip.b.x) {
		code |= Right;
	}
	else if (x < w->clip.a.x) {
		code |= Left;
	}
	return code;
}


/**
 * Perform the clipping of the segment (x0,y0)-(x1,y1) vs. the w->clip
 * rectangle. Return value:
 * 
 * 	0 = the segment is visible, possibly partially; in this case
 * 	    the returned points are both inside w->clip, i.e.
 * 		w->clip.a.x <= x0,x1 < w->clip.b.x
 * 		w->clip.a.y <= y0,y1 < w->clip.b.y
 * 
 * 	1 = segment not visible at all
 * 
 * Cohen/Sutherland 2D clipping algorithm as described by Foley, van Dam,
 * Feiner and Hughes.
 */
static int
ClipLine(Alib_Window * w, int *x0, int *y0, int *x1, int *y1)
{

	outcode_t outcode0, outcode1, outcodeOut;

	outcode0 = CompOutCode(w, *x0, *y0);
	outcode1 = CompOutCode(w, *x1, *y1);

	do {
		if (outcode0 == 0 && outcode1 == 0) {
			/* Points are both inside the clip rect */
			return 0;
		}
		else if (outcode0 & outcode1) {
			/* Points are both above some side of the clip rect,
			   for ex. both above the Left side */
			return 1;
		}
		else {
			/* At least one point is outside of the clip area */

			int       x, y;

			outcodeOut = outcode0 ? outcode0 : outcode1;

			if (outcodeOut & Top) {
				x = *x0 + (*x1 - *x0) * (w->clip.b.y - 1 - *y0) / (*y1 - *y0);
				y = w->clip.b.y - 1;
			}
			else if (outcodeOut & Bottom) {
				x = *x0 + (*x1 - *x0) * (w->clip.a.y - *y0) / (*y1 - *y0);
				y = w->clip.a.y;
			}
			else if (outcodeOut & Right) {
				y = *y0 + (*y1 - *y0) * (w->clip.b.x - 1 - *x0) / (*x1 - *x0);
				x = w->clip.b.x - 1;
			}
			else {                          /* Left */
				y = *y0 + (*y1 - *y0) * (w->clip.a.x - *x0) / (*x1 - *x0);
				x = w->clip.a.x;
			}

			if (outcodeOut == outcode0) {
				*x0 = x;
				*y0 = y;
				outcode0 = CompOutCode(w, *x0, *y0);
			}
			else {
				*x1 = x;
				*y1 = y;
				outcode1 = CompOutCode(w, *x1, *y1);
			}
		}

	} while (1);
}


void
Alib_draw3DLine(Alib_Window * w, int x1, int y1, int x2, int y2, Alib_ZInfo * zinfo)
{
	int t;
	Edge *e, *e1;

	zinfo->next = NotAnElement;

	if (ClipLine(w, &x1, &y1, &x2, &y2)){
		return;
	}
	
	if (y1 > y2) {
		t = x1;
		x1 = x2;
		x2 = t;
		t = y1;
		y1 = y2;
		y2 = t;
	}

	if ((x1 == x2) || (y1 == y2)) {

		if (w->EPTop + 2 > w->EPSize) {
			fprintf(stderr, "Edge Pool Overflow\n");
			return;
		}

		e = &(w->edgePool[(w->EPTop)++]);
		e1 = &(w->edgePool[(w->EPTop)++]);

		e->y2 = e1->y2 = y2 + 1;

#ifdef FLOAT_SLOPE
		if (x1 == x2)
			e->x1 = e1->x1 = x1;
		else { /* y1 == y2 */
			e->x1 = x1;
			e1->x1 = x2;
		}
		e->Dx = e1->Dx = 0.0;
#else
		if (x1 == x2)
			e->x1 = e1->x1 = (x1 << 16) + 0x8000;
		else { /* y1 == y2 */
			e->x1 = (x1 << 16) + 0x8000;
			e1->x1 = (x2 << 16) + 0x8000;
		}
		e->Dx = e1->Dx = 0;
#endif
		e->p = e1->p = zinfo;

		e1->nexte = w->edges[y1].head;
		e->nexte = e1;
		w->edges[y1].head = e;
	}

/*
 *  A non-trivial case.  There is a slope that is non-zero and not infinity.
 *  We will need one edge entry to hang off the line list at the starting y
 *  value of the line.  We will also pre-allocate two edge entries to be used
 *  as the line'e edges during the EdgeTableToScanLine procedure.  By our own
 *  convention, we'll place them in the two edge pool entries immediately
 *  following the line information entry. 
 */

	else {

		if (w->EPTop + 3 >= w->EPSize) {
			fprintf(stderr, "Edge Pool Overflow\n");
			return;
		}

		e = &(w->edgePool[(w->EPTop)++]);
		w->EPTop += 2;  /* required -- see comment above */

		e->y2 = y2;

#ifdef FLOAT_SLOPE
		e->x1 = x1;
		e->Dx = (x2 - x1) / (double) (y2 - y1);
#else
		e->x1 = (x1 << 16) + 0x8000;
		e->Dx = ((x2 - x1) << 16) / (y2 - y1);
#endif

		e->p = (e + 1)->p = (e + 2)->p = zinfo;

		e->nexte = w->lines[y1].head;
		w->lines[y1].head = e;
	}

	if (y1 < w->ymin)
		w->ymin = y1;

	if (y2 > w->ymax)
		w->ymax = y2;
}


void Alib_setVisibility(Alib_Window *w, double visibility, VColor_Type * haze_color)
{
	w->visibility = visibility;
	w->haze_color = haze_color;
}


Alib_Pixel Alib_computePolygonColor(Alib_Window *w, VPolygon * poly)
{
/*
 *  First, are we seeing the front or the back of this polygon?
 */

	VColor_Type   *c;
	if (poly->flags & PolyUseBackColor) {
		c = poly->backColor;
	}
	else {
		c = poly->color;
	}

/*
 *  If depth cueing isn't turned on, or this color is not a depth-cued
 *  color, then simply return the color index.
 */

	if ((w->flags & Alib_ModeDepthCueing) == 0 || ! VColor_isDepthCueing(c) ) {
		return VColor_getIndex(c);
	}

/*
 *  Okay, it is a depth cued color. Compute the distance of the polygon from
 *  the eye (the origin) and then compute the corresponding depth cued color.
 *  We may take the distance from an arbitrary point of the polygon (for example
 *  the first vertex; fast but some artifacts in rendering due to clipping
 *  against the frustum), or we may compute a middle point (slower, but more
 *  accurate rendering).
 */

	double visibility2 = w->visibility * w->visibility;
	
/*
	// Compute distance from the average point (expensive algo):
	VPoint p = {0.0, 0.0, 0.0};
	for(int i = poly->numVtces - 1; i >= 0; i--){
		VAdd(&p, &poly->vertex[i], &p);
	}
	p.x /= poly->numVtces;
	p.y /= poly->numVtces;
	p.z /= poly->numVtces;
	double d2 = VMagnitude2(&p);
*/
	
	// Compute distance from the first point (fast):
	double d2 = VMagnitude2(&poly->vertex[0]);
	
	if( d2 > visibility2 )
		return VColor_getIndex(w->haze_color);
	// Quite arbitrary blending law, but result good enough anyway:
	double k = 1.0 / (1.0 + 9.0 * d2 / visibility2); // 0 < k <= 1.0
	int r = k * VColor_getRed(c)   + (1.0 - k) * VColor_getRed(w->haze_color);
	int g = k * VColor_getGreen(c) + (1.0 - k) * VColor_getGreen(w->haze_color);
	int b = k * VColor_getBlue(c)  + (1.0 - k) * VColor_getBlue(w->haze_color);
	return gui_getColorIndex(w->gui, r, g, b);
}


void Alib_MatrixIdentity(Alib_Matrix *m)
{
	m->rxx = 1;   m->rxy = 0;
	m->ryx = 0;   m->ryy = 1;
	m->tx = 0;  m->ty = 0;
}


void Alib_MatrixScale(Alib_Matrix *m, double s)
{
	m->rxx *= s;  m->rxy *= s;
	m->ryx *= s;  m->ryy *= s;
	m->tx *= s;  m->ty *= s;
}


void Alib_MatrixRotate(Alib_Matrix *m, double a)
{
	double c = cos(a);
	double s = sin(a);
	double rxx = c * m->rxx + s * m->ryx;
	double rxy = c * m->rxy + s * m->ryy;
	m->ryx = -s * m->rxx + c * m->ryx;
	m->ryy = -s * m->rxy + c * m->ryy;
	m->rxx = rxx;
	m->rxy = rxy;
	m->tx *= c;
	m->ty *= c;
}


void Alib_MatrixTranslate(Alib_Matrix *m, double dx, double dy)
{
	m->tx += dx;
	m->ty += dy;
}


void Alib_MatrixTransformPoint(Alib_Point *p, Alib_Matrix *m, Alib_Point *q)
{
	double qx = m->rxx * p->x + m->rxy * p->y + m->tx;
	q->y = m->ryx * p->x + m->ryy * p->y + m->ty + 0.5;
	q->x = qx + 0.5;
}


#define MAX_TRANSFORMED_POINTS 99

void
Alib_fillPolygonWithMatrix(Alib_Window *w, Alib_Point * pts, int npts, Alib_Matrix *m, Alib_Pixel color)
{
	int i;
	Alib_Point tpts[MAX_TRANSFORMED_POINTS];
	assert( npts <= MAX_TRANSFORMED_POINTS );
	for(i = npts - 1; i >= 0; i--)
		Alib_MatrixTransformPoint(&pts[i], m, &tpts[i]);
	Alib_fillPolygon(w, tpts, npts, color);
}


static void Alib_Polygon_destruct(void *p)
{
	Alib_Polygon *poly = p;
	memory_dispose(poly->pts);
}


Alib_Polygon * Alib_Polygon_new()
{
	Alib_Polygon *poly = memory_allocate(sizeof(Alib_Polygon), Alib_Polygon_destruct);
	poly->allocated_pts = 10;
	poly->npts = 0;
	poly->pts = memory_allocate(poly->allocated_pts * sizeof(Alib_Point), NULL);
	return poly;
}


void Alib_Polygon_addPointXY(Alib_Polygon *poly, int x, int y)
{
	if( poly->npts >= poly->allocated_pts ){
		poly->allocated_pts *= 2;
		poly->pts = memory_realloc(poly->pts, poly->allocated_pts * sizeof(Alib_Point));
	}
	poly->pts[poly->npts++] = (Alib_Point) {x, y};
}


void Alib_Polygon_addPoint(Alib_Polygon *poly, Alib_Point *p)
{
	if( poly->npts >= poly->allocated_pts ){
		poly->allocated_pts *= 2;
		poly->pts = memory_realloc(poly->pts, poly->allocated_pts * sizeof(Alib_Point));
	}
	poly->pts[poly->npts++] = *p;
}


Alib_Polygon * Alib_Polygon_clone(Alib_Polygon *poly)
{
	Alib_Polygon *copy = memory_allocate(sizeof(Alib_Polygon), Alib_Polygon_destruct);
	copy->allocated_pts = poly->npts;
	copy->npts = poly->npts;
	copy->pts = memory_allocate(copy->npts * sizeof(Alib_Point), NULL);
	memcpy(copy->pts, poly->pts, copy->npts * sizeof(Alib_Point));
	return copy;
}
