#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <string.h>

#include "misc.h"
#include "xf86.h"
#include "xf86_OSproc.h"

#include <X11/X.h>
#include "scrnintstr.h"
#include "xf86str.h"
#include "xaa.h"
#include "xaalocal.h"
#include "migc.h"
#include "gcstruct.h"
#include "pixmapstr.h"
#include "xaawrap.h"

static void XAAValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDraw);
static void XAAChangeGC(GCPtr pGC, unsigned long mask);
static void XAACopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
static void XAADestroyGC(GCPtr pGC);
static void XAAChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects);
static void XAADestroyClip(GCPtr pGC);
static void XAACopyClip(GCPtr pgcDst, GCPtr pgcSrc);

GCFuncs XAAGCFuncs = {
    XAAValidateGC, XAAChangeGC, XAACopyGC, XAADestroyGC,
    XAAChangeClip, XAADestroyClip, XAACopyClip
};

extern GCOps XAAPixmapOps;

Bool
XAACreateGC(GCPtr pGC)
{
    ScreenPtr pScreen = pGC->pScreen;
    XAAGCPtr pGCPriv = (XAAGCPtr) dixLookupPrivate(&pGC->devPrivates,
                                                   XAAGetGCKey());
    Bool ret;

    XAA_SCREEN_PROLOGUE(pScreen, CreateGC);

    if ((ret = (*pScreen->CreateGC) (pGC))) {
        pGCPriv->wrapOps = NULL;
        pGCPriv->wrapFuncs = pGC->funcs;
        pGCPriv->XAAOps = &XAAFallbackOps;
        pGC->funcs = &XAAGCFuncs;
    }

    XAA_SCREEN_EPILOGUE(pScreen, CreateGC, XAACreateGC);

    return ret;
}

static void
XAAValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDraw)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);

    XAA_GC_FUNC_PROLOGUE(pGC);

    (*pGC->funcs->ValidateGC) (pGC, changes, pDraw);

    if ((changes & GCPlaneMask) &&
        ((pGC->planemask & infoRec->FullPlanemasks[pGC->depth - 1]) ==
         infoRec->FullPlanemasks[pGC->depth - 1])) {
        pGC->planemask = ~0;
    }

    if (pGC->depth != 32) {
        /* 0xffffffff is reserved for transparency */
        if (pGC->bgPixel == 0xffffffff)
            pGC->bgPixel = 0x7fffffff;
        if (pGC->fgPixel == 0xffffffff)
            pGC->fgPixel = 0x7fffffff;
    }

    if ((pDraw->type == DRAWABLE_PIXMAP) && !IS_OFFSCREEN_PIXMAP(pDraw)) {
        pGCPriv->flags = OPS_ARE_PIXMAP;
        pGCPriv->changes |= changes;

        /* make sure we're not using videomemory pixmaps to render
           onto system memory drawables */

        if ((pGC->fillStyle == FillTiled) &&
            IS_OFFSCREEN_PIXMAP(pGC->tile.pixmap) &&
            !OFFSCREEN_PIXMAP_LOCKED(pGC->tile.pixmap)) {

            XAAPixmapPtr pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);
            FBAreaPtr area = pPriv->offscreenArea;

            XAARemoveAreaCallback(area);        /* clobbers pPriv->offscreenArea */
            xf86FreeOffscreenArea(area);
        }
    }
    else if (!infoRec->pScrn->vtSema && (pDraw->type == DRAWABLE_WINDOW)) {
        pGCPriv->flags = 0;
        pGCPriv->changes |= changes;
    }
    else {
        if (!(pGCPriv->flags & OPS_ARE_ACCEL)) {
            changes |= pGCPriv->changes;
            pGCPriv->changes = 0;
        }
        pGCPriv->flags = OPS_ARE_ACCEL;

#if 1
        /* Ugh.  If we can't use the blitter on offscreen pixmaps used
           as tiles, then we need to move them out as cfb can't handle
           tiles with non-zero origins */

        if ((pGC->fillStyle == FillTiled) &&
            IS_OFFSCREEN_PIXMAP(pGC->tile.pixmap) &&
            (DO_PIXMAP_COPY != (*infoRec->TiledFillChooser) (pGC))) {

            XAAPixmapPtr pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);
            FBAreaPtr area = pPriv->offscreenArea;

            XAARemoveAreaCallback(area);        /* clobbers pPriv->offscreenArea */
            xf86FreeOffscreenArea(area);
        }
#endif
    }

    XAA_GC_FUNC_EPILOGUE(pGC);

    if (!(pGCPriv->flags & OPS_ARE_ACCEL))
        return;

    if ((changes & GCTile) && !pGC->tileIsPixel && pGC->tile.pixmap) {
        XAAPixmapPtr pixPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);

        if (pixPriv->flags & DIRTY) {
            pixPriv->flags &= ~(DIRTY | REDUCIBILITY_MASK);
            pGC->tile.pixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
        }
    }
    if ((changes & GCStipple) && pGC->stipple) {
        XAAPixmapPtr pixPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);

        if (pixPriv->flags & DIRTY) {
            pixPriv->flags &= ~(DIRTY | REDUCIBILITY_MASK);
            pGC->stipple->drawable.serialNumber = NEXT_SERIAL_NUMBER;
        }
    }

    /* If our Ops are still the default ones we need to allocate new ones */
    if (pGC->ops == &XAAFallbackOps) {
        if (!(pGCPriv->XAAOps = malloc(sizeof(GCOps)))) {
            pGCPriv->XAAOps = &XAAFallbackOps;
            return;
        }
        /* make a modifiable copy of the default ops */
        memcpy(pGCPriv->XAAOps, &XAAFallbackOps, sizeof(GCOps));
        pGC->ops = pGCPriv->XAAOps;
        changes = ~0;
    }

    if (!changes)
        return;

    if ((changes & GCDashList) && infoRec->ComputeDash)
        infoRec->ComputeDash(pGC);

    if (changes & infoRec->FillSpansMask)
        (*infoRec->ValidateFillSpans) (pGC, changes, pDraw);

    if (changes & infoRec->SetSpansMask)
        (*infoRec->ValidateSetSpans) (pGC, changes, pDraw);

    if (changes & infoRec->PutImageMask)
        (*infoRec->ValidatePutImage) (pGC, changes, pDraw);

    if (changes & infoRec->CopyAreaMask)
        (*infoRec->ValidateCopyArea) (pGC, changes, pDraw);

    if (changes & infoRec->CopyPlaneMask)
        (*infoRec->ValidateCopyPlane) (pGC, changes, pDraw);

    if (changes & infoRec->PolyPointMask)
        (*infoRec->ValidatePolyPoint) (pGC, changes, pDraw);

    if (changes & infoRec->PolylinesMask)
        (*infoRec->ValidatePolylines) (pGC, changes, pDraw);

    if (changes & infoRec->PolySegmentMask)
        (*infoRec->ValidatePolySegment) (pGC, changes, pDraw);

    if (changes & infoRec->PolyRectangleMask)
        (*infoRec->ValidatePolyRectangle) (pGC, changes, pDraw);

    if (changes & infoRec->PolyArcMask)
        (*infoRec->ValidatePolyArc) (pGC, changes, pDraw);

    if (changes & infoRec->FillPolygonMask)
        (*infoRec->ValidateFillPolygon) (pGC, changes, pDraw);

    if (changes & infoRec->PolyFillRectMask)
        (*infoRec->ValidatePolyFillRect) (pGC, changes, pDraw);

    if (changes & infoRec->PolyFillArcMask)
        (*infoRec->ValidatePolyFillArc) (pGC, changes, pDraw);

    if (changes & infoRec->PolyGlyphBltMask)
        (*infoRec->ValidatePolyGlyphBlt) (pGC, changes, pDraw);

    if (changes & infoRec->ImageGlyphBltMask)
        (*infoRec->ValidateImageGlyphBlt) (pGC, changes, pDraw);

    if (changes & infoRec->PolyText8Mask)
        (*infoRec->ValidatePolyText8) (pGC, changes, pDraw);

    if (changes & infoRec->PolyText16Mask)
        (*infoRec->ValidatePolyText16) (pGC, changes, pDraw);

    if (changes & infoRec->ImageText8Mask)
        (*infoRec->ValidateImageText8) (pGC, changes, pDraw);

    if (changes & infoRec->ImageText16Mask)
        (*infoRec->ValidateImageText16) (pGC, changes, pDraw);

    if (changes & infoRec->PushPixelsMask)
        (*infoRec->ValidatePushPixels) (pGC, changes, pDraw);
}

static void
XAADestroyGC(GCPtr pGC)
{
    XAA_GC_FUNC_PROLOGUE(pGC);

    if (pGCPriv->XAAOps != &XAAFallbackOps)
        free(pGCPriv->XAAOps);

    free(pGCPriv->DashPattern);
    pGCPriv->flags = 0;

    (*pGC->funcs->DestroyGC) (pGC);
    XAA_GC_FUNC_EPILOGUE(pGC);
}

static void
XAAChangeGC(GCPtr pGC, unsigned long mask)
{
    XAA_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->ChangeGC) (pGC, mask);
    XAA_GC_FUNC_EPILOGUE(pGC);

    /* we have to assume that shared memory pixmaps are dirty 
       because we can't wrap all operations on them */

    if ((mask & GCTile) && !pGC->tileIsPixel &&
        PIXMAP_IS_SHARED(pGC->tile.pixmap)) {
        XAAPixmapPtr pPixPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);

        pPixPriv->flags |= DIRTY;
    }

    if ((mask & GCStipple) && PIXMAP_IS_SHARED(pGC->stipple)) {
        XAAPixmapPtr pPixPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);

        pPixPriv->flags |= DIRTY;
    }
}

static void
XAACopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
{
    XAA_GC_FUNC_PROLOGUE(pGCDst);
    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
    XAA_GC_FUNC_EPILOGUE(pGCDst);
}

static void
XAAChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
{
    XAA_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
    XAA_GC_FUNC_EPILOGUE(pGC);
}

static void
XAACopyClip(GCPtr pgcDst, GCPtr pgcSrc)
{
    XAA_GC_FUNC_PROLOGUE(pgcDst);
    (*pgcDst->funcs->CopyClip) (pgcDst, pgcSrc);
    XAA_GC_FUNC_EPILOGUE(pgcDst);
}

static void
XAADestroyClip(GCPtr pGC)
{
    XAA_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->DestroyClip) (pGC);
    XAA_GC_FUNC_EPILOGUE(pGC);
}

/**** Pixmap Wrappers ****/

static void
XAAFillSpansPixmap(DrawablePtr pDraw,
                   GC * pGC,
                   int nInit, DDXPointPtr pptInit, int *pwidthInit, int fSorted)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->FillSpans) (pDraw, pGC, nInit, pptInit, pwidthInit, fSorted);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAASetSpansPixmap(DrawablePtr pDraw,
                  GCPtr pGC,
                  char *pcharsrc,
                  register DDXPointPtr ppt,
                  int *pwidth, int nspans, int fSorted)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->SetSpans) (pDraw, pGC, pcharsrc, ppt, pwidth, nspans, fSorted);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPutImagePixmap(DrawablePtr pDraw,
                  GCPtr pGC,
                  int depth,
                  int x, int y, int w, int h,
                  int leftPad, int format, char *pImage)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PutImage) (pDraw, pGC, depth, x, y, w, h,
                           leftPad, format, pImage);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static RegionPtr
XAACopyAreaPixmap(DrawablePtr pSrc,
                  DrawablePtr pDst,
                  GC * pGC,
                  int srcx, int srcy, int width, int height, int dstx, int dsty)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    RegionPtr ret;

    if (infoRec->pScrn->vtSema &&
        ((pSrc->type == DRAWABLE_WINDOW) || IS_OFFSCREEN_PIXMAP(pSrc))) {
        if (infoRec->ReadPixmap && (pGC->alu == GXcopy) &&
            (pSrc->bitsPerPixel == pDst->bitsPerPixel) &&
            ((pGC->planemask & infoRec->FullPlanemasks[pSrc->depth - 1])
             == infoRec->FullPlanemasks[pSrc->depth - 1])) {
            XAAPixmapPtr pixPriv = XAA_GET_PIXMAP_PRIVATE((PixmapPtr) (pDst));

            pixPriv->flags |= DIRTY;

            return (XAABitBlt(pSrc, pDst, pGC,
                              srcx, srcy, width, height, dstx, dsty,
                              XAADoImageRead, 0L));
        }
        else if (infoRec->NeedToSync) {
            (*infoRec->Sync) (infoRec->pScrn);
            infoRec->NeedToSync = FALSE;
        }
    }

    {
        XAA_PIXMAP_OP_PROLOGUE(pGC, pDst);
        ret = (*pGC->ops->CopyArea) (pSrc, pDst,
                                     pGC, srcx, srcy, width, height, dstx,
                                     dsty);
        XAA_PIXMAP_OP_EPILOGUE(pGC);
    }
    return ret;
}

static RegionPtr
XAACopyPlanePixmap(DrawablePtr pSrc,
                   DrawablePtr pDst,
                   GCPtr pGC,
                   int srcx, int srcy,
                   int width, int height,
                   int dstx, int dsty, unsigned long bitPlane)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    RegionPtr ret;

    XAA_PIXMAP_OP_PROLOGUE(pGC, pDst);

    if (infoRec->pScrn->vtSema &&
        ((pSrc->type == DRAWABLE_WINDOW) || IS_OFFSCREEN_PIXMAP(pSrc))) {
        if (infoRec->NeedToSync) {
            (*infoRec->Sync) (infoRec->pScrn);
            infoRec->NeedToSync = FALSE;
        }
    }

    ret = (*pGC->ops->CopyPlane) (pSrc, pDst,
                                  pGC, srcx, srcy, width, height, dstx, dsty,
                                  bitPlane);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
    return ret;
}

static void
XAAPolyPointPixmap(DrawablePtr pDraw,
                   GCPtr pGC, int mode, int npt, xPoint * pptInit)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyPoint) (pDraw, pGC, mode, npt, pptInit);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolylinesPixmap(DrawablePtr pDraw,
                   GCPtr pGC, int mode, int npt, DDXPointPtr pptInit)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->Polylines) (pDraw, pGC, mode, npt, pptInit);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolySegmentPixmap(DrawablePtr pDraw, GCPtr pGC, int nseg, xSegment * pSeg)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolySegment) (pDraw, pGC, nseg, pSeg);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolyRectanglePixmap(DrawablePtr pDraw,
                       GCPtr pGC, int nRectsInit, xRectangle *pRectsInit)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyRectangle) (pDraw, pGC, nRectsInit, pRectsInit);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolyArcPixmap(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyArc) (pDraw, pGC, narcs, parcs);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAFillPolygonPixmap(DrawablePtr pDraw,
                     GCPtr pGC,
                     int shape, int mode, int count, DDXPointPtr ptsIn)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->FillPolygon) (pDraw, pGC, shape, mode, count, ptsIn);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolyFillRectPixmap(DrawablePtr pDraw,
                      GCPtr pGC, int nrectFill, xRectangle *prectInit)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyFillRect) (pDraw, pGC, nrectFill, prectInit);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolyFillArcPixmap(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyFillArc) (pDraw, pGC, narcs, parcs);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static int
XAAPolyText8Pixmap(DrawablePtr pDraw,
                   GCPtr pGC, int x, int y, int count, char *chars)
{
    int ret;

    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    ret = (*pGC->ops->PolyText8) (pDraw, pGC, x, y, count, chars);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
    return ret;
}

static int
XAAPolyText16Pixmap(DrawablePtr pDraw,
                    GCPtr pGC, int x, int y, int count, unsigned short *chars)
{
    int ret;

    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    ret = (*pGC->ops->PolyText16) (pDraw, pGC, x, y, count, chars);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
    return ret;
}

static void
XAAImageText8Pixmap(DrawablePtr pDraw,
                    GCPtr pGC, int x, int y, int count, char *chars)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->ImageText8) (pDraw, pGC, x, y, count, chars);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAImageText16Pixmap(DrawablePtr pDraw,
                     GCPtr pGC, int x, int y, int count, unsigned short *chars)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->ImageText16) (pDraw, pGC, x, y, count, chars);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAImageGlyphBltPixmap(DrawablePtr pDraw,
                       GCPtr pGC,
                       int xInit, int yInit,
                       unsigned int nglyph,
                       CharInfoPtr * ppci, pointer pglyphBase)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->ImageGlyphBlt) (pDraw, pGC, xInit, yInit, nglyph,
                                ppci, pglyphBase);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPolyGlyphBltPixmap(DrawablePtr pDraw,
                      GCPtr pGC,
                      int xInit, int yInit,
                      unsigned int nglyph,
                      CharInfoPtr * ppci, pointer pglyphBase)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PolyGlyphBlt) (pDraw, pGC, xInit, yInit, nglyph,
                               ppci, pglyphBase);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

static void
XAAPushPixelsPixmap(GCPtr pGC,
                    PixmapPtr pBitMap,
                    DrawablePtr pDraw, int dx, int dy, int xOrg, int yOrg)
{
    XAA_PIXMAP_OP_PROLOGUE(pGC, pDraw);
    (*pGC->ops->PushPixels) (pGC, pBitMap, pDraw, dx, dy, xOrg, yOrg);
    XAA_PIXMAP_OP_EPILOGUE(pGC);
}

GCOps XAAPixmapOps = {
    XAAFillSpansPixmap, XAASetSpansPixmap,
    XAAPutImagePixmap, XAACopyAreaPixmap,
    XAACopyPlanePixmap, XAAPolyPointPixmap,
    XAAPolylinesPixmap, XAAPolySegmentPixmap,
    XAAPolyRectanglePixmap, XAAPolyArcPixmap,
    XAAFillPolygonPixmap, XAAPolyFillRectPixmap,
    XAAPolyFillArcPixmap, XAAPolyText8Pixmap,
    XAAPolyText16Pixmap, XAAImageText8Pixmap,
    XAAImageText16Pixmap, XAAImageGlyphBltPixmap,
    XAAPolyGlyphBltPixmap, XAAPushPixelsPixmap,
};