#include <stdio.h>
#include <dos.h>
#include "X.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "ega.h"
#include "../../mi/mistruct.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "../common/ibmmouse.h"
#include "../../../os/msdos/msdos.h"
#include "egafuncs.h"
#include "mxfuncs.h"

Bool
mxCreateWindow(pWin)
WindowPtr pWin;
{
	pWin = pWin;
	return TRUE;
}

Bool
mxDestroyWindow(pWin)
WindowPtr pWin;
{
	pWin = pWin;
	return TRUE;
}

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

Bool
mxMapWindow(pWin)
WindowPtr pWin;
{
	pWin = pWin;
	return TRUE;
}

Bool
mxUnmapWindow(pWin)
WindowPtr pWin;
{
	pWin = pWin;
	return TRUE;
}

void 
mxCopyWindow(pWin, ptOldOrg, prgnSrc)
    WindowPtr pWin;
    DDXPointRec ptOldOrg;
    RegionPtr prgnSrc;
{
    RegionPtr prgnDst;
    int dx, dy, width, yinc;
    int i, j;
    u_char VGA_BASED *psrc, VGA_BASED *pdst;
    RegBoxPtr pbox;
    int y, xMax, yMax;
    int xMin, yMin, nlines;
    Bool revx = 0;	/* True if moving right and stationary vertical */
    Bool revy = 0;
#ifdef EGA_HIRES
    Bool copy = 0;
#endif
    u_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 ((unsigned)abs(dy) >= pWin->drawable.height + width ||
      (unsigned)abs(dx) >= pWin->drawable.width + 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;
          }
	  if (dy == 0)
		revx = TRUE;
        }
      }
      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
    {
	    buf = ALLOCATE_LOCAL((pWin->drawable.width + width + 7) / 8);
	    if (!buf) {
		DEALLOCATE_LOCAL(ordering);
		goto out;
	    }
    }

    if (revy)
	yinc = -vga_width;
    else
	yinc = vga_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++) {
	int dstpage;

	pbox = &REGION_RECTS(prgnDst)[ordering[i]];
	xMin = pbox->x1;
	xMax = pbox->x2;
	yMin = pbox->y1;
	yMax = pbox->y2;
	if (xMax <= xMin || yMax <= yMin)
	    continue;
	j = (xMax - dx + 7) / 8 - (xMin - dx) / 8;
	xMax -= xMin;
	if (revy)
	    y = yMax - 1;
	else
	    y = yMin;
	psrc = vga_mem(0) + vga_width * (y - dy);
	pdst = vga_mem(0) + vga_width * y;
	nlines = yMax - yMin;
#ifdef EGA_HIRES
	/* set starting page */
	if (psrc > vga_mem(0xffff)) {
	    psrc -= 0x10000;
	    if (!vga_curpage) {
		vga_curpage = 1;
		SetPage();
	    }
	} else {
	    if (vga_curpage) {
		vga_curpage = 0;
		SetPage();
	    }
	}
	if (pdst > vga_mem(0xffff)) {
	    dstpage = 1;
	    pdst -= 0x10000;
	} else
	    dstpage = 0;
loop:
	if (vga_hires) {
	    if (revy) {
		/* 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;
	    } else {
		/* check if source overlaps */
		if (yMin - dy < HIRES_LPP && yMax - dy > HIRES_LPP)
		    nlines = HIRES_LPP - (yMin - dy);
		/* check dest */
		if (yMin < HIRES_LPP && yMin + nlines > HIRES_LPP)
		    nlines = HIRES_LPP - yMin;
		yMin += nlines;
	    }
	    copy = vga_curpage != dstpage;
	}
#endif
	while (nlines--) {
	    if (revx
#ifdef EGA_HIRES
	    || copy
#endif
	    ) {
		u_char VGA_BASED *cp = psrc + (xMin - dx) / 8;
		vga_memcpy(buf, cp, j);
#ifdef EGA_HIRES
		if (copy) {
		    VGASetPage(dstpage << vga_page_shift);
		    copybits(pdst, buf, xMin, (xMin-dx)&7, xMax);
		    SetPage();
		} else
#endif
		copybits(pdst, buf, xMin, (xMin-dx)&7, xMax);
	    } else {
		copybits(pdst, psrc, xMin, xMin-dx, xMax);
	    }
	    psrc += yinc;
	    pdst += yinc;
	}
#ifdef EGA_HIRES
	if (vga_hires && (nlines = yMax - yMin)) {
	    if (revy) {
		if (psrc < vga_mem(0)) {
		    psrc += 0x10000;
		    vga_curpage = 0;
		    SetPage();
		}
		if (pdst < vga_mem(0)) {
		    pdst += 0x10000;
		    dstpage = 0;
		}
	    } else {
		if (psrc > vga_mem(0xffff)) {
		    psrc -= 0x10000;
		    vga_curpage = 1;
		    SetPage();
		}
		if (pdst > vga_mem(0xffff)) {
		    pdst -= 0x10000;
		    dstpage = 1;
		}
	    }
	    goto loop;
	}
#endif
    }
    if (buf)
	DEALLOCATE_LOCAL(buf);
    DEALLOCATE_LOCAL(ordering);
out:
    (* pWin->drawable.pScreen->RegionDestroy)(prgnDst);
}

RegionPtr
mxCopyArea(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, nlines;
    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 */
    unsigned char	VGA_BASED *psrcBase, VGA_BASED *psrc;
    unsigned char	VGA_BASED *pdstBase, VGA_BASED *pdst;
    unsigned char	*buf = 0;
    int			srcWidth, dstWidth;
    Bool		realSrcClip=0;
    WindowPtr		pWin=0;
    egaPrivGC		*pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    void (*func)(u_char *, u_char *, int, int, int) = pPriv->movebits;
#ifdef EGA_HIRES
    Bool copy = 0;
#endif

    if (func == noopbits)
	return NULL;

    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;		/* src is a window */
	srcWidth = vga_width;
	psrcBase = vga_mem(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;
	psrcBase = ((PixmapPtr)pSrcDrawable)->devPrivate.ptr;
	prgnSrc = 0;
	if (pDstDrawable->type == DRAWABLE_WINDOW)
		pWin = (WindowPtr)pDstDrawable;
    }

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

    if (func == noopbits)
	goto out;

    if (pDstDrawable->type == DRAWABLE_WINDOW)
    {

	dx = xOut + pDstDrawable->x;
	dy = yOut + pDstDrawable->y;
	iswin |= 1;			/* dst is a window */
	dstWidth = vga_width;
	pdstBase = vga_mem(0);
    }
    else
    {
	dx = xOut;
	dy = yOut;
	dstWidth = ((PixmapPtr)pDstDrawable)->devKind;
	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;
    }

    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;
	  if (dy == 0 && -dx < 8)
		revx = TRUE;
        } 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;
      }
    }

    if (revx
#ifdef EGA_HIRES
	|| iswin
#endif
    ) {
	    buf = ALLOCATE_LOCAL(dstBox.x2 - dstBox.x1 + 15 >> 3);
	    if (!buf) {
		DEALLOCATE_LOCAL(ordering);
		return NULL;
	    }
    }
	    
    if (iswin) {
	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();
    }
 
     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;

	xMax -= xMin;
	srcx = xMin-dx;

	nlines = yMax - yMin;

	if (revy) {
	    int dstpage;

	    psrc = psrcBase + (yMax - 1 - dy) * srcWidth;
	    pdst = pdstBase + (yMax - 1) * dstWidth;
#ifdef EGA_HIRES
	    if (vga_hires) {
		if (iswin & 2) {
		    if (psrc > vga_mem(0xffff)) {
			psrc -= 0x10000;
			vga_curpage = 1;
			SetPage();
		    } else if (vga_curpage) {
			vga_curpage = 0;
			SetPage();
		    }
		    if (iswin & 1) {
			if (pdst > vga_mem(0xffff)) {
			    pdst -= 0x10000;
			    dstpage = 1;
			} else
			    dstpage = 0;
			copy = vga_curpage != dstpage;
		    }
		} else if (iswin & 1) {
		    if (pdst > vga_mem(0xffff)) {
			pdst -= 0x10000;
			vga_curpage = 1;
			SetPage();
		    } else if (vga_curpage) {
			vga_curpage = 0;
			SetPage();
		    }
		}
	    }
#endif
	    /* Copying from bottom up */
	    while (nlines--) {
#ifdef EGA_HIRES
		if (copy) {
		    u_char VGA_BASED *cp = psrc + (srcx >> 3);
		    vga_memcpy(buf, cp, xMax + 15 >> 3);
		    VGASetPage(dstpage << vga_page_shift);
		    (*func)(pdst, buf, xMin, srcx & 7, xMax);
		    SetPage();
		} else
#endif
		(*func)(pdst, psrc, xMin, srcx, xMax);
		psrc -= srcWidth;
		pdst -= dstWidth;
#ifdef EGA_HIRES
		if (vga_hires) {
		    if (iswin & 2) {
			if (psrc < vga_mem(0)) {
			    psrc += 0x10000;
			    vga_curpage = 0;
			    SetPage();
			}
			if (iswin & 1) {
			    if (pdst < vga_mem(0)) {
				pdst += 0x10000;
				dstpage = 0;
			    }
			    copy = vga_curpage != dstpage;
			}
		    } else if (iswin & 1)
			if (pdst < vga_mem(0)) {
			    pdst += 0x10000;
			    vga_curpage = 0;
			    SetPage();
			}
		}
#endif
	    }
	} else {
	    int dstpage;

	    psrc = psrcBase + (yMin - dy) * srcWidth;
	    psrc += srcx >> 3;
	    srcx &= 7;
	    pdst = pdstBase + yMin * dstWidth;
#ifdef EGA_HIRES
	    if (vga_hires) {
		vga_curpage = 0;
		SetPage();
		dstpage = 0;
	    }
#endif

	    while (nlines--) {
#ifdef EGA_HIRES
		if (vga_hires) {
		    if (iswin & 2) {
			if (psrc > vga_mem(0xffff)) {
			    psrc -= 0x10000;
			    vga_curpage = 1;
			    SetPage();
			}
			if (iswin & 1) {
			    if (pdst > vga_mem(0xffff)) {
				pdst -= 0x10000;
				dstpage = 1;
			    }
			    copy = vga_curpage != dstpage;
			}
		    } else if (iswin & 1) {
			if (pdst > vga_mem(0xffff)) {
			    pdst -= 0x10000;
			    vga_curpage = 1;
			    SetPage();
			}
		    }
		}
#endif
		if (revx
#ifdef EGA_HIRES
		    || copy
#endif
			) {
		    u_char VGA_BASED *cp = psrc;
		    vga_memcpy(buf, cp, xMax + 15 >> 3);
#ifdef EGA_HIRES
		if (vga_hires && copy) {
		    VGASetPage(dstpage << vga_page_shift);
		    (*func)(pdst, buf, xMin, srcx, xMax);
		    SetPage();
		} else
#endif
		    (*func)(pdst, buf, xMin, srcx, xMax);
		} else
		    (*func)(pdst, psrc, xMin, srcx, xMax);
		psrc += srcWidth;
		pdst += dstWidth;
	    }
	}
    }
    if (buf)
	DEALLOCATE_LOCAL(buf);

    if (prgnNew)
	(*pGC->pScreen->RegionDestroy)(prgnNew);
	
    if(realSrcClip)
	(*pGC->pScreen->RegionDestroy)(prgnSrc);

    DEALLOCATE_LOCAL(ordering);
out:
    prgnDst = miHandleExposures(pSrcDrawable, pDstDrawable, pGC, xIn, yIn,
		      widthSrc, heightSrc, xOut, yOut, 0L);
    return prgnDst;
}

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

    pWin = pWin;
    mask = mask;
    return(TRUE);
}

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

RegionPtr
mxCopyPlane(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;
	pGC->fgPixel = !!pGC->fgPixel;
	pGC->bgPixel = !!pGC->bgPixel;
	if (pGC->fgPixel && !pGC->bgPixel)
	    prgnExposed = (*pGC->ops->CopyArea)(pSrcDrawable, pDstDrawable,
		pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut);
	else {
	    alu = pGC->alu;
	    if (pGC->fgPixel == pGC->bgPixel)
		pGC->alu = ReduceRop[alu][pGC->fgPixel];
	    else
		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;
}
