#include "X.h"
#include "Xmd.h"
#include <dos.h>
#include <time.h>

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

#include "ega.h"
#include "../common/ibmmouse.h"

#include "servermd.h"
#include "../../../os/msdos/msdos.h"
#include "egafuncs.h"
#include "mxfuncs.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
mxSetSpans(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;
{
    u_char		VGA_BASED *pdstBase, VGA_BASED *pdst;	/* 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			iswin;
    egaPrivGC		*pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    void (*func)(u_char *, u_char *, int, int, int) = pPriv->movebits;

    alu = pGC->alu;
    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;
	pdstBase = vga_mem(0);
	widthDst = vga_width;
	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();
	iswin = 1;
    }
    else
    {
	iswin = 0;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
    }

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

    if(fSorted)
    {
#ifdef EGA_HIRES
	if (vga_hires == 1 && iswin) {
	    if (vga_curpage && ppt->y < HIRES_LPP) {
		vga_curpage = 0;
		SetPage();
	    }
	}
#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 + (int)*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 + (int)*pwidth, pbox->x2);
		xEnd -= xStart;
		if (!xEnd)
		    goto skip;
		pdst = pdstBase + widthDst * ppt->y;
#ifdef EGA_HIRES
		if (vga_hires && iswin) {
#ifdef EGA_1280
		    if (vga_hires == 2) {
			u_long bytenum;
			char page;

			bytenum = FP_OFF(pdst) + (xStart >> 3);
			*(u_long *)&pdst = (u_short)bytenum;
			page = bytenum >> 16;
			if (page != vga_curpage) {
			    vga_curpage = page;
			    SetPage();
			}
			page = xStart & 7;
			xStart -= ppt->x;
			if (pdst + (xEnd + page + 7) >> 3 > vga_mem(0xffff)) {
			    (*func)(pdst, psrc, page, xStart,
				((0x10000 - FP_OFF(pdst)) << 3) - page);
			    ++vga_curpage;
			    SetPage();
			    (*func)(pdst, psrc, page, xStart,
				((0x10000 - FP_OFF(pdst)) << 3) - page);
			}
			(*func)(pdst, psrc, page, xStart, xEnd);
		    } else
#endif
		    {
		    if (pdst > vga_mem(0xffff)) {
			pdst -= 0x10000;
			vga_curpage = 1;
			SetPage();
		    }
		    (*func)(pdst, psrc, xStart, xStart - ppt->x, xEnd);
		    }
		} else
#endif
		    (*func)(pdst, psrc, xStart, xStart - ppt->x, xEnd);
skip:
		if(ppt->x + (int)*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 + (int)*pwidth &&
		       pbox->x2 > ppt->x)
		    {
			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + (int)*pwidth);
			if (!xEnd)
			    continue;
			pdst = pdstBase + widthDst * ppt->y;
#ifdef EGA_HIRES
			if (vga_hires && iswin) {
			    if (pdst > vga_mem(0xffff)) {
				pdst -= 0x10000;
				if (!vga_curpage) {
				    vga_curpage = 1;
				    SetPage();
				}
			    } else if (vga_curpage) {
				vga_curpage = 0;
				SetPage();
			    }
			}
#endif
			(*func)(pdst, psrc,
			    xStart, xStart - ppt->x, xEnd-xStart);
		    }

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

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

unsigned char 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;
{
    unsigned char VGA_BASED *pdstBase;
    unsigned char VGA_BASED *pdst;
    unsigned char	*psrcBase;	/* start of src bitmap */
    int 		widthDst;	/* width of bitmap in words */
    int			widthSrc, heightSrc;
    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;
    unsigned		srcx;
    int			xoff;
    int			flags = pPriv->flags;
    PixmapPtr pixmap;
    unsigned char mask, src;
    void (*func)(u_char *, u_char *, int, int, int) = pPriv->FillArea;

    if (pPriv->ppPixmap) {
	pixmap = *pPriv->ppPixmap;
	widthSrc = (unsigned int)(pixmap->devKind);
	heightSrc = pixmap->drawable.height;
	if (flags & EGA_FASTFILL)
	    psrcBase = pixmap->devPrivate.ptr;
    } else
	pixmap = 0;

    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;
	pdstBase = vga_mem(0);
	widthDst = vga_width;
	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;
	}
	ySrc += pGC->patOrg.y;
	xSrc += pGC->patOrg.x;
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
    } else {
	iswin = 0;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
	xSrc = pGC->patOrg.x;
	ySrc = pGC->patOrg.y;
    }
    if (pixmap) {
	while (ySrc > 0)
	    ySrc -= heightSrc;
	while (xSrc > 0)
	    xSrc -= pixmap->drawable.width;
    }

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

    if (fSorted)
    {
#ifdef EGA_HIRES
	if (vga_hires && iswin) {
	    if (vga_curpage && ppt->y < HIRES_LPP) {
		vga_curpage = 0;
		SetPage();
	    }
	}
#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 + (int)*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 + (int)*pwidth, pbox->x2);
		pdst = pdstBase + widthDst * ppt->y;
		xEnd -= xStart;
		if (!xEnd)
			goto skip;
#ifdef EGA_HIRES
		if (vga_hires && iswin) {
		    if (pdst > vga_mem(0xffff)) {
			if (!vga_curpage) {
			    vga_curpage = 1;
			    SetPage();
			}
			pdst -= 0x10000;
		    }
		}
#endif
		if (flags & EGA_FASTFILL) {
		    pdst += (xStart >> 3);
		    xoff = xStart & 7;
		    src = psrcBase[(ppt->y - ySrc) % heightSrc];
		    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 skip;
			pdst++;
		    }
		    xoff = xEnd >> 3;
		    xEnd &= 7;
		    memset(pdst, src, xoff);
		    pdst += xoff;
		    if (xEnd) {
			mask = right_shift[xEnd];
			*pdst = (u_char)((*pdst & mask) | (src & ~mask));
		    }
		} else if (flags & EGA_FILLSOLID) {
		    (*func)(pdst, (u_char *)0, xStart, 0, xEnd);
		} else {
		    psrcBase = (unsigned char *)pixmap->devPrivate.ptr +
			((ppt->y - ySrc) % heightSrc) * widthSrc;
		    srcx = (xStart - xSrc) % pixmap->drawable.width;
		    if (srcx) {
			xoff = min(xEnd, (int)pixmap->drawable.width-srcx);
			(*func)(pdst, psrcBase, xStart, srcx, xoff);
			xEnd -= xoff;
			xStart += xoff;
		    }
		    while (xEnd) {
			xoff = min(xEnd, (int)pixmap->drawable.width);
			(*func)(pdst, psrcBase, xStart, 0, xoff);
			xEnd -= xoff;
			xStart += xoff;
		    }
		}
skip:
		if (ppt->x + (int)*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++;
	    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 + (int)*pwidth &&
		       pbox->x2 > ppt->x)
		    {
			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + (int)*pwidth);
			pdst = pdstBase + widthDst * ppt->y;
			xEnd -= xStart;
			if (!xEnd)
			    continue;
#ifdef EGA_HIRES
			if (vga_hires && iswin) {
			    if (pdst > vga_mem(0xffff)) {
				if (!vga_curpage) {
				    vga_curpage = 1;
				    SetPage();
				}
				pdst -= 0x10000;
			    } else if (vga_curpage) {
				vga_curpage = 0;
				SetPage();
			    }
			}
#endif
			if (flags & EGA_FASTFILL) {
			    pdst += (xStart >> 3);
			    xoff = xStart & 7;
			    src = psrcBase[(ppt->y - ySrc) % heightSrc];
			    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 skip;
				pdst++;
			    }
			    xoff = xEnd >> 3;
			    xEnd &= 7;
			    memset(pdst, src, xoff);
			    pdst += xoff;
			    if (xEnd) {
				mask = right_shift[xEnd];
				*pdst = (u_char)((*pdst & mask) | (src & ~mask));
			    }
			} else if (flags & EGA_FILLSOLID) {
			    (*func)(pdst, (u_char *)0, xStart, 0, xEnd);
			} else {
			    psrcBase = (unsigned char *)pixmap->devPrivate.ptr +
				((ppt->y - ySrc) % heightSrc) * widthSrc;
			    srcx = (xStart - xSrc) % pixmap->drawable.width;
			    if (srcx) {
				xoff = min(xEnd, (int)pixmap->drawable.width-srcx);
				(*func)(pdst, psrcBase, xStart, srcx, xoff);
				xEnd -= xoff;
				xStart += xoff;
			    }
			    while (xEnd) {
				xoff = min(xEnd, (int)pixmap->drawable.width);
				(*func)(pdst, psrcBase, xStart, 0, xoff);
				xEnd -= xoff;
				xStart += xoff;
			    }
			}
		    }
		}
	    }
	ppt++;
	pwidth++;
	}
    }
}

extern char ReduceRop[16][2];
extern int InverseAlu[16];

void
mxPutImage(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 w, h;
{
    int savedRop = pGC->alu;
    int i;

    depth = depth;
    /* This is monochrome.  Therefore there is no difference between
     * XY and Z format. */
    if (format == XYBitmap) {
	/* Change pixel values into either 0 or 1 */
	pGC->fgPixel = !!pGC->fgPixel;
	pGC->bgPixel = !!pGC->bgPixel;
	if (pGC->fgPixel == pGC->bgPixel)
	    pGC->alu = ReduceRop[pGC->alu][pGC->fgPixel];
	else if (!pGC->fgPixel)
	    pGC->alu = InverseAlu[pGC->alu];
	mxValidateGC(pGC, GCFunction, pDst);
	pGC->alu = savedRop;
	pGC->serialNumber |= GC_CHANGE_SERIAL_BIT;
	pGC->stateChanges |= GCFunction;
    }
    if (pDst->type == DRAWABLE_WINDOW && pGC->miTranslate) {
	x += pDst->x;
	y += pDst->y;
    }
    if (!leftPad) {
	int *widths, *wp;
	DDXPointPtr ppt;
	DDXPointPtr pp;

	widths = ALLOCATE_LOCAL(sizeof (int) * h);
	if (!widths)
	    return;
	wp = widths;
	ppt = ALLOCATE_LOCAL(sizeof (*ppt) * h);
	if (!ppt) {
	    DEALLOCATE_LOCAL(widths);
	    return;
	}
	pp = ppt;
	for (i = 0; i < h; i++) {
		*wp++ = w;
		pp->x = x;
		(pp++)->y = y++;
	}
	mxSetSpans(pDst, pGC, pSrc, ppt, widths, h, 1, 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;
	    i = w + leftPad;	/* Ok, it's a bad name. */
	    while (h-- > 0) {
		copybits(line, pSrc, 0, leftPad, w);
		mxSetSpans(pDst, pGC, line, &pt, &w, 1, 1, 0);
		pt.y++;
		pSrc += PixmapWidthInPadUnits(i, 1);
	    }
	    DEALLOCATE_LOCAL(line);
    }
}
