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

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

#include "egafuncs.h"
#include "mxfuncs.h"

static GCFuncs egaGCFuncs = {
	mxValidateGC,
	mxChangeGC,
	mxCopyGC,
	mxDestroyGC,
	mxChangeClip,
	mxDestroyClip,
	mxCopyClip,
				/* devPrivate */
};

static GCOps egaGCOps = {
	mxFillSpans,
	mxSetSpans,
	mxPutImage,
	mxCopyArea,
	mxCopyPlane,
	miPolyPoint,
	mfbLineSS,
	miPolySegment,
	miPolyRectangle,
	miZeroPolyArc,
	miFillPolygon,
	miPolyFillRect,
	miPolyFillArc,
	miPolyText8,
	miPolyText16,
	miImageText8,
	miImageText16,
	mxImageGlyphBlt,
	mxPolyGlyphBlt,
	mxPushPixels,
	0,	/* LineHelper */
			/* devPrivate */
};

static GCOps egaGCOpsWide = {
	mxFillSpans,
	mxSetSpans,
	mxPutImage,
	mxCopyArea,
	mxCopyPlane,
	miPolyPoint,
	miWideLine,
	miPolySegment,
	miPolyRectangle,
	miPolyArc,
	miFillPolygon,
	miPolyFillRect,
	miPolyFillArc,
	miPolyText8,
	miPolyText16,
	miImageText8,
	miImageText16,
	mxImageGlyphBlt,
	mxPolyGlyphBlt,
	mxPushPixels,
	0,	/* LineHelper */
			/* devPrivate */
};

Bool
mxCreateGC(pGC)
GCPtr pGC;
{
	egaPrivGC *devPriv;

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

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

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

	return TRUE;
}

char ReduceRop[16][2] = {
	{ RROP_BLACK,	RROP_BLACK },
	{ RROP_BLACK,	RROP_NOP },
	{ RROP_BLACK,	RROP_INVERT },
	{ RROP_BLACK,	RROP_WHITE },	/* GXcopy */
	{ RROP_NOP,	RROP_BLACK },
	{ RROP_NOP,	RROP_NOP },
	{ RROP_NOP,	RROP_INVERT },	/* GXxor */
	{ 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 }
};

char StippleRop[16][2] = {
	{ GXandInverted,GXandInverted },/* GXclear */
	{ GXandInverted,GXnoop },	/* GXand */
	{ GXandInverted,GXxor },	/* GXandReverse */
	{ GXandInverted,GXor },		/* GXcopy */
	{ GXnoop,	GXandInverted },/* GXandInverted */
	{ GXnoop,	GXnoop },	/* GXnoop */
	{ GXnoop,	GXxor },	/* GXxor */
	{ GXnoop,	GXor },		/* GXor */
	{ GXxor,	GXandInverted },/* GXnor */
	{ GXxor,	GXnoop },	/* GXequiv */
	{ GXxor,	GXxor },	/* GXinvert */
	{ GXxor,	GXor },		/* GXorReverse */
	{ GXor,		GXandInverted },/* GXcopyInverted */
	{ GXor,		GXnoop },	/* GXorInverted */
	{ GXor,		GXxor },	/* GXnand */
	{ GXor,		GXor }		/* GXset */
};

/*
 * func(InverseAlu[alu], src, dst) == func(alu, ~src, dst)
 */
int InverseAlu[16] = {
	GXclear,	/* GXclear */
	GXandInverted,	/* GXand */
	GXnor,		/* GXandReverse */
	GXcopyInverted,	/* GXcopy */
	GXand,		/* GXandInverted */
	GXnoop,		/* GXnoop */
	GXequiv,	/* GXxor */
	GXorInverted,	/* GXor */
	GXandReverse,	/* GXnor */
	GXxor,		/* GXequiv */
	GXinvert,	/* GXinvert */
	GXnand,		/* GXorReverse */
	GXcopy,		/* GXcopyInverted */
	GXor,		/* GXorInverted */
	GXorReverse,	/* GXnand */
	GXset		/* GXset */
};
	
void
mxChangeGC(pGC, changes)
GCPtr pGC;
Mask changes;
{
	pGC = pGC;
	changes = changes;
}

static void (* 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 *devPriv;
    int i;
    RegionPtr clientClip = 0;
    PixmapPtr	pixmap;
    u_char	ropFillArea;

    if (changes & GCLineWidth) {
	if (pGC->lineWidth)
		pGC->ops = &egaGCOpsWide;
	else
		pGC->ops = &egaGCOps;
    }

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

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

    if (pGC->fillStyle != FillSolid && changes & (GCTile|GCStipple))
	changes |= GCFillStyle;
    if (changes & (GCFunction|GCForeground|GCBackground|GCFillStyle)) {
	pGC->fgPixel = !!pGC->fgPixel;
	pGC->bgPixel = !!pGC->bgPixel;
	devPriv->rop = ReduceRop[pGC->alu][pGC->fgPixel];
	devPriv->flags &= ~(EGA_FILLSOLID|EGA_FASTFILL);
	switch (pGC->fillStyle) {
	    case FillSolid:
solid:
		devPriv->ppPixmap = 0;
		ropFillArea = devPriv->rop;
		devPriv->flags |= EGA_FILLSOLID;
		break;
	    case FillTiled:
		ropFillArea = pGC->alu;
		devPriv->ppPixmap = &pGC->tile.pixmap;
		pixmap = pGC->tile.pixmap;
		/* special check for default background   may expand later */
fast_check:
		if (pixmap->devKind == 1 && ropFillArea == GXcopy &&
		  abs(pWin->drawable.x - pGC->patOrg.x)
		  % pixmap->drawable.width == 0) {
		    switch (pixmap->drawable.width) {
			case 1:
			    for (i = 0; i < pixmap->drawable.height; i++) {
				((char *)pixmap->devPrivate.ptr)[i] &= 0x80;
				((char *)pixmap->devPrivate.ptr)[i] |=
				    (((char *)pixmap->devPrivate.ptr)[i] >> 1);
			    }
			case 2:
			    for (i = 0; i < pixmap->drawable.height; i++) {
				((char *)pixmap->devPrivate.ptr)[i] &= 0xc0;
				((char *)pixmap->devPrivate.ptr)[i] |=
				    (((char *)pixmap->devPrivate.ptr)[i] >> 2);
			    }
			case 4:
			    for (i = 0; i < pixmap->drawable.height; i++) {
				((char *)pixmap->devPrivate.ptr)[i] &= 0xf0;
				((char *)pixmap->devPrivate.ptr)[i] |=
				    (((char *)pixmap->devPrivate.ptr)[i] >> 4);
			    }
			case 8:
			    devPriv->flags |= EGA_FASTFILL;
			    break;
			default:
			    break;
		    }
		}
		break;
	    case FillOpaqueStippled:
		devPriv->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:
		devPriv->ppPixmap = &pGC->stipple;
		ropFillArea = StippleRop[pGC->alu][pGC->fgPixel];
		break;
	}
	devPriv->FillArea = movefuncs[ropFillArea];
    }

    if (changes & GCFunction)
	devPriv->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 = devPriv->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) (devPriv->pCompositeClip);
		}
	        devPriv->pCompositeClip = pregWin;
	        devPriv->freeCompClip = freeTmpClip;
	    }
	    else
	    {
	        if(freeCompClip == FREE_CC) 
		    (* pGC->pScreen->RegionDestroy) (devPriv->pCompositeClip);
		(*pGC->pScreen->Intersect)(clientClip, clientClip, pregWin);
		devPriv->pCompositeClip = clientClip;
		if (freeTmpClip == FREE_CC)
		    (* pGC->pScreen->RegionDestroy) (pregWin);
		devPriv->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)(
			devPriv->pCompositeClip,
			pregWin,
			clientClip);
		    (* pGC->pScreen->RegionDestroy)(pregWin);
		}
		else if ((freeTmpClip == REPLACE_CC) && 
		        (freeCompClip == FREE_CC))
		{
		    (* pGC->pScreen->Intersect)(
			devPriv->pCompositeClip,
		        pregWin,
			clientClip);
		}
		else if ((freeTmpClip == FREE_CC) &&
		         (freeCompClip == REPLACE_CC))
		{
		    (* pGC->pScreen->Intersect)( 
		       pregWin,
		       pregWin,
		       clientClip);
		    devPriv->pCompositeClip = pregWin;
		}
		else if ((freeTmpClip == REPLACE_CC) &&
		         (freeCompClip == REPLACE_CC))
		{
		    (* pGC->pScreen->Intersect)(
			clientClip,
			clientClip,
			pregWin);
		    devPriv->pCompositeClip = clientClip;
		    clientClip = 0;
		}
		devPriv->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 (devPriv->freeCompClip == FREE_CC)
		(* pGC->pScreen->RegionReset)(
		    devPriv->pCompositeClip, &pixbounds);
	    else
	    {
		devPriv->freeCompClip = FREE_CC;
		devPriv->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)(devPriv->pCompositeClip);
			devPriv->pCompositeClip = clientClip;
			devPriv->freeCompClip = FREE_CC;
		} else {
		    (* pGC->pScreen->Intersect)(
		       devPriv->pCompositeClip, 
		       devPriv->pCompositeClip,
		       clientClip);
		    (* pGC->pScreen->RegionDestroy)(clientClip);
		}
	    }
	} /* end of composite clip for pixmap */
    }
}

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

void
mxDestroyGC(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
}

void
mxChangeClip(pGC, type, pvalue, nrects)
    GCPtr	pGC;
    int		type;
    pointer	pvalue;
    int		nrects;
{
    mxDestroyClip(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
mxCopyClip(pgcDst, pgcSrc)
GCPtr pgcDst, pgcSrc;
{
    RegionPtr rgn;

    /* destroy old clip */
    mxDestroyClip(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
mxDestroyClip(pGC)
    GCPtr	pGC;
{
    if(pGC->clientClipType == CT_NONE)
	return;
    if (pGC->clientClipType == CT_PIXMAP) {
	(void)mxDestroyPixmap((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;
}
