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

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

#include <X11/X.h>
#include "scrnintstr.h"
#include "mi.h"
#include "pixmapstr.h"
#include "xf86str.h"
#include "xaa.h"
#include "xaalocal.h"

void
XAAMoveDWORDS_FixedBase(register CARD32 *dest,
                        register CARD32 *src, register int dwords)
{
    while (dwords & ~0x03) {
        *dest = *src;
        *dest = *(src + 1);
        *dest = *(src + 2);
        *dest = *(src + 3);
        dwords -= 4;
        src += 4;
    }

    if (!dwords)
        return;
    *dest = *src;
    if (dwords == 1)
        return;
    *dest = *(src + 1);
    if (dwords == 2)
        return;
    *dest = *(src + 2);
}

void
XAAMoveDWORDS(register CARD32 *dest, register CARD32 *src, register int dwords)
{
    while (dwords & ~0x03) {
        *dest = *src;
        *(dest + 1) = *(src + 1);
        *(dest + 2) = *(src + 2);
        *(dest + 3) = *(src + 3);
        src += 4;
        dest += 4;
        dwords -= 4;
    }
    if (!dwords)
        return;
    *dest = *src;
    if (dwords == 1)
        return;
    *(dest + 1) = *(src + 1);
    if (dwords == 2)
        return;
    *(dest + 2) = *(src + 2);
}

void
XAAMoveDWORDS_FixedSrc(register CARD32 *dest,
                       register CARD32 *src, register int dwords)
{
    while (dwords & ~0x03) {
        *dest = *src;
        *(dest + 1) = *src;
        *(dest + 2) = *src;
        *(dest + 3) = *src;
        dest += 4;
        dwords -= 4;
    }
    if (!dwords)
        return;
    *dest = *src;
    if (dwords == 1)
        return;
    *(dest + 1) = *src;
    if (dwords == 2)
        return;
    *(dest + 2) = *src;
}

static void
XAAWritePixmap32To24(ScrnInfoPtr pScrn, int x, int y, int w, int h, unsigned char *srcInit, int srcwidth,       /* bytes */
                     int rop, unsigned int planemask, int trans)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int count, dwords = bytes_to_int32(w * 3);
    CARD32 *src, *dst;
    Bool PlusOne = FALSE;

    if ((infoRec->ImageWriteFlags & CPU_TRANSFER_PAD_QWORD) &&
        ((dwords * h) & 0x01)) {
        PlusOne = TRUE;
    }

    (*infoRec->SetupForImageWrite) (pScrn, rop, planemask, trans, 24, 24);
    (*infoRec->SubsequentImageWriteRect) (pScrn, x, y, w, h, 0);

    if (dwords > infoRec->ImageWriteRange) {
        dst = (CARD32 *) infoRec->ImageWriteBase;
        while (h--) {
            src = (CARD32 *) srcInit;
            count = w;

            while (count >= 4) {
                *dst = (src[0] & 0x00ffffff) | (src[1] << 24);
                *dst = ((src[1] >> 8) & 0x0000ffff) | (src[2] << 16);
                *dst = ((src[2] >> 16) & 0x000000ff) | (src[3] << 8);
                src += 4;
                count -= 4;
            }
            switch (count) {
            case 0:
                break;
            case 1:
                *dst = src[0];
                break;
            case 2:
                *dst = (src[0] & 0x00ffffff) | (src[1] << 24);
                *dst = src[1] >> 8;
                break;
            default:
                *dst = (src[0] & 0x00ffffff) | (src[1] << 24);
                *dst = ((src[1] >> 8) & 0x0000ffff) | (src[2] << 16);
                *dst = src[2] >> 16;
                break;
            }
            srcInit += srcwidth;
        }
    }
    else {
        while (h--) {
            dst = (CARD32 *) infoRec->ImageWriteBase;
            src = (CARD32 *) srcInit;
            count = w;

            while (count >= 4) {
                dst[0] = (src[0] & 0x00ffffff) | (src[1] << 24);
                dst[1] = ((src[1] >> 8) & 0x0000ffff) | (src[2] << 16);
                dst[2] = ((src[2] >> 16) & 0x000000ff) | (src[3] << 8);
                dst += 3;
                src += 4;
                count -= 4;
            }
            switch (count) {
            case 0:
                break;
            case 1:
                dst[0] = src[0];
                break;
            case 2:
                dst[0] = (src[0] & 0x00ffffff) | (src[1] << 24);
                dst[1] = src[1] >> 8;
                break;
            default:
                dst[0] = (src[0] & 0x00ffffff) | (src[1] << 24);
                dst[1] = ((src[1] >> 8) & 0x0000ffff) | (src[2] << 16);
                dst[2] = src[2] >> 16;
                break;
            }
            srcInit += srcwidth;
        }
    }

    if (PlusOne) {
        CARD32 *base = (CARD32 *) infoRec->ImageWriteBase;

        *base = 0x00000000;
    }

    if (infoRec->ImageWriteFlags & SYNC_AFTER_IMAGE_WRITE)
        (*infoRec->Sync) (pScrn);
    else
        SET_SYNC_FLAG(infoRec);

}

void
XAAWritePixmap(ScrnInfoPtr pScrn, int x, int y, int w, int h, unsigned char *src, int srcwidth, /* bytes */
               int rop, unsigned int planemask, int trans, int bpp, int depth)
{
    XAAInfoRecPtr infoRec;
    int dwords, skipleft, Bpp;
    Bool beCareful, PlusOne;

    if ((bpp == 32) && (pScrn->bitsPerPixel == 24)) {
        XAAWritePixmap32To24(pScrn, x, y, w, h, src, srcwidth,
                             rop, planemask, trans);
        return;
    }

    infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    beCareful = PlusOne = FALSE;
    Bpp = bpp >> 3;

    if ((skipleft = (long) src & 0x03L)) {
        if (!(infoRec->ImageWriteFlags & LEFT_EDGE_CLIPPING)) {
            skipleft = 0;
            beCareful = TRUE;
            goto BAD_ALIGNMENT;
        }

        if (Bpp == 3)
            skipleft = 4 - skipleft;
        else
            skipleft /= Bpp;

        if ((x < skipleft) && !(infoRec->ImageWriteFlags &
                                LEFT_EDGE_CLIPPING_NEGATIVE_X)) {
            skipleft = 0;
            beCareful = TRUE;
            goto BAD_ALIGNMENT;
        }

        x -= skipleft;
        w += skipleft;

        if (Bpp == 3)
            src -= 3 * skipleft;
        else                    /* is this Alpha friendly ? */
            src = (unsigned char *) ((long) src & ~0x03L);
    }

 BAD_ALIGNMENT:

    dwords = bytes_to_int32(w * Bpp);

    if ((infoRec->ImageWriteFlags & CPU_TRANSFER_PAD_QWORD) &&
        ((dwords * h) & 0x01)) {
        PlusOne = TRUE;
    }

    (*infoRec->SetupForImageWrite) (pScrn, rop, planemask, trans, bpp, depth);
    (*infoRec->SubsequentImageWriteRect) (pScrn, x, y, w, h, skipleft);

    if (beCareful) {
        /* in cases with bad alignment we have to be careful not
           to read beyond the end of the source */
        if (((x * Bpp) + (dwords << 2)) > srcwidth)
            h--;
        else
            beCareful = FALSE;
    }

    if (dwords > infoRec->ImageWriteRange) {
        while (h--) {
            XAAMoveDWORDS_FixedBase((CARD32 *) infoRec->ImageWriteBase,
                                    (CARD32 *) src, dwords);
            src += srcwidth;
        }
        if (beCareful) {
            int shift = ((long) src & 0x03L) << 3;

            if (--dwords)
                XAAMoveDWORDS_FixedBase((CARD32 *) infoRec->ImageWriteBase,
                                        (CARD32 *) src, dwords);
            src = (unsigned char *) ((long) (src + (dwords << 2)) & ~0x03L);
            *((CARD32 *) infoRec->ImageWriteBase) = *((CARD32 *) src) >> shift;
        }
    }
    else {
        if (srcwidth == (dwords << 2)) {
            int decrement = infoRec->ImageWriteRange / dwords;

            while (h > decrement) {
                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords * decrement);
                src += (srcwidth * decrement);
                h -= decrement;
            }
            if (h) {
                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords * h);
                if (beCareful)
                    src += (srcwidth * h);
            }
        }
        else {
            while (h--) {
                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords);
                src += srcwidth;
            }
        }

        if (beCareful) {
            int shift = ((long) src & 0x03L) << 3;

            if (--dwords)
                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords);
            src = (unsigned char *) ((long) (src + (dwords << 2)) & ~0x03L);

            ((CARD32 *) infoRec->ImageWriteBase)[dwords] =
                *((CARD32 *) src) >> shift;
        }
    }

    if (PlusOne) {
        CARD32 *base = (CARD32 *) infoRec->ImageWriteBase;

        *base = 0x00000000;
    }

    if (infoRec->ImageWriteFlags & SYNC_AFTER_IMAGE_WRITE)
        (*infoRec->Sync) (pScrn);
    else
        SET_SYNC_FLAG(infoRec);
}

void
XAAWritePixmapScanline(ScrnInfoPtr pScrn, int x, int y, int w, int h, unsigned char *src, int srcwidth, /* bytes */
                       int rop,
                       unsigned int planemask, int trans, int bpp, int depth)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int dwords, skipleft, bufferNo = 0, Bpp = bpp >> 3;
    Bool beCareful = FALSE;
    CARD32 *base;

    if ((skipleft = (long) src & 0x03L)) {
        if (!(infoRec->ScanlineImageWriteFlags & LEFT_EDGE_CLIPPING)) {
            skipleft = 0;
            beCareful = TRUE;
            goto BAD_ALIGNMENT;
        }

        if (Bpp == 3)
            skipleft = 4 - skipleft;
        else
            skipleft /= Bpp;

        if ((x < skipleft) && !(infoRec->ScanlineImageWriteFlags &
                                LEFT_EDGE_CLIPPING_NEGATIVE_X)) {
            skipleft = 0;
            beCareful = TRUE;
            goto BAD_ALIGNMENT;
        }

        x -= skipleft;
        w += skipleft;

        if (Bpp == 3)
            src -= 3 * skipleft;
        else
            src = (unsigned char *) ((long) src & ~0x03L);
    }

 BAD_ALIGNMENT:

    dwords = bytes_to_int32(w * Bpp);

    (*infoRec->SetupForScanlineImageWrite) (pScrn, rop, planemask, trans, bpp,
                                            depth);
    (*infoRec->SubsequentScanlineImageWriteRect) (pScrn, x, y, w, h, skipleft);

    if (beCareful) {
        /* in cases with bad alignment we have to be careful not
           to read beyond the end of the source */
        if (((x * Bpp) + (dwords << 2)) > srcwidth)
            h--;
        else
            beCareful = FALSE;
    }

    while (h--) {
        base = (CARD32 *) infoRec->ScanlineImageWriteBuffers[bufferNo];
        XAAMoveDWORDS(base, (CARD32 *) src, dwords);
        (*infoRec->SubsequentImageWriteScanline) (pScrn, bufferNo++);
        src += srcwidth;
        if (bufferNo >= infoRec->NumScanlineImageWriteBuffers)
            bufferNo = 0;
    }

    if (beCareful) {
        int shift = ((long) src & 0x03L) << 3;

        base = (CARD32 *) infoRec->ScanlineImageWriteBuffers[bufferNo];
        if (--dwords)
            XAAMoveDWORDS(base, (CARD32 *) src, dwords);
        src = (unsigned char *) ((long) (src + (dwords << 2)) & ~0x03L);

        base[dwords] = *((CARD32 *) src) >> shift;
        (*infoRec->SubsequentImageWriteScanline) (pScrn, bufferNo);
    }

    SET_SYNC_FLAG(infoRec);
}

void
XAAPutImage(DrawablePtr pDraw,
            GCPtr pGC,
            int depth,
            int x, int y, int w, int h, int leftPad, int format, char *pImage)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    int bpp = BitsPerPixel(depth);
    Bool depthBug = FALSE;

    if (!w || !h)
        return;

    if (!RegionNumRects(pGC->pCompositeClip))
        return;

    depthBug = XAA_DEPTH_BUG(pGC);

    if (((format == ZPixmap) && infoRec->WritePixmap &&
         ((pDraw->bitsPerPixel == bpp) ||
          ((pDraw->bitsPerPixel == 24) && (bpp == 32) &&
           (infoRec->WritePixmapFlags & CONVERT_32BPP_TO_24BPP))) &&
         CHECK_ROP(pGC, infoRec->WritePixmapFlags) &&
         CHECK_ROPSRC(pGC, infoRec->WritePixmapFlags) &&
         CHECK_PLANEMASK(pGC, infoRec->WritePixmapFlags) &&
         CHECK_NO_GXCOPY(pGC, infoRec->WritePixmapFlags)) ||
        ((format == XYBitmap) && !depthBug && infoRec->WriteBitmap &&
         CHECK_ROP(pGC, infoRec->WriteBitmapFlags) &&
         CHECK_ROPSRC(pGC, infoRec->WriteBitmapFlags) &&
         CHECK_PLANEMASK(pGC, infoRec->WriteBitmapFlags) &&
         CHECK_COLORS(pGC, infoRec->WriteBitmapFlags) &&
         !(infoRec->WriteBitmapFlags & TRANSPARENCY_ONLY)) ||
        ((format == XYPixmap) && !depthBug && infoRec->WriteBitmap &&
         CHECK_ROP(pGC, infoRec->WriteBitmapFlags) &&
         CHECK_ROPSRC(pGC, infoRec->WriteBitmapFlags) &&
         !(infoRec->WriteBitmapFlags & NO_PLANEMASK) &&
         !(infoRec->WriteBitmapFlags & TRANSPARENCY_ONLY))) {

        int MaxBoxes = RegionNumRects(pGC->pCompositeClip);
        BoxPtr pbox, pClipBoxes;
        int nboxes, srcx, srcy, srcwidth;
        xRectangle TheRect;

        TheRect.x = pDraw->x + x;
        TheRect.y = pDraw->y + y;
        TheRect.width = w;
        TheRect.height = h;

        if (MaxBoxes > (infoRec->PreAllocSize / sizeof(BoxRec))) {
            pClipBoxes = malloc(MaxBoxes * sizeof(BoxRec));
            if (!pClipBoxes)
                return;
        }
        else
            pClipBoxes = (BoxPtr) infoRec->PreAllocMem;

        nboxes = XAAGetRectClipBoxes(pGC, pClipBoxes, 1, &TheRect);
        pbox = pClipBoxes;

        if (format == XYBitmap) {
            srcwidth = BitmapBytePad(leftPad + w);
            while (nboxes--) {
                srcx = pbox->x1 - TheRect.x + leftPad;
                srcy = pbox->y1 - TheRect.y;
                (*infoRec->WriteBitmap) (infoRec->pScrn, pbox->x1, pbox->y1,
                                         pbox->x2 - pbox->x1,
                                         pbox->y2 - pbox->y1,
                                         (unsigned char *) pImage +
                                         (srcwidth * srcy) + ((srcx >> 5) << 2),
                                         srcwidth, srcx & 31, pGC->fgPixel,
                                         pGC->bgPixel, pGC->alu,
                                         pGC->planemask);
                pbox++;
            }
        }
        else if (format == ZPixmap) {
            int Bpp = bpp >> 3;

            srcwidth = PixmapBytePad(leftPad + w, depth);
            while (nboxes--) {
                srcx = pbox->x1 - TheRect.x + leftPad;
                srcy = pbox->y1 - TheRect.y;
                (*infoRec->WritePixmap) (infoRec->pScrn, pbox->x1, pbox->y1,
                                         pbox->x2 - pbox->x1,
                                         pbox->y2 - pbox->y1,
                                         (unsigned char *) pImage +
                                         (srcwidth * srcy) + (srcx * Bpp),
                                         srcwidth, pGC->alu, pGC->planemask, -1,
                                         Bpp << 3, depth);
                pbox++;
            }
        }
        else {                  /* XYPixmap */
            int depth = pGC->depth;
            int numBox, increment;
            unsigned long i, mask;
            BoxPtr pntBox;

            srcwidth = BitmapBytePad(w + leftPad);
            increment = h * srcwidth;
            i = 1 << (depth - 1);
            mask = ~0;

            if ((infoRec->pScrn->overlayFlags & OVERLAY_8_32_PLANAR) &&
                (pGC->depth == 8)) {
                i = 0x80000000;
                mask = 0xff000000;
            }

            for (; i & mask; i >>= 1, pImage += increment) {
                if (i & pGC->planemask) {
                    pntBox = pbox;
                    numBox = nboxes;
                    while (numBox--) {
                        srcx = pntBox->x1 - TheRect.x + leftPad;
                        srcy = pntBox->y1 - TheRect.y;
                        (*infoRec->WriteBitmap) (infoRec->pScrn,
                                                 pntBox->x1, pntBox->y1,
                                                 pntBox->x2 - pntBox->x1,
                                                 pntBox->y2 - pntBox->y1,
                                                 (unsigned char *) pImage +
                                                 (srcwidth * srcy) +
                                                 ((srcx >> 5) << 2), srcwidth,
                                                 srcx & 31, ~0, 0, pGC->alu, i);
                        pntBox++;
                    }
                }
            }

        }

        if (pClipBoxes != (BoxPtr) infoRec->PreAllocMem)
            free(pClipBoxes);
    }
    else
        XAAFallbackOps.PutImage(pDraw, pGC, depth, x, y, w, h, leftPad,
                                format, pImage);
}