#include "X.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "i8.h"
#include "../../mi/mistruct.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "../common/mouse.h"
#include "../../../os/msdos/msdos.h"
#include <string.h>

RegionPtr
i8CopyArea(pSrcDrawable, pDstDrawable,
	    pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut)
    DrawablePtr 	pSrcDrawable;
    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			depth = pSrcDrawable->depth;
    int			iswin = 0;
    Bool revx = 0;	/* True if scrollling right and stationary vertical */
    Bool revy = 0;	/* True if scrolling down */
    _segment base = i8priv.fb;	/* used for screens */
    u_char	*pm_psrcBase, *pm_pdstBase;	/* pointers for pixmaps */
    int			srcWidth, dstWidth;
    int			alu = pGC->alu;
    Bool		realSrcClip=0;
    WindowPtr		pWin=0;
    /* TODO: figure out if src needs to be used at all (for instance if
     * alu == GXinvert the only dst is used. */
    Bool		constant=0;
    int			num_rects;
    u_char		*buf;
    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;

    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)
    {

	srcx += pSrcDrawable->x;
	srcy += pSrcDrawable->y;
	iswin |= 2;
	srcWidth = i8priv.devKind;
	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;
	pm_psrcBase = ((PixmapPtr)pSrcDrawable)->devPrivate.ptr;
	prgnSrc = 0;
	if (pDstDrawable->type == DRAWABLE_WINDOW)
		pWin = (WindowPtr)pDstDrawable;
    }

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

    if (pDstDrawable->type == DRAWABLE_WINDOW)
    {
	dx = xOut + pDstDrawable->x;
	dy = yOut + pDstDrawable->y;
	iswin |= 1;
	dstWidth = i8priv.devKind;
    }
    else
    {
	dx = xOut;
	dy = yOut;
	dstWidth = ((PixmapPtr)pDstDrawable)->devKind;
	pm_pdstBase = ((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;
    }
    num_rects = REGION_NUM_RECTS(prgnDst);

    ordering = (unsigned int *)
        ALLOCATE_LOCAL(num_rects * 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 (constant || ((pSrcDrawable != pDstDrawable) &&
	((pGC->subWindowMode != IncludeInferiors) ||
	 (pSrcDrawable->type == DRAWABLE_PIXMAP) ||
	 (pDstDrawable->type == DRAWABLE_PIXMAP))))
      for (i=0; i < num_rects; 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 < num_rects; i++)
            ordering[i] = i;
        } else { /* scroll right. must reverse horizontal banding of rects. */
	  revx = !dy;
          for (i=0, j=1, xMax=0;
               i < num_rects;
               j=i+1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((j < num_rects) &&
                   (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=num_rects-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=num_rects-1;
               i < num_rects;
               i++, j--)
              ordering[i] = j;
      }
    }

    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
    }

    if (revx)
	buf = ALLOCATE_LOCAL(widthSrc);
 
     for(i = 0; i < num_rects; i++) {
	u_char *ypsrc, *ypdst;

        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;

	/* convert to count */
	xMax -= xMin;

	srcx = xMin-dx;
	if (iswin == 3) {
/*
	    if (alu == GXcopy)
		(*vga_conf.copy)(srcx, yMin-dy, xMin, yMin, xMax, yMax - yMin);
	    else
		(*vga_conf.move)(srcx, yMin-dy, xMin, yMin, xMax, yMax - yMin, alu);
*/
	    continue;
	}

	if (revy) {
	    y = yMax - 1;
	} else {
	    y = yMin;
	}

	/* convert to count */
	yMax -= yMin;

	if (depth == 1) {
	    ypdst = pm_pdstBase + y * dstWidth + (xMin >> 3);
	    ypsrc = pm_psrcBase + (y-dy) * srcWidth + (srcx >> 3);
	    while (yMax--) {
		(*movebits)(ypsrc, ypdst, srcx & 7, xMin & 7, xMax);
		if (revy) {
		    ypsrc -= srcWidth;
		    ypdst -= dstWidth;
		} else {
		    ypsrc += srcWidth;
		    ypdst += dstWidth;
		}
	    }
	} else {
	    u_long bytenum;
	    u_char ypage;
	    u_char _based(base) *win_ypsrc, _based(base) *win_ypdst;

	    if (iswin & 1) {	/* dest is window */
		bytenum = (u_long)y * dstWidth + xMin;
		ypage = (u_char)(bytenum >> 16);
		win_ypdst = (u_char _based(base) *)(u_short)bytenum;
		ypdst = (u_char far *)win_ypdst;
		win_ypsrc = 0;
		ypsrc = pm_psrcBase + (y-dy) * dstWidth + srcx;
	    } else {
		win_ypdst = 0;
		ypdst = pm_pdstBase + y * dstWidth + xMin;
		if (iswin & 2) {
		    bytenum = (u_long)(y-dy) * srcWidth + srcx;
		    ypage = (u_char)(bytenum >> 16);
		    win_ypsrc = (u_char _based(base) *)(u_short)bytenum;
		    ypsrc = (u_char far *)win_ypsrc;
		} else {
		    win_ypsrc = 0;
		    ypsrc = pm_psrcBase + (y-dy) * dstWidth + srcx;
		}
	    }
/*
	    if (iswin && ypage != i8priv.curpage) {
		(*vga_conf.page)(i8priv.curpage = ypage);
	    }
*/

	    while (yMax--) {
		u_char page;
		u_char *psrc, *pdst;
		u_char _based(base) *win_psrc, _based(base) *win_pdst;

		psrc = ypsrc;
		pdst = ypdst;

		win_psrc = win_ypsrc;
		win_pdst = win_ypdst;
		page = i8priv.curpage;

		/* This loop will be executed a minimum of one time and a
		 * maximum of three (if two pages are crossed).
		 */
		y = xMax;
		for (;;) {
		    u_char Ol;

		    Ol = 0;
		    xMin = y;	/* X count */
		    if (iswin & 1 && (u_short)win_pdst > (u_short)(-xMin)) {
			xMin = -(short)win_pdst;
			Ol++;
		    }
		    if (iswin & 2 && (u_short)win_psrc > (u_short)(-xMin)) {
			xMin = -(short)win_psrc;
			Ol++;
		    }
		    if (revx) {
			memcpy(buf, psrc, xMin);
			(*movebytes)(pdst, buf, xMin);
		    } else if (alu == GXcopy)
			memcpy(pdst, psrc, xMin);
		    else
			(*movebytes)(pdst, psrc, xMin);
		    y -= xMin;
		    if (!y)
			    break;
		    if (Ol)
			page++;
		    if (iswin & 2) {
			win_psrc += xMin;
			psrc = (u_char far *)win_psrc;
		    } else
			psrc += xMin;
		    if (iswin & 1) {
			win_pdst += xMin;
			pdst = (u_char far *)win_pdst;
		    } else
			pdst += xMin;
/*
		    (*vga_conf.page)(i8priv.curpage = page);
*/
		}
		if (revy) {
		    if (iswin & 2) {
			if ((u_short)win_ypsrc < (u_short)srcWidth)
			    ypage--;
			win_ypsrc -= srcWidth;
			ypsrc = (u_char far *)win_ypsrc;
		    } else
			ypsrc -= srcWidth;
		    if (iswin & 1) {
			if ((u_short)win_ypdst < (u_short)dstWidth)
			    ypage--;
			win_ypdst -= dstWidth;
			ypdst = (u_char far *)win_ypdst;
		    } else
			ypdst -= dstWidth;
		} else {
		    if (iswin & 2) {
			if ((u_short)win_ypsrc >= (u_short)(-srcWidth))
			    ypage++;
			win_ypsrc += srcWidth;
			ypsrc = (u_char far *)win_ypsrc;
		    } else
			ypsrc += srcWidth;
		    if (iswin & 1) {
			if ((u_short)win_ypdst >= (u_short)(-dstWidth))
			    ypage++;
			win_ypdst += dstWidth;
			ypdst = (u_char far *)win_ypdst;
		    } else
			ypdst += dstWidth;
		}
/*
		if (ypage != i8priv.curpage) {
		    (*vga_conf.page)(i8priv.curpage = ypage);
		}
*/
	    }
	}
    }
/*
    (*vga_conf.page)(i8priv.curpage);
*/
    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);
    if (revx)
	DEALLOCATE_LOCAL(buf);
    return prgnDst;
}

/*
 * Take the plane from SrcDrawable specified by bitPlane and turn it into
 * a bitmap.  Then use this bitmap with fillStyle = FillOpaqueStippled to
 * draw into DstDrawable.
 */
RegionPtr
i8CopyPlane(pSrcDrawable, pDstDrawable,
	    pGC, srcx, srcy, width, height, dstx, dsty, bitPlane)
    DrawablePtr 	pSrcDrawable;
    DrawablePtr		pDstDrawable;
    GCPtr		pGC;
    int 		srcx, srcy;
    int 		width, height;
    int 		dstx, dsty;
    unsigned long	bitPlane;
{
    int			i, j;
    PixmapPtr		bitmap;
    Bool		made_bitmap;
    DDXPointPtr		pptFirst, ppt;
    int			*pwidthFirst, *pwidth;
    int			oldFillStyle;
    PixmapPtr		oldStipple;
    DDXPointRec		oldPatOrg;
    int			x, y;

    if (!width || !height)
	return 0;

    ppt = pptFirst = (DDXPointPtr)ALLOCATE_LOCAL(height * sizeof(DDXPointRec));
    pwidth = pwidthFirst = (int *)ALLOCATE_LOCAL(height * sizeof(int));
    if(!pptFirst || !pwidthFirst)
    {
printf("CopyPlane: ALLOCATE_LOCAL failed\n");
       if (pwidthFirst)
	   DEALLOCATE_LOCAL(pwidthFirst);
       if (pptFirst)
	   DEALLOCATE_LOCAL(pptFirst);
       return 0;
    }

    if (pSrcDrawable->depth == 1) {	/* it's already a bitmap */
	bitmap = (PixmapPtr)pSrcDrawable;
	made_bitmap = 0;
    } else {
	u_char *data, *cp, *dp, mask;

	bitmap = i8CreatePixmap(pDstDrawable->pScreen, width, height, 1);
	if (!bitmap) {
	    made_bitmap = 0;
	    goto out;
	}
	made_bitmap = 1;
	data = ALLOCATE_LOCAL(width * height);
	if (!data)
	    goto out;

	x = srcx + pSrcDrawable->x;
	y = srcy + pSrcDrawable->y;

	for(i = 0; i < height; i++)
	{
	    ppt->x = x;
	    ppt->y = y + i;
	    ppt++;
	    *pwidth++ = width;
	}
	ppt = pptFirst;
	pwidth = pwidthFirst;
	i8GetSpans(pSrcDrawable, width, pptFirst, pwidthFirst, height, data);
	cp = data;
	for (i = 0; i < height; i++) {
	    mask = 0x80;
	    dp = (u_char *)bitmap->devPrivate.ptr + i * bitmap->devKind;
	    for (j = 0; j < width; j++) {
		if (*cp & (u_char)bitPlane)
		    *dp |= mask;
		cp++;
		mask >>= 1;
		if (!mask) {
			dp++;
			mask = 0x80;
		}
	    }
	}
	DEALLOCATE_LOCAL(data);
    }

    oldFillStyle = pGC->fillStyle;
    pGC->fillStyle = FillOpaqueStippled;
    oldStipple = pGC->stipple;
    pGC->stipple = bitmap;
    oldPatOrg = pGC->patOrg;
    pGC->patOrg.x = dstx;
    pGC->patOrg.y = dsty;

    x = dstx + pDstDrawable->x;
    y = dsty + pDstDrawable->y;

    for(i = 0; i < height; i++)
    {
	ppt->x = x;
	ppt->y = y + i;
	ppt++;
	*pwidth++ = width;
    }
    i8FillSpans(pDstDrawable, pGC, height, pptFirst, pwidthFirst, SPANS_SORTED|SPANS_RECT, 0);

    pGC->fillStyle = oldFillStyle;
    pGC->stipple = oldStipple;
    pGC->patOrg = oldPatOrg;
out:
    DEALLOCATE_LOCAL(pwidthFirst);
    DEALLOCATE_LOCAL(pptFirst);
    if (made_bitmap)
	i8DestroyPixmap(bitmap);
    return miHandleExposures(pSrcDrawable, pDstDrawable, pGC, srcx, srcy,
		      width, height, dstx, dsty, 0L);
}
