#include "X.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "ega.h"
#include "../../mi/mistruct.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "../common/mouse.h"
#include "../../../os/msdos/msdos.h"

typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned long u_long;

Bool
egaCreateWindow(pWin)
WindowPtr pWin;
{
	return TRUE;
}

Bool
egaDestroyWindow(pWin)
WindowPtr pWin;
{
#ifdef BACKING_STORE
	if (pWin->backingStore != NotUseful) {
		miFreeBackingStore(pWin);
	}
#endif
	return TRUE;
}

Bool
egaPositionWindow(pWin, x, y)
WindowPtr pWin;
int x, y;
{
	return TRUE;
}

Bool
egaMapWindow(pWin)
WindowPtr pWin;
{
	return TRUE;
}

Bool
egaUnmapWindow(pWin)
WindowPtr pWin;
{
	return TRUE;
}

void 
egaCopyWindow(pWin, ptOldOrg, prgnSrc)
    WindowPtr pWin;
    DDXPointRec ptOldOrg;
    RegionPtr prgnSrc;
{
    RegionPtr prgnDst;
    int dx, dy, width, yinc;
    int i, j;
    _segment base = ((egaPrivScr *)(pWin->drawable.pScreen->devPrivate))->fb;
    unsigned char _based(base) *psrc, _based(base) *pdst;
    RegBoxPtr pbox;
    int y, xMax, yMax;
    int xMin, yMin, nlines;
    Bool revx = 0;	/* True if moving right and stationary vertical */
    Bool revy = 0;
    char *buf = 0;
    unsigned int	*ordering;

    dx = pWin->drawable.x - ptOldOrg.x;
    dy = pWin->drawable.y - ptOldOrg.y;
    prgnDst = (* pWin->drawable.pScreen->RegionCreate)(NullBox, 
					       REGION_NUM_RECTS(&pWin->borderClip));

    (* pWin->drawable.pScreen->TranslateRegion)(prgnSrc, dx, dy);
    (* pWin->drawable.pScreen->Intersect)(prgnDst, &pWin->borderClip, prgnSrc);

    ordering = (unsigned int *)
        ALLOCATE_LOCAL(REGION_NUM_RECTS(prgnDst) * sizeof(unsigned int));
    if (!ordering)
	goto out;

    width = pWin->borderWidth * 2;
    /* find out which direction to scroll */
    /* if no overlap, it doesn't matter which direction, so stop now */
    if ((!egapriv.hires &&
	(unsigned)abs(dx) >= pWin->drawable.width + width) ||
	(unsigned)abs(dy) >= pWin->drawable.height + width)
	for (i=0; i < REGION_NUM_RECTS(prgnDst); i++)
	    ordering[i] = i;
    else {
      if (dy <= 0) { /* Scroll up or stationary vertical.
                                  Vertical order OK */
        if (dx <= 0) { /* Scroll left or stationary horizontal.
                                  Horizontal order OK as well */
          for (i=0; i < REGION_NUM_RECTS(prgnDst); i++)
            ordering[i] = i;
	  if (dy == 0 && -dx < 8)
		revx = TRUE;
        } else { /* scroll right. must reverse horizontal banding of rects. */
          for (i=0, j=1, xMax=0;
               i < REGION_NUM_RECTS(prgnDst);
               j=i+1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((j < REGION_NUM_RECTS(prgnDst)) &&
                   (REGION_RECTS(prgnDst)[j].y1 == y))
              j++;
            /* reverse the horizontal band in the output ordering */
            for (j-- ; j >= xMax; j--, i++)
              ordering[i] = j;
          }
        }
	revx = dy == 0;
      }
      else { /* Scroll down. Must reverse vertical banding. */
        if (dx < 0) { /* Scroll left. Horizontal order OK. */
          for (i=REGION_NUM_RECTS(prgnDst)-1, j=i-1, xMax=i, yMax=0;
              i >= 0;
              j=i-1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((j >= 0) &&
                   (REGION_RECTS(prgnDst)[j].y1 == y))
              j--;
            /* reverse the horizontal band in the output ordering */
            for (j++ ; j <= xMax; j++, i--, yMax++)
              ordering[yMax] = j;
          }
        }
        else /* Scroll right or horizontal stationary.
                Reverse horizontal order as well (if stationary, horizontal
                order can be swapped without penalty and this is faster
                to compute). */
          for (i=0, j=REGION_NUM_RECTS(prgnDst)-1;
               i < REGION_NUM_RECTS(prgnDst);
               i++, j--)
              ordering[i] = j;
	revy = 1;
      }
    }

#ifndef EGA_HIRES
    if (revx)
#endif
    {
	    j = (pWin->drawable.width + width + 15) / 8;
	    buf = ALLOCATE_LOCAL(j);
	    if (!buf) {
		DEALLOCATE_LOCAL(ordering);
		goto out;
	    }
    }

    width = ((egaPrivScr *)(pWin->drawable.pScreen->devPrivate))->devKind;
    if (revy)
	yinc = -width;
    else
	yinc = width;

    /* Turn cursor off while we do the copy.  We could take the extent
     * of prgnDst and extend it by dy and dx, but windows are not moved
     * that often and this is a lot easier.
     */
    HideCursor();

    for (i = 0; i < REGION_NUM_RECTS(prgnDst); i++) {
	pbox = &REGION_RECTS(prgnDst)[ordering[i]];
	xMin = pbox->x1;
	xMax = pbox->x2;
	yMin = pbox->y1;
	yMax = pbox->y2;
	if (xMax <= xMin || yMax <= yMin)
	    continue;
	xMax -= xMin;
#ifdef EGA_HIRES
	while (nlines = yMax - yMin)
#else
	nlines = yMax - yMin;
#endif
	{
	    if (revy) {
		y = yMax - 1;
#ifdef EGA_HIRES
		/* check if source overlaps */
		if (yMin - dy < HIRES_LPP && yMax - dy > HIRES_LPP)
		    nlines = yMax - dy - HIRES_LPP;
		/* else check dest */
		else if (yMin < HIRES_LPP && yMax > HIRES_LPP)
		    nlines = yMax - HIRES_LPP;
		yMax -= nlines;
#endif
	    } else {
		y = yMin;
#ifdef EGA_HIRES
		/* check if source overlaps */
		if (yMin - dy < HIRES_LPP && yMax - dy > HIRES_LPP)
		    nlines = HIRES_LPP - (yMin - dy);
		/* else check dest */
		else if (yMin < HIRES_LPP && yMax > HIRES_LPP)
		    nlines = HIRES_LPP - yMin;
		yMin += nlines;
#endif
	    }
	    psrc = (char _based(base) *)0 + (width * (y - dy));
	    pdst = (char _based(base) *)0 + (width * y);
#ifdef EGA_HIRES
	    if (egapriv.hires) { /* set starting page */
		if (y - dy < HIRES_LPP) {
		    if (egapriv.curpage)
			(*vga_conf.page)(egapriv.curpage = 0);
		} else if (!egapriv.curpage)
		    (*vga_conf.page)(egapriv.curpage = 1);
		/* test if src and dest are on different pages */
		if (dy)
		    revx = y < HIRES_LPP == egapriv.curpage;
	    }
#endif
	    if (revx) {
		psrc += (xMin - dx) >> 3;
		while (nlines--) {
		    memcpy(buf, (char _far *)psrc, j);
#ifdef EGA_HIRES
		    if (egapriv.hires && dy) {
			egapriv.curpage ^= 1;
			(*vga_conf.page)(egapriv.curpage);
		    }
#endif
		    copybits(buf, (char _far *)pdst, (xMin-dx)&7, xMin, xMax);
#ifdef EGA_HIRES
		    if (egapriv.hires && dy) {
			egapriv.curpage ^= 1;
			(*vga_conf.page)(egapriv.curpage);
		    }
#endif
		    psrc += yinc;
		    pdst += yinc;
		}
	    } else {
		while (nlines--) {
		    copybits((char _far *)psrc, (char _far *)pdst,
			xMin-dx, xMin, xMax);
		    psrc += yinc;
		    pdst += yinc;
		}
	    }
	}
    }
    if (buf)
	DEALLOCATE_LOCAL(buf);
    DEALLOCATE_LOCAL(ordering);
#ifdef DIRECT_MOUSE
    /* turn cursor back on */
    ShowCursor();
#endif
out:
    (* pWin->drawable.pScreen->RegionDestroy)(prgnDst);
}

/* convert a X11 operation to a EGA hardware operation
 * -1 means we can't take the shortcut.  */
char GXtoEGA[] = {
	EGA_COPY|EGA_ZERO,	/* GXclear */
	EGA_AND,		/* GXand */
	-1,			/* GXandReverse */
	EGA_COPY,		/* GXcopy */
	EGA_AND|EGA_INVERT,	/* GXandInverted */
	EGA_OR|EGA_ZERO,	/* GXnoop */
	EGA_XOR,		/* GXxor */
	EGA_OR,			/* GXor */
	-1,			/* GXnor */
	EGA_XOR|EGA_INVERT,	/* GXequiv */
	EGA_XOR|EGA_ONE,	/* GXinvert */
	-1,			/* GXorReverse */
	EGA_COPY|EGA_INVERT,	/* GXcopyInverted */
	EGA_OR|EGA_INVERT,	/* GXorInverted */
	-1,			/* GXnand */
	EGA_COPY|EGA_ONE,	/* GXset */
};

RegionPtr
egaCopyArea(pSrcDrawable, pDstDrawable,
	    pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut)
    register DrawablePtr 	pSrcDrawable;
    register DrawablePtr 	pDstDrawable;
    GCPtr 			pGC;
    int 			xIn, yIn;
    int 			widthSrc, heightSrc;
    int 			xOut, yOut;
{
    BoxRec 		dstBox, *prect;
    			/* may be a new region, or just a copy */
    RegionPtr 		prgnDst, prgnSrc, prgnNew=0;
    int			y;
    int			dx, dy, i, j, srcx, srcy,
    			xMin, xMax, yMin, yMax;
    unsigned int	*ordering;
    int			iswin=0;	/* true if either is window */
    Bool revx = 0;	/* True if scrollling right and stationary vertical */
    Bool revy = 0;	/* True if scrolling down */
    _segment srcbase, dstbase;
    unsigned char	_based(srcbase) *psrcBase, _based(dstbase) *pdstBase;
    unsigned char	*buf = 0;
    int			srcWidth, dstWidth;
    Bool		realSrcClip=0;
    WindowPtr		pWin=0;
    egaPrivGC		*pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    void (near *func)(u_char *, u_char *, int, int, int) = pPriv->movebits;
#ifdef EGA_HIRES
    Bool copy = 0;
#endif

    srcx = xIn;
    srcy = yIn;

    /* If the destination isn't realized, this is easy */
    if (pDstDrawable->type == DRAWABLE_WINDOW &&
	!((WindowPtr)pDstDrawable)->realized)
    {
	return NULL;
    }

    if (pSrcDrawable->type == DRAWABLE_WINDOW)
    {
	egaPrivScr *pScr =
		(egaPrivScr *)(pSrcDrawable->pScreen->devPrivate);

	srcx += pSrcDrawable->x;
	srcy += pSrcDrawable->y;
	iswin |= 2;
	srcWidth = pScr->devKind;
	srcbase = pScr->fb;
	psrcBase = 0;
	if (pGC->subWindowMode == IncludeInferiors)
	{
	    prgnSrc = NotClippedByChildren((WindowPtr)pSrcDrawable);
	    realSrcClip = 1;
	}
	else
	{
	    prgnSrc = &((WindowPtr)pSrcDrawable)->clipList;
	}
	if (pDstDrawable->type != DRAWABLE_WINDOW ||
	    pDstDrawable == pSrcDrawable)
		pWin = (WindowPtr)pSrcDrawable;
    } else {
	srcWidth = ((PixmapPtr)pSrcDrawable)->devKind;
	srcbase = (u_short)((u_long)(((PixmapPtr)pSrcDrawable)->devPrivate.ptr) >> 16);
	psrcBase = (u_char _based(srcbase) *)(u_short)(u_long)(((PixmapPtr)pSrcDrawable)->devPrivate.ptr);
	prgnSrc = 0;
	if (pDstDrawable->type == DRAWABLE_WINDOW)
		pWin = (WindowPtr)pDstDrawable;
    }

    prgnDst = ((egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr))->pCompositeClip;

    if (pDstDrawable->type == DRAWABLE_WINDOW)
    {
	egaPrivScr *pScr =
		(egaPrivScr *)(pDstDrawable->pScreen->devPrivate);

	dx = xOut + pDstDrawable->x;
	dy = yOut + pDstDrawable->y;
	iswin |= 1;
	dstWidth = pScr->devKind;
	dstbase = pScr->fb;
	pdstBase = 0;
    }
    else
    {
	dx = xOut;
	dy = yOut;
	dstWidth = ((PixmapPtr)pDstDrawable)->devKind;
	dstbase = (u_short)((u_long)(((PixmapPtr)pDstDrawable)->devPrivate.ptr) >> 16);
	pdstBase = (u_char _based(dstbase) *)(u_short)(u_long)(((PixmapPtr)pDstDrawable)->devPrivate.ptr);
    }

    /* If the dst drawable is a window, we need to translate the dstBox so
     * that we can compare it with the window's clip region later on. */
    dstBox.x1 = dx;
    dstBox.y1 = dy;
    dstBox.x2 = dx  + widthSrc;
    dstBox.y2 = dy  + heightSrc;

    dy -= srcy;
    dx -= srcx;

    if (prgnSrc) {
	prgnNew = 
	    (pDstDrawable->pScreen->RegionCreate)((BoxPtr)0, REGION_NUM_RECTS(prgnSrc));
	(pDstDrawable->pScreen->RegionCopy)(prgnNew, prgnSrc);
	(pDstDrawable->pScreen->TranslateRegion)(prgnNew, dx, dy);
	(pDstDrawable->pScreen->Intersect)(prgnNew, prgnNew, prgnDst);
	prgnDst = prgnNew;
    }

    ordering = (unsigned int *)
        ALLOCATE_LOCAL(REGION_NUM_RECTS(prgnDst) * sizeof(unsigned int));
    if(!ordering)
    {
       return NULL;
    }

    /* If not the same drawable then order of move doesn't matter.
       Following assumes that prgnDst->rects are sorted from top
       to bottom and left to right.
    */
    if ((pSrcDrawable != pDstDrawable) &&
	((pGC->subWindowMode != IncludeInferiors) ||
	 (pSrcDrawable->type == DRAWABLE_PIXMAP) ||
	 (pDstDrawable->type == DRAWABLE_PIXMAP)))
      for (i=0; i < REGION_NUM_RECTS(prgnDst); i++)
        ordering[i] = i;
    else { /* within same drawable, must sequence moves carefully! */
      if (dy <= 0) { /* Scroll up or stationary vertical.
                                  Vertical order OK */
        if (dx <= 0) { /* Scroll left or stationary horizontal.
                                  Horizontal order OK as well */
          for (i=0; i < REGION_NUM_RECTS(prgnDst); i++)
            ordering[i] = i;
        } else { /* scroll right. must reverse horizontal banding of rects. */
	  revx = !dy;
          for (i=0, j=1, xMax=0;
               i < REGION_NUM_RECTS(prgnDst);
               j=i+1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((j < REGION_NUM_RECTS(prgnDst)) &&
                   (REGION_RECTS(prgnDst)[j].y1 == y))
              j++;
            /* reverse the horizontal band in the output ordering */
            for (j-- ; j >= xMax; j--, i++)
              ordering[i] = j;
          }
        }
      }
      else { /* Scroll down. Must reverse vertical banding. */
	revy = TRUE;
        if (dx < 0) { /* Scroll left. Horizontal order OK. */
          for (i=REGION_NUM_RECTS(prgnDst)-1, j=i-1, yMin=i, yMax=0;
              i >= 0;
              j=i-1, yMin=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((j >= 0) &&
                   (REGION_RECTS(prgnDst)[j].y1 == y))
              j--;
            /* reverse the horizontal band in the output ordering */
            for (j++ ; j <= yMin; j++, i--, yMax++)
              ordering[yMax] = j;
          }
        }
        else /* Scroll right or horizontal stationary.
                Reverse horizontal order as well (if stationary, horizontal
                order can be swapped without penalty and this is faster
                to compute). */
          for (i=0, j=REGION_NUM_RECTS(prgnDst)-1;
               i < REGION_NUM_RECTS(prgnDst);
               i++, j--)
              ordering[i] = j;
      }
    }

#ifndef EGA_HIRES
    if (revx)
#endif
    {
	    buf = ALLOCATE_LOCAL((dstBox.x2 - dstBox.x1 + 7 >> 3) + 1);
	    if (!buf) {
		DEALLOCATE_LOCAL(ordering);
		return NULL;
	    }
    }
	    
    if (iswin) {
#ifdef DIRECT_MOUSE
	if (iswin != 3 || pSrcDrawable == pDstDrawable) {
	    ConditionalOff(pWin->drawable.x, pWin->drawable.y,
		pWin->drawable.x + pWin->drawable.width,
		pWin->drawable.y + pWin->drawable.height);
	} else
	    HideCursor();
#else
	if (iswin != 3 || pSrcDrawable == pDstDrawable) {
	    if (CUR_OVERLAP(pWin->drawable.x, pWin->drawable.y,
		pWin->drawable.x + pWin->drawable.width,
		pWin->drawable.y + pWin->drawable.height))
		    HideCursor();
	} else
		HideCursor();
#endif
    }
 
     for(i = 0;
         i < REGION_NUM_RECTS(prgnDst);
         i++)
     {
        prect = &REGION_RECTS(prgnDst)[ordering[i]];
  	xMin = max(prect->x1, dstBox.x1);
  	xMax = min(prect->x2, dstBox.x2);
  	yMin = max(prect->y1, dstBox.y1);
	yMax = min(prect->y2, dstBox.y2);
	/* is there anything visible here? */
	if(xMax <= xMin || yMax <= yMin)
	    continue;

	srcx = xMin-dx;

	if (revy) {
#ifdef EGA_HIRES
	    if (egapriv.hires && iswin)
		    (*vga_conf.page)(egapriv.curpage = 1);
#endif
	    /* Copying from bottom up */
	    for (y = yMax-1; y >= yMin; y--) {
#ifdef EGA_HIRES
		if (egapriv.hires) {
		    switch (iswin) {
			case 1:
			    if (egapriv.curpage && y < HIRES_LPP)
				(*vga_conf.page)(egapriv.curpage = 0);
			    break;
			case 2:
			    if (egapriv.curpage && y-dy < HIRES_LPP)
				(*vga_conf.page)(egapriv.curpage = 0);
			    break;
			case 3:
			    if (y-dy < HIRES_LPP) {
				if (egapriv.curpage)
				    (*vga_conf.page)(egapriv.curpage = 0);
				copy = y >= HIRES_LPP;
			    }
			    break;
		    }
		}
		if (copy) {
		    memcpy(buf,
			(char _far *)(psrcBase + (y-dy) * srcWidth + (srcx >> 3)),
			(xMax - xMin + 7 >> 3) + 1);
		    (*vga_conf.page)(egapriv.curpage = 1);
		    (*func)(buf,
			(char _far *)(pdstBase + y * dstWidth),
			srcx & 7, xMin, xMax - xMin);
		} else
#endif
		(*func)((char _far *)(psrcBase + (y-dy) * srcWidth),
		    (char _far *)(pdstBase + y * dstWidth),
		    srcx, xMin, xMax - xMin);
	    }
	} else {
#ifdef EGA_HIRES
	    if (egapriv.hires)
		(*vga_conf.page)(egapriv.curpage = 0);
#endif

	    for (y = yMin; y < yMax; y++) {
#ifdef EGA_HIRES
		if (egapriv.hires)
		    switch (iswin) {
			case 1:
			    if (!egapriv.curpage && y >= HIRES_LPP)
				(*vga_conf.page)(egapriv.curpage = 1);
			    break;
			case 2:
			    if (!egapriv.curpage && y-dy >= HIRES_LPP)
				(*vga_conf.page)(egapriv.curpage = 1);
			    break;
			case 3:
			    if (y-dy >= HIRES_LPP) {
				if (!egapriv.curpage)
				    (*vga_conf.page)(egapriv.curpage = 1);
				copy = y < HIRES_LPP;
			    }
			    break;
		    }
#endif
		if (revx
#ifdef EGA_HIRES
		    || copy
#endif
			) {
		    memcpy(buf,
			(char _far *)(psrcBase + (y-dy) * srcWidth + (srcx >> 3)),
			(xMax - xMin + 7 >> 3) + 1);
#ifdef EGA_HIRES
		if (egapriv.hires && copy)
		    (*vga_conf.page)(egapriv.curpage = 0);
#endif
		    (*func)(buf, (char _far *)(pdstBase + y * dstWidth),
			srcx & 7, xMin, xMax - xMin);
		} else
		    (*func)((char _far *)(psrcBase + (y-dy) * srcWidth),
			(char _far *)(pdstBase + y * dstWidth),
			srcx, xMin, xMax - xMin);
	    }
	}
    }
    if (buf)
	DEALLOCATE_LOCAL(buf);
#ifdef DIRECT_MOUSE
    if (iswin)
	ShowCursor();
#endif
    prgnDst = miHandleExposures(pSrcDrawable, pDstDrawable, pGC, xIn, yIn,
		      widthSrc, heightSrc, xOut, yOut, 0L);
    if (prgnNew)
	(*pGC->pScreen->RegionDestroy)(prgnNew);
	
    if(realSrcClip)
	(*pGC->pScreen->RegionDestroy)(prgnSrc);

    DEALLOCATE_LOCAL(ordering);
    return prgnDst;
}

Bool
egaChangeWindowAttributes(pWin, mask)
WindowPtr pWin;
unsigned long mask;
{

    return(TRUE);
}

extern u_char ReduceRop[16][2];
extern u_char InverseAlu[16];

RegionPtr
egaCopyPlane(pSrcDrawable, pDstDrawable,
	    pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut, plane)
    register DrawablePtr 	pSrcDrawable;
    register DrawablePtr 	pDstDrawable;
    GCPtr 			pGC;
    int 			xIn, yIn;
    int 			widthSrc, heightSrc;
    int 			xOut, yOut;
    unsigned long		plane;
{
	int alu;
	RegionPtr prgnExposed;

	if (plane != 1)
		return NULL;
	if (pGC->fgPixel && !pGC->bgPixel)
	    prgnExposed = (*pGC->ops->CopyArea)(pSrcDrawable, pDstDrawable,
		pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut);
	else if (pGC->fgPixel == pGC->bgPixel) {
	    alu = pGC->alu;
	    pGC->alu = ReduceRop[alu][pGC->fgPixel ? 1 : 0];
	    mxValidateGC(pGC, GCFunction, pDstDrawable);
	    prgnExposed = (*pGC->ops->CopyArea)(pSrcDrawable, pDstDrawable,
		pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut);
	    pGC->alu = alu;
	    pGC->serialNumber |= GC_CHANGE_SERIAL_BIT;
	    pGC->stateChanges |= GCFunction;
	} else {
	    alu = pGC->alu;
	    pGC->alu = InverseAlu[alu];
	    mxValidateGC(pGC, GCFunction, pDstDrawable);
	    prgnExposed = (*pGC->ops->CopyArea)(pSrcDrawable, pDstDrawable,
		pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut);
	    pGC->alu = alu;
	    pGC->serialNumber |= GC_CHANGE_SERIAL_BIT;
	    pGC->stateChanges |= GCFunction;
	}
	return prgnExposed;
}
