#include "X.h"
#include "Xmd.h"
#include "Xproto.h"
#include "dixfontstr.h"
#include "fontstruct.h"
#include "gcstruct.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "scrnintstr.h"
#include "regionst.h"

#include "ega.h"
#include "../../mi/mistruct.h"
#include "../../../os/msdos/msdos.h"

#include "maskbits.h"

void egaChangeGC();

#ifdef LITE
static void egaArc(DrawablePtr, GCPtr, int, xArc *);
#endif

static GCFuncs _near egaGCFuncs = {
	mxValidateGC,
	egaChangeGC,
	egaCopyGC,
	egaDestroyGC,
	egaChangeClip,
	egaDestroyClip,
	egaCopyClip,
				/* devPrivate */
};

static GCOps _near egaGCOps = {
	mxFillSpans,
	egaSetSpans,
	egaPutImage,
	egaCopyArea,
	egaCopyPlane,
	miPolyPoint,
	mfbLineSS,
	miPolySegment,
	miPolyRectangle,
#ifdef LITE
	egaArc,
#else
	miPolyArc,
#endif
	miFillPolygon,
	miPolyFillRect,
#ifdef LITE
	egaArc,
#else
	miPolyFillArc,
#endif
	miPolyText8,
	miPolyText16,
	miImageText8,
	miImageText16,
	egaImageGlyphBlt,
	egaPolyGlyphBlt,
	egaPushPixels,
	mfbMiter,	/* LineHelper */
			/* devPrivate */
};

Bool
egaCreateGC(pGC)
GCPtr pGC;
{
	egaPrivGC *pPriv;

	pGC->funcs = &egaGCFuncs;
	pGC->ops = &egaGCOps;

	pGC->miTranslate = 1;
	pGC->planemask = ~0;

	pPriv = (egaPrivGC *)pGC->devPrivates[egaGCPrivateIndex].ptr;
	bzero(pPriv, sizeof (*pPriv));
	pPriv->movebits = copybits;
	pPriv->FillArea = copybits;

	return TRUE;
}

u_char ReduceRop[16][2] = {
	{ RROP_BLACK,	RROP_BLACK },
	{ RROP_BLACK,	RROP_NOP },
	{ RROP_BLACK,	RROP_INVERT },
	{ RROP_BLACK,	RROP_WHITE },
	{ RROP_NOP,	RROP_BLACK },
	{ RROP_NOP,	RROP_NOP },
	{ RROP_NOP,	RROP_INVERT },
	{ RROP_NOP,	RROP_WHITE },
	{ RROP_INVERT,	RROP_BLACK },
	{ RROP_INVERT,	RROP_NOP },
	{ RROP_INVERT,	RROP_INVERT },
	{ RROP_INVERT,	RROP_WHITE },
	{ RROP_WHITE,	RROP_BLACK },
	{ RROP_WHITE,	RROP_NOP },
	{ RROP_WHITE,	RROP_INVERT },
	{ RROP_WHITE,	RROP_WHITE }
};

u_char InverseAlu[16] = {
	GXclear,
	GXandInverted,
	GXnor,
	GXcopyInverted,
	GXand,
	GXnoop,
	GXequiv,
	GXorInverted,
	GXandReverse,
	GXxor,
	GXinvert,
	GXnand,
	GXcopy,
	GXor,
	GXorReverse,
	GXset
};
	
void
egaChangeGC(pGC, changes)
GCPtr pGC;
Mask changes;
{
}

static void (near * near movefuncs[16])(u_char *, u_char *, int, int, int) = {
	clearbits,	andbits,	andReversebits,	copybits,
	andInvertedbits, noopbits,	xorbits,	orbits,
	norbits,	equivbits,	invertbits,	orReversebits,
	copyInvertedbits, orInvertedbits, nandbits,	setbits
};

void
mxValidateGC(pGC, changes, pDrawable)
    GCPtr 	pGC;
    Mask 		changes;
    DrawablePtr 	pDrawable;
{
    WindowPtr pWin;
    DDXPointRec	oldOrg;		/* origin of thing GC was last used with */
    Bool win_moved;		/* window has moved since last time */
    egaPrivGC *pPriv;
    int i;
    RegionPtr clientClip = 0;
    PixmapPtr	pixmap;
    u_char	ropFillArea;
    int		width;

    oldOrg = pGC->lastWinOrg;
    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	pWin = (WindowPtr)pDrawable;
	pGC->lastWinOrg.x = pDrawable->x;
	pGC->lastWinOrg.y = pDrawable->y;
    }
    else
    {
	pWin = (WindowPtr)NULL;
	pGC->lastWinOrg.x = 0;
	pGC->lastWinOrg.y = 0;
    }

    win_moved = (oldOrg.x != pGC->lastWinOrg.x) ||
		(oldOrg.y != pGC->lastWinOrg.y);

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

    if (pGC->fillStyle != FillSolid && changes & (GCTile|GCStipple))
	changes |= GCFillStyle;
    if (changes & GCTileStipXOrigin)
	pGC->patOrg.x %=
	    (pGC->fillStyle == FillTiled ? pGC->tile.pixmap : pGC->stipple)
	    ->drawable.width;
    if (changes & GCTileStipYOrigin)
	pGC->patOrg.y %=
	    (pGC->fillStyle == FillTiled ? pGC->tile.pixmap : pGC->stipple)
	    ->drawable.height;
    if (win_moved ||
	changes & (GCFunction|GCForeground|GCBackground|GCFillStyle)) {
	pPriv->rop = ReduceRop[pGC->alu][pGC->fgPixel ? 1 : 0];
	pPriv->flags &= ~(EGA_FILLSOLID|EGA_FASTFILL);
	xfree(pPriv->fillBits);
	pPriv->fillBits = 0;
	switch (pGC->fillStyle) {
	    case FillSolid:
solid:
		ropFillArea = pPriv->rop;
		pPriv->flags |= EGA_FILLSOLID;
		break;
	    case FillTiled:
		ropFillArea = pGC->alu;
		pPriv->ppPixmap = &pGC->tile.pixmap;
		pixmap = pGC->tile.pixmap;
		/* special check for easy widths */
fast_check:
		width = pixmap->drawable.width;
		if (pixmap->devKind == 1 && ropFillArea == GXcopy &&
		  (width == 1 || width == 2 || width == 4 || width == 8)) {
		    int height = pixmap->drawable.height;
		    int shift;
		    pPriv->fillBits = xrealloc(pPriv->fillBits, height);
		    memcpy(pPriv->fillBits, pixmap->devPrivate.ptr, height);
		    switch (width) {
			case 1:
			    for (i = 0; i < height; i++) {
				pPriv->fillBits[i] &= 0x80;
				pPriv->fillBits[i] |= pPriv->fillBits[i] >> 1;
			    }
			case 2:
			    for (i = 0; i < height; i++) {
				pPriv->fillBits[i] &= 0xc0;
				pPriv->fillBits[i] |= pPriv->fillBits[i] >> 2;
			    }
			case 4:
			    for (i = 0; i < height; i++) {
				pPriv->fillBits[i] &= 0xf0;
				pPriv->fillBits[i] |= pPriv->fillBits[i] >> 4;
			    }
			case 8:
			    /* rotate this pattern */
			    shift = pDrawable->x + pGC->patOrg.x;
			    if (shift < 0)
				shift = 7 - ((-shift - 1) % 8);
			    else
				shift %= 8;
			    if (shift)
				for (i = 0; i < height; i++) {
				    pPriv->fillBits[i] =
					(u_char)(((u_short)pPriv->fillBits[i] |
				        ((u_short)pPriv->fillBits[i] << 8))
				        >> shift);
				}
			    pPriv->flags |= EGA_FASTFILL;
			    break;
		    }
		}
		break;
	    case FillOpaqueStippled:
		pPriv->ppPixmap = &pGC->stipple;
		pixmap = pGC->stipple;
		if (pGC->fgPixel != pGC->bgPixel) {
		    if (pGC->fgPixel)
			ropFillArea = pGC->alu;
		    else
			ropFillArea = InverseAlu[pGC->alu];
		    goto fast_check;
		} else
			goto solid;
		break;
	    case FillStippled:
		pPriv->ppPixmap = &pGC->stipple;
		ropFillArea = pGC->fgPixel ? (u_char)GXor : (u_char)GXandInverted;
		break;
	}
	pPriv->FillArea = movefuncs[ropFillArea];
    }
    if (changes & GCFunction)
	pPriv->movebits = movefuncs[pGC->alu];

    /*
	if the client clip is different or moved OR
	the subwindowMode has changed OR
	the window's clip has changed since the last validation
	we need to recompute the composite clip
    */

    if ((changes & (GCClipXOrigin|GCClipYOrigin|GCClipMask)) ||
	(changes & GCSubwindowMode) ||
	(pDrawable->serialNumber != (pGC->serialNumber & DRAWABLE_SERIAL_BITS))
       )
    {
	if (pGC->clientClipType != CT_NONE) {
	    if (pGC->clientClipType == CT_PIXMAP) {
		clientClip = mfbPixmapToRegion((PixmapPtr)pGC->clientClip);
	    } else {
		clientClip = (*pGC->pScreen->RegionCreate)((BoxRec *)0,
			    REGION_NUM_RECTS((RegionPtr)pGC->clientClip));
		/* retranslate client clip */
		(* pGC->pScreen->RegionCopy)(clientClip,
					      (RegionPtr)pGC->clientClip);
	    }

	    (* pGC->pScreen->TranslateRegion)(clientClip, 
			   pGC->lastWinOrg.x + pGC->clipOrg.x,
			   pGC->lastWinOrg.y + pGC->clipOrg.y);
	}

	if (pWin)
	{
	    int freeTmpClip, freeCompClip;
	    RegionPtr pregWin;		/* clip for this window, without
					   client clip */

	    if (pGC->subWindowMode == IncludeInferiors)
	    {
	        pregWin = NotClippedByChildren(pWin);
		freeTmpClip = FREE_CC;
	    }
	    else
	    {
	        pregWin = &pWin->clipList;
		freeTmpClip = REPLACE_CC;
	    }
	    freeCompClip = pPriv->freeCompClip;

	    /* if there is no client clip, we can get by with
	       just keeping the pointer we got, and remembering
	       whether or not should destroy (or maybe re-use)
	       it later.  this way, we avoid unnecessary copying
	       of regions.  (this wins especially if many clients clip
	       by children and have no client clip.)
	    */
	    if (!clientClip)
	    {
	        if(freeCompClip == FREE_CC) 
		{
		    (* pGC->pScreen->RegionDestroy) (pPriv->pCompositeClip);
		}
	        pPriv->pCompositeClip = pregWin;
	        pPriv->freeCompClip = freeTmpClip;
	    }
	    else
	    {
	        if(freeCompClip == FREE_CC) 
		    (* pGC->pScreen->RegionDestroy) (pPriv->pCompositeClip);
		(*pGC->pScreen->Intersect)(clientClip, clientClip, pregWin);
		pPriv->pCompositeClip = clientClip;
		if (freeTmpClip == FREE_CC)
		    (* pGC->pScreen->RegionDestroy) (pregWin);
		pPriv->freeCompClip = FREE_CC;
#ifdef notdef
		/* we need one 'real' region to put into the composite
		   clip.
			if pregWin and the current composite clip 
		   are real, we can get rid of one.
			if the current composite clip is real and
		   pregWin isn't, intersect the client clip and
		   pregWin into the existing composite clip.
			if pregWin is real and the current composite
		   clip isn't, intersect pregWin with the client clip
		   and replace the composite clip with it.
			if neither is real, use clientClip just created.
		   do the intersection into it.
		*/

		if ((freeTmpClip == FREE_CC) && (freeCompClip == FREE_CC))
		{
		    (* pGC->pScreen->Intersect)(
			pPriv->pCompositeClip,
			pregWin,
			clientClip);
		    (* pGC->pScreen->RegionDestroy)(pregWin);
		}
		else if ((freeTmpClip == REPLACE_CC) && 
		        (freeCompClip == FREE_CC))
		{
		    (* pGC->pScreen->Intersect)(
			pPriv->pCompositeClip,
		        pregWin,
			clientClip);
		}
		else if ((freeTmpClip == FREE_CC) &&
		         (freeCompClip == REPLACE_CC))
		{
		    (* pGC->pScreen->Intersect)( 
		       pregWin,
		       pregWin,
		       clientClip);
		    pPriv->pCompositeClip = pregWin;
		}
		else if ((freeTmpClip == REPLACE_CC) &&
		         (freeCompClip == REPLACE_CC))
		{
		    (* pGC->pScreen->Intersect)(
			clientClip,
			clientClip,
			pregWin);
		    pPriv->pCompositeClip = clientClip;
		    clientClip = 0;
		}
		pPriv->freeCompClip = FREE_CC;
#endif
	    }
	} /* end of composite clip for a window */
	else
	{
	    BoxRec pixbounds;

	    pixbounds.x1 = 0;
	    pixbounds.y1 = 0;
	    pixbounds.x2 = pDrawable->width;
	    pixbounds.y2 = pDrawable->height;

	    if (pPriv->freeCompClip == FREE_CC)
		(* pGC->pScreen->RegionReset)(
		    pPriv->pCompositeClip, &pixbounds);
	    else
	    {
		pPriv->freeCompClip = FREE_CC;
		pPriv->pCompositeClip = 
			(* pGC->pScreen->RegionCreate)(&pixbounds, 1);
	    }

	    if (clientClip) {
		/* trival case: The pixmap size completely
		 * encloses clientClip.  In this case, the Intersect is
		 * not necessary.
		 */
		if (pixbounds.x1 <= clientClip->extents.x1 &&
		    pixbounds.x2 >= clientClip->extents.x2 &&
		    pixbounds.y1 <= clientClip->extents.y1 &&
		    pixbounds.y2 >= clientClip->extents.y2) {
			(*pGC->pScreen->RegionDestroy)(pPriv->pCompositeClip);
			pPriv->pCompositeClip = clientClip;
			pPriv->freeCompClip = FREE_CC;
		} else {
		    (* pGC->pScreen->Intersect)(
		       pPriv->pCompositeClip, 
		       pPriv->pCompositeClip,
		       clientClip);
		    (* pGC->pScreen->RegionDestroy)(clientClip);
		}
	    }
	} /* end of composite clip for pixmap */
    }
}

void
egaCopyGC(pGC, mask, srcGC)
GCPtr pGC, srcGC;
Mask mask;
{
}

void
egaDestroyGC(pGC)
    GC 			*pGC;
{
    egaPrivGC *pPriv;

    pPriv = (egaPrivGC *)(pGC->devPrivates[egaGCPrivateIndex].ptr);
    if (pPriv->freeCompClip == FREE_CC)
	(*pGC->pScreen->RegionDestroy)(pPriv->pCompositeClip);
#ifdef notdef
    if(pPriv->pAbsClientRegion)
	(*pGC->pScreen->RegionDestroy)(pPriv->pAbsClientRegion);
#endif
    if (pPriv->fillBits)
	xfree(pPriv->fillBits);
}

void
egaChangeClip(pGC, type, pvalue, nrects)
    GCPtr	pGC;
    int		type;
    pointer	pvalue;
    int		nrects;
{
    egaDestroyClip(pGC);
    if(type == CT_PIXMAP)
    {
#ifdef notdef
	/* convert the pixmap to a region */
	pGC->clientClip = (pointer) mfbPixmapToRegion((PixmapPtr)pvalue);
	/* you wouldn't do this if you were leaving the pixmap in
	   rather than converting it.
	*/
	(*pGC->pScreen->DestroyPixmap)(pvalue);
	pGC->clientClipType = CT_REGION;
#else
	pGC->clientClip = pvalue;
	pGC->clientClipType = CT_PIXMAP;
#endif
    }
    else if (type == CT_REGION)
    {
	/* stuff the region in the GC */
	pGC->clientClip = pvalue;
	pGC->clientClipType = CT_REGION;
    }
    else if (type != CT_NONE)
    {
	pGC->clientClip = (pointer) miRectsToRegion(nrects,
		(xRectangle *)pvalue, type);
	Xfree(pvalue);
	pGC->clientClipType = CT_REGION;
    }
    pGC->stateChanges |= GCClipMask;
}

void
egaCopyClip(pgcDst, pgcSrc)
GCPtr pgcDst, pgcSrc;
{
    RegionPtr rgn;

    /* destroy old clip */
    egaDestroyClip(pgcDst);
    if(pgcSrc->clientClipType == CT_NONE)
	return;
    pgcDst->clientClipType = pgcSrc->clientClipType;
    if (pgcSrc->clientClipType == CT_PIXMAP) {
	pgcDst->clientClip = pgcSrc->clientClip;
	((PixmapPtr)(pgcDst->clientClip))->refcnt++;
	return;
    }
    /* Must be a REGION */
    rgn = (RegionPtr)pgcSrc->clientClip;
    rgn = (*pgcDst->pScreen->RegionCreate)((BoxRec *)0, REGION_NUM_RECTS(rgn));
    (*pgcDst->pScreen->RegionCopy)(rgn, (RegionPtr)pgcSrc->clientClip);
    pgcDst->clientClip = (pointer)rgn;
}

void
egaDestroyClip(pGC)
    GCPtr	pGC;
{
    if(pGC->clientClipType == CT_NONE)
	return;
    if (pGC->clientClipType == CT_PIXMAP) {
	(void)egaDestroyPixmap((PixmapPtr)(pGC->clientClip));
    }
    else
    {
	/* we know we'll never have a list of rectangles, since
	   ChangeClip immediately turns them into a region 
	*/
        (*pGC->pScreen->RegionDestroy)((RegionPtr)pGC->clientClip);
    }
    pGC->clientClip = NULL;
    pGC->clientClipType = CT_NONE;
}

#ifdef LITE
static void
egaArc(pDraw, pGC, narcs, parcs)
    DrawablePtr	pDraw;
    GCPtr	pGC;
    int		narcs;
    xArc	*parcs;
{
}
#endif
