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

/* 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 */
};

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

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

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

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

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

void 
vgaCopyWindow(pWin, ptOldOrg, prgnSrc)
    WindowPtr pWin;
    DDXPointRec ptOldOrg;
    RegionPtr prgnSrc;
{
    RegionPtr prgnDst;
    int x, y, dx, dy, width;
    int i, j;
    _segment base = ((vgaPrivScr *)(pWin->drawable.pScreen->devPrivate))->fb;
    RegBoxPtr pbox;
    int xMax, yMax;
    int xMin, yMin;
    Bool revx = 0;	/* True if moving right and stationary vertical */
    Bool revy = 0;
    char *buf = 0;
    unsigned int	*ordering;
    int		num_rects;
    int (near *func)() = egacopybits;
    Bool easy;

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

    /* turn cursor off while we do the copy */
    HideCursor();
    dx = pWin->drawable.x - ptOldOrg.x;
    dy = y - ptOldOrg.y;
    (* pWin->drawable.pScreen->TranslateRegion)(prgnSrc, dx, dy);
    (* pWin->drawable.pScreen->Intersect)(prgnDst, &pWin->borderClip, prgnSrc);

    easy = (dx & 7) == 0;

    num_rects = REGION_NUM_RECTS(prgnDst);
    ordering = (unsigned int *)
        ALLOCATE_LOCAL(num_rects * sizeof(unsigned int));
    if (!ordering) {
printf("Can't allocate ordering\n");
	return;
    }

    width = pWin->borderWidth;
    ptOldOrg.x -= width;
    ptOldOrg.y -= width;
    width *= 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 < num_rects; 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 < num_rects; i++)
            ordering[i] = i;
	  if (dy == 0 && -dx < 8)
		func = copybits;
        } else { /* scroll right. must reverse horizontal banding of rects. */
	  revx = !dy;
          for (i=0, xMin=1, xMax=0;
               i < num_rects;
               xMin=i+1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((xMin < num_rects) &&
                   (REGION_RECTS(prgnDst)[xMin].y1 == y))
              xMin++;
            /* reverse the horizontal band in the output ordering */
            for (xMin-- ; xMin >= xMax; xMin--, i++)
              ordering[i] = xMin;
          }
        }
      }
      else { /* Scroll down. Must reverse vertical banding. */
        if (dx < 0) { /* Scroll left. Horizontal order OK. */
          for (i=num_rects-1, xMin=i-1, xMax=i, yMax=0;
              i >= 0;
              xMin=i-1, xMax=i) {
            /* find extent of current horizontal band */
            y=REGION_RECTS(prgnDst)[i].y1; /* band has this y coordinate */
            while ((xMin >= 0) &&
                   (REGION_RECTS(prgnDst)[xMin].y1 == y))
              xMin--;
            /* reverse the horizontal band in the output ordering */
            for (xMin++ ; xMin <= xMax; xMin++, i--, yMax++)
              ordering[yMax] = xMin;
          }
        }
        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, xMin=num_rects-1;
               i < num_rects;
               i++, xMin--)
              ordering[i] = xMin;
	revy = 1;
      }
    }

    if (revx)
	    buf = ALLOCATE_LOCAL((pWin->drawable.width + 7) / 8);

    width = ((vgaPrivScr *)(pWin->drawable.pScreen->devPrivate))->devKind;

    if (easy) {
	outp(0x3ce, 5);	/* mode register */
	outp(0x3cf, (inp(0x3cf) & ~3) | 1);	/* write mode 1 */
    }

    for (i = 0; i < num_rects; i++) {
	u_char _based(base) *psrc, _based(base) *pdst;
	u_char _based(base) *psrcBase, _based(base) *pdstBase;

	pbox = &REGION_RECTS(prgnDst)[ordering[i]];
	xMin = pbox->x1;
	xMax = pbox->x2;
	yMin = pbox->y1;
	yMax = pbox->y2;
	if (xMax <= xMin || yMax <= yMin)
	    continue;

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

	psrcBase = (char _based(base) *)0 + width * (y-dy) + (xMin - dx) / 8;
	pdstBase = (char _based(base) *)0 + width * y + xMin / 8;

	while (yMax--) {
	    psrc = psrcBase;
	    pdst = pdstBase;

	    if (easy) {
		/* dx is multiple of 8 */
		if (revx) {
printf("RevX\n");
		} else {
		    x = xMin & 7;
		    y = xMax - xMin;
		    if (x) {
			i = 0xff >> x;
			y -= 8 - x;
			if (y < 0) {
				i &= 0xff << -y;
				y = 0;
			}
			outpw(0x3ce, i << 8 | 8);
			*pdst++ = *psrc++;
			outpw(0x3ce, 0xff08);
		    }
		    x = y / 8;
		    y &= 7;
		    while (x--)
			*pdst++ = *psrc++;
		    if (y) {
			outpw(0x3ce, ~(0xff >> y) << 8 | 8);
			*pdst++ = *psrc++;
			outpw(0x3ce, 0xff08);
		    }
		}
	    } else {
		if (revx) {
		    buf[0] = pdst[0];
		    j = xMax/8-xMin/8;
		    buf[j] = pdst[j];
		    memcpy(buf, (char far *)psrc, j+1);
		    egacopybits(buf, (char far *)pdst, (xMin-dx)&7, xMin&7, xMax-xMin,
			    EGA_COPY);
		} else {
		    (*func)((char far *)psrc, (char far *)pdst, (xMin-dx)&7, xMin&7, xMax-xMin, 0);
		}
	    }
	}
	if (revy) {
	    psrcBase -= width;
	    pdstBase -= width;
	} else {
	    psrcBase += width;
	    pdstBase += width;
	}
    }
    if (easy) {
	outp(0x3ce, 5);	/* mode register */
	outp(0x3cf, inp(0x3cf) & ~3);	/* write mode 0 */
    }
    if (buf)
	DEALLOCATE_LOCAL(buf);
    DEALLOCATE_LOCAL(ordering);
#ifdef DIRECT_MOUSE
    /* turn cursor back on */
    ShowCursor();
#endif
    (* pWin->drawable.pScreen->RegionDestroy)(prgnDst);
}

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

    return(TRUE);
}
