#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 "ega.h"
#include "maskbits.h"
#include "../common/mouse.h"

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

/* 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
egaSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    unsigned char	*psrc;
    register DDXPointPtr ppt;
    int			*pwidth;
    int			nspans;
    Bool		fSorted;
    BoxPtr		limits;
{
    _segment		base;
    unsigned char _based(base)	*pdstBase;	/* start of dst bitmap */
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    register DDXPointPtr pptLast;
    RegionPtr 		prgnDst;
    int			xStart, xEnd;
    int			iswin;
    egaPrivGC		*pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    int (near *func)(u_char *, u_char *, int, int, int) = pPriv->movebits;

    prgnDst = ((egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].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 = egapriv.fb;
	pdstBase = 0;
	widthDst = egapriv.devKind;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} 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();
	/* Set to write mode 0 and read mode 0 */
	outpw(0x3ce, 0x0005);
	/* set data rotate register to 0 */
	outpw(0x3ce, 0x0003);
	iswin = 1;
    }
    else
    {
	iswin = 0;
	base = (u_short)((u_long)(((PixmapPtr)pDrawable)->devPrivate.ptr) >> 16);
	pdstBase = (u_char _based(base) *)(u_short)(u_long)(((PixmapPtr)pDrawable)->devPrivate.ptr);
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
    }

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

    if(fSorted & SPANS_SORTED)
    {
#ifdef EGA_HIRES
	if (egapriv.hires && iswin) {
	    if (egapriv.curpage && ppt->y < HIRES_LPP)
		(*vga_conf.page)(egapriv.curpage = 0);
	}
#endif
    /* 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);
#ifdef EGA_HIRES
		if (egapriv.hires && iswin) {
		    if (!egapriv.curpage && ppt->y >= HIRES_LPP)
			(*vga_conf.page)(egapriv.curpage = 1);
		}
#endif
		(*func)(psrc, (char _far *)(pdstBase + widthDst * ppt->y),
			xStart - ppt->x, xStart, 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, 1);
	    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);
#ifdef EGA_HIRES
			if (egapriv.hires && iswin) {
			    if (ppt->y >= HIRES_LPP) {
				if (!egapriv.curpage)
				    (*vga_conf.page)(egapriv.curpage = 1);
			    } else if (egapriv.curpage)
				(*vga_conf.page)(egapriv.curpage = 0);
			}
#endif
			(*func)(psrc,
			    (char _far *)(pdstBase + widthDst * ppt->y),
				xStart - ppt->x, xStart, xEnd-xStart);
		    }

		}
	    }
	psrc += PixmapWidthInPadUnits(*pwidth, 1);
	ppt++;
	pwidth++;
	}
    }
}

unsigned char near right_shift[8] = {
	0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };

unsigned char near left_shift[8] = {
	0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 };
	

/* 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
mxFillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    int			nspans;
    DDXPointPtr		ppt;
    int			*pwidth;
    Bool		fSorted;
    BoxPtr		limits;
{
    _segment		base;
    unsigned char _based(base) *pdstBase, _based(base) *pdst;
    unsigned char	*psrcBase;	/* start of src bitmap */
    unsigned int 	widthDst;	/* width of bitmap in words */
    unsigned int	widthSrc;
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    DDXPointPtr pptLast;
    RegionPtr 		prgnDst;
    int			xStart, xEnd;
    egaPrivGC		*pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    char		iswin;
    int			xSrc, ySrc;
    int			srcx;
    int			xoff;
    int			flags = pPriv->flags;
    PixmapPtr pixmap;
    unsigned char mask, src;
    unsigned int	heightSrc;
    int (near *func)(u_char *, u_char *, int, int, int) = pPriv->FillArea;

    pixmap = *pPriv->ppPixmap;
    widthSrc = (unsigned int)(pixmap->devKind);
    heightSrc = pixmap->drawable.height;
    if (flags & EGA_FASTFILL)
	psrcBase = pPriv->fillBits;

    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;
	iswin = 1;
	base = egapriv.fb;
	pdstBase = 0;
	widthDst = egapriv.devKind;
	xSrc = pDrawable->x;
	ySrc = pDrawable->y;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else {
		x1 = xSrc;
		y1 = ySrc;
		x2 = xSrc + pDrawable->width;
		y2 = ySrc + pDrawable->height;
	}
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
    } else {
	iswin = 0;
	base = (u_short)((u_long)(((PixmapPtr)pDrawable)->devPrivate.ptr) >> 16);
	pdstBase = (u_char _based(base) *)(u_short)(u_long)(((PixmapPtr)pDrawable)->devPrivate.ptr);
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
	xSrc = 0;
	ySrc = 0;
    }

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

    if (fSorted & SPANS_SORTED)
    {
#ifdef EGA_HIRES
	if (egapriv.hires && iswin) {
	    if (egapriv.curpage && ppt->y < HIRES_LPP)
		(*vga_conf.page)(egapriv.curpage = 0);
	}
#endif
    /* 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(ppt->x, pbox->x1);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		pdst = pdstBase + widthDst * ppt->y;
		xEnd -= xStart;
#ifdef EGA_HIRES
		if (egapriv.hires && iswin &&
		    !egapriv.curpage && ppt->y >= HIRES_LPP)
			(*vga_conf.page)(egapriv.curpage = 1);
#endif
		if (flags & EGA_FASTFILL) {
		    pdst += (xStart >> 3);
		    xoff = ppt->y - ySrc - pGC->patOrg.y;
		    if (xoff < 0)
			xoff = heightSrc - 1 - ((-xoff - 1) % heightSrc);
		    else
			xoff %= heightSrc;
		    src = psrcBase[xoff];
		    xoff = xStart & 7;
		    if (xoff) {
			xEnd -= 8 - xoff;
			mask = right_shift[xoff];
			if (xEnd < 0)
			       mask &= left_shift[-xEnd];
			*pdst = (u_char)((*pdst & ~mask) | (src & mask));
			if (xEnd <= 0)
			       goto next;
			pdst++;
		    }
		    xoff = xEnd >> 3;
		    xEnd &= 7;
		    memset((char _far *)pdst, src, xoff);
		    pdst += xoff;
		    if (xEnd) {
			mask = right_shift[xEnd];
			*pdst = (u_char)((*pdst & mask) | (src & ~mask));
		    }
		} else if (flags & EGA_FILLSOLID) {
		    (*func)((char _far *)0, (char _far *)pdst, 0, xStart, xEnd);
		} else {
		    psrcBase = (unsigned char *)pixmap->devPrivate.ptr +
			((ppt->y - ySrc - pGC->patOrg.y +
			heightSrc) % heightSrc) * widthSrc;
		    srcx = (xStart - xSrc - pGC->patOrg.x +
		    pixmap->drawable.width) % pixmap->drawable.width;
		    if (srcx) {
			xoff = min(xEnd, (int)pixmap->drawable.width-srcx);
			(*func)(psrcBase, (char _far *)pdst,
			    srcx, xStart, xoff);
			xEnd -= xoff;
			xStart += xoff;
		    }
		    while (xEnd) {
			xoff = min(xEnd, (int)pixmap->drawable.width);
			(*func)(psrcBase, (char _far *)pdst, 0, xStart, xoff);
			xEnd -= xoff;
			xStart += xoff;
		    }
		}
		if (ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
next:
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    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);
			pdst = pdstBase + widthDst * (unsigned)ppt->y;
			xEnd -= xStart;
#ifdef EGA_HIRES
			if (egapriv.hires && iswin) {
			    if (ppt->y >= HIRES_LPP) {
				if (!egapriv.curpage)
				    (*vga_conf.page)(egapriv.curpage = 1);
			    } else if (egapriv.curpage)
				(*vga_conf.page)(egapriv.curpage = 0);
			}
#endif
			if (flags & EGA_FILLSOLID) {
			    (*func)(0, (char _far *)pdst, 0, xStart, xEnd);
			} else {
			    psrcBase =
				(unsigned char *)(pixmap->devPrivate.ptr) +
				    ((ppt->y - ySrc - pGC->patOrg.y +
				    heightSrc) % heightSrc) * widthSrc;
			    srcx = (xStart - xSrc - pGC->patOrg.x +
				pixmap->drawable.width) %
				pixmap->drawable.width;
			    if (srcx) {
				xoff = min(xEnd, (int)pixmap->drawable.width-srcx);
				(*func)(psrcBase, (char _far *)pdst,
				    srcx, xStart, xoff);
				xEnd -= xoff;
				xStart += xoff;
			    }
			    while (xEnd) {
				xoff = min(xEnd, (int)pixmap->drawable.width);
				(*func)(psrcBase, (char _far *)pdst,
				    0, xStart, xoff);
				xEnd -= xoff;
				xStart += xoff;
			    }
			}
		    }
		}
	    }
	ppt++;
	pwidth++;
	}
    }
}

egaPutImage(pDst, pGC, depth, x, y, w, h, leftPad, format, pSrc)
DrawablePtr pDst;
GCPtr		pGC;
int depth;
int leftPad, format;
unsigned char *pSrc;
unsigned x, y;
int format;
int w, h;
{
    u_char savedRop = pGC->alu;
    int i;

    if (pDst->type == DRAWABLE_WINDOW && pGC->miTranslate) {
	x += pDst->x;
	y += pDst->y;
    }
    /* This is monochrome.  Therefore there is no difference between
     * XY and Z format. */
    if (format == XYBitmap) {
	if (pGC->fgPixel && pGC->bgPixel)
	    pGC->alu = GXset;
	else if (!pGC->fgPixel && pGC->bgPixel)
	    pGC->alu = GXcopyInverted;
	else if (!pGC->fgPixel && !pGC->bgPixel)
	    pGC->alu = GXclear;
	mxValidateGC(pGC, GCFunction, pDst);
    }
    if (!leftPad) {
	int *widths, *wp;
	DDXPointPtr ppt;
	register DDXPointPtr pp;

	widths = ALLOCATE_LOCAL(sizeof (int) * h);
	if (!widths)
	    return BadAlloc;
	wp = widths;
	ppt = ALLOCATE_LOCAL(sizeof (*ppt) * h);
	if (!ppt) {
	    DEALLOCATE_LOCAL(widths);
	    return BadAlloc;
	}
	pp = ppt;
	for (i = 0; i < h; i++) {
		*wp++ = w;
		pp->x = x;
		(pp++)->y = y++;
	}
	egaSetSpans(pDst, pGC, pSrc, ppt, widths, h, SPANS_SORTED|SPANS_RECT, 0);
	DEALLOCATE_LOCAL(widths);
	DEALLOCATE_LOCAL(ppt);
    } else {
	    DDXPointRec pt;
	    unsigned char *line;

	    line = ALLOCATE_LOCAL(w + 7 >> 3);
	    pt.x = x;
	    pt.y = y;
	    pSrc += leftPad >> 3;
	    i = w + leftPad;	/* Ok, it's a bad name. */
	    while (h-- > 0) {
		copybits(pSrc, line, leftPad & 7, 0, w);
		egaSetSpans(pDst, pGC, line, &pt, &w, 1, 1, 0);
		pt.y++;
		pSrc += PixmapWidthInPadUnits(i, 1);
	    }
	    DEALLOCATE_LOCAL(line);
    }
    pGC->alu = savedRop;
    pGC->serialNumber |= GC_CHANGE_SERIAL_BIT;
    pGC->stateChanges |= GCFunction;
    return 0;
}
