#include "X.h"
#include "Xmd.h"

#include "misc.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "scrnintstr.h"

#include "i8.h"
#include "../common/mouse.h"

#include "servermd.h"
#include "../../../os/msdos/msdos.h"

union place {
	struct {
	    u_char *ptr;
	} pm;
	struct {
		u_short x, y;
	} win;
};
static void _near i8FillSolid(int, union place, int, int, int, int, u_char);
static void _near i8FillTiled(int, union place, int, int, int, int, GCPtr,
	void (near *)(u_char *, u_char *, int, int, int),
	void (near *)(u_char *, u_char *, int));
static void _near i8FillStippled(int, union place, int, int, int, int, GCPtr,
	int, void (near *)(u_char *, u_char *, int, int, int));

/* SetSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
i8SetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    unsigned char	*psrc;
    register DDXPointPtr ppt;
    int			*pwidth;
    int			nspans;
    int			fSorted;
    BoxPtr		limits;
{
    _segment		base;
    u_char		*pdstBase;	/* start of dst bitmap */
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    register DDXPointPtr pptLast;
    int 		alu;
    RegionPtr 		prgnDst;
    int			xStart, xEnd;
    int			depth;
    i8PrivGC		*pPriv = (i8PrivGC *)(pGC->devPrivates[i8GCPrivateIndex].ptr);
    void (near * movebytes)(u_char *, u_char *, int) = pPriv->movebytes;
    void (near * movebits)(u_char *, u_char *, int, int, int) = pPriv->movebits;

    alu = pGC->alu;
    prgnDst = ((i8PrivGC *)(pGC->devPrivates[i8GCPrivateIndex].ptr))->pCompositeClip;

    pptLast = ppt + nspans;

    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	int x1, x2, y1, y2;

	/* quick check to avoid flashing cursor */
	if (!((WindowPtr)pDrawable)->viewable)
		return;
	base = i8priv.fb;
	widthDst = i8priv.devKind;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else if (fSorted & (SPANS_RECT|SPANS_TRECT)) {
		x1 = ppt->x;
		y1 = ppt->y;
		x2 = x1 + *pwidth;
		y2 = y1 + nspans;
	} else {
		/* Turn off cursor anywhere in this window.
		 * That should cover it. */
		x1 = pDrawable->x;
		y1 = pDrawable->y;
		x2 = pDrawable->x + pDrawable->width;
		y2 = pDrawable->y + pDrawable->height;
	}
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
	depth = 0;
    }
    else
    {
	depth = pDrawable->depth;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
    }

    pbox =  REGION_RECTS(prgnDst);
    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);

    if(fSorted)
    {
    /* scan lines sorted in ascending order. Because they are sorted, we
     * don't have to check each scanline against each clip box.  We can be
     * sure that this scanline only has to be clipped to boxes at or after the
     * beginning of this y-band 
     */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    pbox = pboxTest;
	    while(pbox < pboxLast)
	    {
		if(pbox->y1 > ppt->y)
		{
		    /* scanline is before clip box */
		    break;
		}
		else if(pbox->y2 <= ppt->y)
		{
		    /* clip box is before scanline */
		    pboxTest = ++pbox;
		    continue;
		}
		else if(pbox->x1 >= ppt->x + *pwidth) 
		{
		    /* clip box is to right of scanline */
		    break;
		}
		else if(pbox->x2 <= ppt->x)
		{
		    /* scanline is to right of clip box */
		    pbox++;
		    continue;
		}

		/* at least some of the scanline is in the current clip box */
		xStart = max(pbox->x1, ppt->x);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		if (depth == 1) {
		    (*movebits)(psrc,
			pdstBase + widthDst * ppt->y,
			xStart - ppt->x, xStart, xEnd-xStart);
		} else if (depth) {
		    if (alu == GXcopy)
			memcpy(pdstBase + (widthDst * ppt->y + xStart),
			    psrc + (xStart - ppt->x),
			    xEnd-xStart);
		    else
			(*movebytes)(pdstBase + (widthDst * ppt->y + xStart),
			    psrc + (xStart - ppt->x),
			    xEnd-xStart);
		}
		if(ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    ppt++;
	    psrc += PixmapWidthInPadUnits(*pwidth, depth ? depth : 8);
	    pwidth++;
	}
    }
    else
    {
    /* scan lines not sorted. We must clip each line against all the boxes */
	while(ppt < pptLast)
	{
	    if(ppt->y >= 0)
	    {
		
		for(pbox = REGION_RECTS(prgnDst); pbox< pboxLast; pbox++)
		{
		    if(pbox->y1 > ppt->y)
		    {
			/* rest of clip region is above this scanline,
			 * skip it */
			break;
		    }
		    if(pbox->y2 <= ppt->y)
		    {
			/* clip box is below scanline */
			pbox++;
			break;
		    }
		    if(pbox->x1 < ppt->x + *pwidth && pbox->x2 > ppt->x) {
			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + *pwidth);
			if (depth == 1) {
			    (*movebits)(psrc, pdstBase + widthDst * ppt->y,
				xStart - ppt->x, xStart, xEnd-xStart);
			} else if (depth) {
			    (*movebytes)(pdstBase + (widthDst * ppt->y + xStart),
				psrc + (xStart - ppt->x), xEnd-xStart);
			}
		    }
		}
	    }
	    psrc += PixmapWidthInPadUnits(*pwidth, 1);
	    ppt++;
	    pwidth++;
	}
    }
}

/* FillSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
i8FillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    int			nspans;
    DDXPointPtr		ppt;
    int			*pwidth;
    Bool		fSorted;
    BoxPtr		limits;
{
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    DDXPointPtr pptLast;
    RegionPtr 		prgnDst;
    unsigned		xStart, xEnd;
    i8PrivGC		*pPriv = (i8PrivGC *)(pGC->devPrivates[i8GCPrivateIndex].ptr);
    int			depth;
    int			xSrc, ySrc;
    int			alu = pGC->alu;
    int			fgPixel = (int)pGC->fgPixel;
    int			style = pGC->fillStyle;
    int			nlines = 0;
    u_char		pmask = (u_char)pGC->planemask;
    union place place;
    u_char		*pdstBase;
    void (near * movebytes)(u_char *, u_char *, int) = pPriv->movebytes;
    void (near * movebits)(u_char *, u_char *, int, int, int) = pPriv->movebits;

    prgnDst = pPriv->pCompositeClip;
    pptLast = ppt + nspans;
    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	int x1, x2, y1, y2;

	/* quick check to avoid flashing cursor */
	if (!((WindowPtr)pDrawable)->viewable)
		return;
	depth = 0;
	widthDst = i8priv.devKind;
	xSrc = pDrawable->x;
	ySrc = pDrawable->y;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else if (fSorted & (SPANS_RECT|SPANS_TRECT)) {
		x1 = ppt->x;
		y1 = ppt->y;
		x2 = x1 + *pwidth;
		y2 = y1 + nspans;
	} else {
		x1 = xSrc;
		y1 = ySrc;
		x2 = xSrc + pDrawable->width;
		y2 = ySrc + pDrawable->height;
	}
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
	if (style != FillOpaqueStippled || alu != GXcopy || pmask != 0xff)
		fSorted &= ~SPANS_RECT;
    } else {
	depth = pDrawable->depth;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
	xSrc = 0;
	ySrc = 0;
	fSorted &= ~SPANS_RECT;
    }

    pbox =  REGION_RECTS(prgnDst);
    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);

    if (fSorted)
    {
    /* scan lines sorted in ascending order. Because they are sorted, we
     * don't have to check each scanline against each clip box.  We can be
     * sure that this scanline only has to be clipped to boxes at or after the
     * beginning of this y-band 
     */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    pbox = pboxTest;
	    while(pbox < pboxLast)
	    {
		if(pbox->y1 > ppt->y)
		{
		    /* scanline is before clip box */
		    break;
		}
		else if(pbox->y2 <= ppt->y)
		{
		    /* clip box is before scanline */
		    pboxTest = ++pbox;
		    continue;
		}
		else if(pbox->x1 >= ppt->x + *pwidth) 
		{
		    /* clip box is to right of scanline */
		    break;
		}
		else if(pbox->x2 <= ppt->x)
		{
		    /* scanline is to right of clip box */
		    pbox++;
		    continue;
		}

		/* at least some of the scanline is in the current clip box */
		xStart = max(pbox->x1, ppt->x);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		if (fSorted & SPANS_RECT) {
			nlines = min(pbox->y2 - ppt->y, pptLast - ppt);
		}
		if (!depth) {
			place.win.y = ppt->y;
		} else {
			place.pm.ptr = pdstBase + ppt->y * widthDst;
		}

		if (style == FillSolid) {
		    i8FillSolid(depth, place, fgPixel, xStart,
			xEnd-xStart, alu, pmask);
		} else {
		    int srcy = ppt->y - ySrc - pGC->patOrg.y;
		    int srcx = xStart - xSrc - pGC->patOrg.x;
		    if (style == FillTiled) {
			i8FillTiled(depth, place, srcx, xStart, xEnd-xStart,
			    srcy, pGC, movebits, movebytes);
		    } else {
			i8FillStippled(depth, place, srcx, xStart,
			    xEnd-xStart, srcy, pGC, style, movebits);
		    }
		}
		if (ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    if (nlines) {
		ppt += nlines;
		pwidth += nlines;
	    } else {
		ppt++;
		pwidth++;
	    }
	}
    } else {
    /* scan lines not sorted. We must clip each line against all the boxes */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    if(ppt->y >= 0)
	    {
		
		for(pbox = pboxTest; pbox< pboxLast; pbox++)
		{
		    if (pbox->y1 > ppt->y)
		    {
			/* rest of clip region is above this scanline,
			 * skip it */
			break;
		    }
		    if (pbox->y2 <= ppt->y)
		    {
			/* clip box is below scanline */
			continue;
		    }
		    if (pbox->x1 < ppt->x + *pwidth &&
		       pbox->x2 > ppt->x) {

			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + *pwidth);
			if (!depth) {
				place.win.y = ppt->y;
			} else {
				place.pm.ptr = pdstBase + ppt->y * widthDst;
			}

			if (style == FillSolid) {
			    i8FillSolid(depth, place, fgPixel, xStart,
				xEnd-xStart, alu, pmask);
			} else {
			    int srcy = ppt->y - ySrc - pGC->patOrg.y;
			    int srcx = xStart - xSrc - pGC->patOrg.x;
			    if (style == FillTiled) {
				i8FillTiled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC, movebits, movebytes);
			    } else {
				i8FillStippled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC, style, movebits);
			    }
			}
		    }
		}
	    }
	ppt++;
	pwidth++;
	}
    }
}

/* for used with depth 1 pixmaps */

static char ReduceRop[16][2] = {
	{ RROP_BLACK,	RROP_BLACK },	/* GXclear */
	{ RROP_BLACK,	RROP_NOP },	/* GXand */
	{ RROP_BLACK,	RROP_INVERT },	/* GXandReverse */
	{ RROP_BLACK,	RROP_WHITE },	/* GXcopy */
	{ RROP_NOP,	RROP_BLACK },	/* GXandInverted */
	{ RROP_NOP,	RROP_NOP },	/* GXnoop */
	{ RROP_NOP,	RROP_INVERT },	/* GXxor */
	{ RROP_NOP,	RROP_WHITE },	/* GXor */
	{ RROP_INVERT,	RROP_BLACK },	/* GXnor */
	{ RROP_INVERT,	RROP_NOP },	/* GXequiv */
	{ RROP_INVERT,	RROP_INVERT },	/* GXinvert */
	{ RROP_INVERT,	RROP_WHITE },	/* GXorReverse */
	{ RROP_WHITE,	RROP_BLACK },	/* GXcopyInverted */
	{ RROP_WHITE,	RROP_NOP },	/* GXorInverted */
	{ RROP_WHITE,	RROP_INVERT },	/* GXnand */
	{ RROP_WHITE,	RROP_WHITE }	/* GXset */
};

static void _near
i8FillSolid(depth, place, pixel, doff, n, alu, pmask)
int depth;
union place place;
int pixel;
int doff, n, alu;
u_char pmask;
{
    pixel =  ((u_short)pixel << 8) | ((u_char)pixel & 0xff);

    if (depth == 1) {
	int rrop;
	u_char mask;

	rrop = ReduceRop[alu][pixel ? 1 : 0];
	if (rrop == RROP_NOP)
	    return;
	place.pm.ptr += doff >> 3;
	doff &= 7;
	if (doff) {
	    mask = (u_char)(0xff >> doff);
	    doff = 8 - doff;
	    if (n < doff) {
		mask &= (u_char)(0xff << (doff - n));
		n = 0;
	    } else
		n -= doff;
	    if (rrop == RROP_WHITE)
		*place.pm.ptr++ |= mask;
	    else if (rrop == RROP_BLACK)
		*place.pm.ptr++ &= ~mask;
	    else
		*place.pm.ptr++ ^= mask;
	}
	doff = n >> 3;
	if (doff) {
	    if (rrop == RROP_WHITE) {
		memset(place.pm.ptr, 0xff, doff);
		place.pm.ptr += doff;
	    } else if (rrop == RROP_BLACK) {
		memset(place.pm.ptr, 0, doff);
		place.pm.ptr += doff;
	    } else {
		while (doff--) {
		    *place.pm.ptr = (u_char)~*place.pm.ptr;
		    place.pm.ptr++;
		}
	    }
	}
	n &= 7;
	if (n) {
	    mask = (u_char)(0xff >> n);
	    if (rrop == RROP_WHITE)
		*place.pm.ptr |= ~mask;
	    else if (rrop == RROP_BLACK)
		*place.pm.ptr &= mask;
	    else
		*place.pm.ptr ^= ~mask;
	}
    } else if (depth) {
	movebyte(place.pm.ptr, pixel, n, alu, pmask);
    }
}

/*
 * Copy one scan line of a tile into a drawable, which may be another
 * pixmap or a window.
 */
static void _near
i8FillTiled(depth, place, soff, doff, n, srcy, pGC, movebits, movebytes)
int depth;
union place place;	/* where to put it */
int soff, doff, n, srcy;
GCPtr pGC;
void (near *movebits)(u_char *, u_char *, int, int, int);
void (near *movebytes)(u_char *, u_char *, int);
{
    PixmapPtr pixmap = pGC->tile.pixmap;
    int alu = pGC->alu;
    u_char *psrcBase, *psrc;
    int width = pixmap->drawable.width;

    soff += width;
    soff %= width;
    srcy += pixmap->drawable.height;
    srcy %= pixmap->drawable.height;

    psrcBase = pixmap->devPrivate.ptr;
    psrc = psrcBase + pixmap->devKind * srcy;
    srcy = pixmap->drawable.height;

    if (depth == 1) {		/* Bitmap */
	u_char *pdst = place.pm.ptr;
	int i;

	if (soff) {
		i = min(n, width - soff);
		(*movebits)(psrc, pdst, soff, doff, i);
		n -= i;
		doff += i;
		doff &= 7;
	}
	while (n) {
		i = min(n, width);
		(*movebits)(psrc, pdst, 0, doff, i);
		n -= i;
		doff += i;
	}
    } else if (depth) {			/* Pixmap */
	u_char *pdst = place.pm.ptr + doff;

	if (soff) {
		doff = min(n, width - soff);
		(*movebytes)(pdst, psrc + soff, doff);
		n -= doff;
		pdst += doff;
	}
	while (n) {
		soff = min(n, width);
		if (alu == GXcopy)
		    memcpy(pdst, psrc, soff);
		else
		    (*movebytes)(pdst, psrc, soff);
		n -= soff;
		pdst += soff;
	}
    }
}

/*
 * pixmap is of depth 1.
 */
static void _near
i8FillStippled(depth, place, soff, doff, n, srcy, pGC, style, movebits)
int depth;
union place place;
int soff, doff, n, srcy;
GCPtr pGC;
int style;
void (near *movebits)(u_char *, u_char *, int, int, int);
{
    PixmapPtr bitmap = pGC->stipple;
    int alu = pGC->alu;
    u_char *psrcBase, *psrc;
    int width = bitmap->drawable.width;
    u_char fgPixel = (u_char)pGC->fgPixel;
    u_char bgPixel = (u_char)pGC->bgPixel;
    u_char pmask = (u_char)pGC->planemask;

    soff += width;
    soff %= width;
    srcy += bitmap->drawable.height;
    srcy %= bitmap->drawable.height;

    psrcBase = bitmap->devPrivate.ptr;
    psrc = psrcBase + srcy * bitmap->devKind;

    if (depth == 1) {		/* Bitmap */
	int fgRrop, bgRrop;
	u_char *pdst = place.pm.ptr + (doff >> 3);

	doff &= 7;
	fgRrop = ReduceRop[alu][fgPixel ? 1 : 0];
	bgRrop = style == FillOpaqueStippled ?
	    ReduceRop[alu][bgPixel ? 1 : 0] : RROP_NOP;
	if (fgRrop == bgRrop) {
	    i8FillSolid(depth, place, fgPixel, doff, n, alu, 0);
	} else {
	    int i;

	    if (style == FillOpaqueStippled) {
		if (bgPixel)
			alu = InverseAlu[alu];
	    } else {
		alu = fgRrop == RROP_WHITE  ? GXor :
			 fgRrop == RROP_BLACK  ? GXandInverted :
			 fgRrop == RROP_INVERT ? GXxor :
			 GXnoop;
	    }
	    if (soff) {
		i = min(n, width - soff);
		(*movebits)(psrc, pdst, soff, doff, i);
		n -= i;
		doff += i;
	    }
	    while (n) {
		i = min(n, width);
		(*movebits)(psrc, pdst, 0, doff, i);
		n -= i;
		doff += i;
	    }
	}
    } else if (depth) {			/* Pixmap */
	u_char *pdst = place.pm.ptr + doff;
	u_char mask;
	u_char *pSrc;

	pSrc = psrc + (soff >> 3);
	soff &= 7;
	mask = masktab[soff];
	while (n) {
	    soff = min(n, width - soff);
	    n -= soff;
	    while (soff--) {
		if (*pSrc & mask)
		    movebyte(pdst, fgPixel, 1, alu, pmask);
		else if (style == FillOpaqueStippled)
		    movebyte(pdst, bgPixel, 1, alu, pmask);
		pdst++;
		mask >>= 1;
		if (!mask) {
			mask = 0x80;
			pSrc++;
		}
	    }
	    soff = 0;
	    mask = 0x80;
	    pSrc = psrc;
	}
    }
}
