/*
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
 *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
 *  Copyright (c) 2004 Clarence Dang <dang@kde.org>
 *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
 *  Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#include <stdlib.h>
#include <string.h>
#include <cfloat>
#include <cmath>
#include <climits>
#include <strings.h>

#include "tqbrush.h"
#include "tqfontinfo.h"
#include "tqfontmetrics.h"
#include "tqpen.h"
#include "tqregion.h"
#include "tqwmatrix.h"
#include <tqimage.h>
#include <tqmap.h>
#include <tqpainter.h>
#include <tqpixmap.h>
#include <tqpointarray.h>
#include <tqrect.h>
#include <tqstring.h>

#include <kdebug.h>
#include <kcommand.h>
#include <tdelocale.h>

#include "kis_brush.h"
#include "kis_debug_areas.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_pattern.h"
#include "kis_rect.h"
#include "kis_colorspace.h"
#include "kis_transaction.h"
#include "kis_types.h"
#include "kis_vec.h"
#include "kis_iterators_pixel.h"
#include "kis_paintop.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_color.h"

// Maximum distance from a Bezier control point to the line through the start
// and end points for the curve to be considered flat.
#define BEZIER_FLATNESS_THRESHOLD 0.5

KisPainter::KisPainter()
{
    init();
}

KisPainter::KisPainter(KisPaintDeviceSP device)
{
    init();
    Q_ASSERT(device);
        begin(device);
}

void KisPainter::init()
{
    m_transaction = 0;
    m_paintOp = 0;
    m_filter = 0;
    m_brush = 0;
    m_pattern= 0;
    m_opacity = OPACITY_OPAQUE;
    m_compositeOp = COMPOSITE_OVER;
    m_dab = 0;
    m_fillStyle = FillStyleNone;
    m_strokeStyle = StrokeStyleBrush;
    m_pressure = PRESSURE_MIN;
    m_duplicateHealing = false;
    m_duplicateHealingRadius = 10;
    m_duplicatePerspectiveCorrection = false;
    m_varyBrushSpacingWithPressureWhenDrawingALine = true;
}

KisPainter::~KisPainter()
{
    m_brush = 0;
    delete m_paintOp;
    end();
}

void KisPainter::begin(KisPaintDeviceSP device)
{
    if (!device) return;

    if (m_transaction)
        delete m_transaction;

    m_device = device;
    m_colorSpace = device->colorSpace();
    m_pixelSize = device->pixelSize();
}

KCommand *KisPainter::end()
{
    return endTransaction();
}

void KisPainter::beginTransaction(const TQString& customName)
{
    if (m_transaction)
        delete m_transaction;
    m_transaction = new KisTransaction(customName, m_device);
    TQ_CHECK_PTR(m_transaction);
}

void KisPainter::beginTransaction( KisTransaction* command)
{
    if (m_transaction)
        delete m_transaction;
    m_transaction = command;
}


KCommand *KisPainter::endTransaction()
{
    KCommand *command = m_transaction;
        m_transaction = 0;
        return command;
}


TQRect KisPainter::dirtyRect() {
    TQRect r = m_dirtyRect;
    m_dirtyRect = TQRect();
    return r;
}

void KisPainter::bitBlt(TQ_INT32 dx, TQ_INT32 dy,
                        const KisCompositeOp& op,
                        KisPaintDeviceSP srcdev,
                        TQ_UINT8 opacity,
                        TQ_INT32 sx, TQ_INT32 sy,
                        TQ_INT32 sw, TQ_INT32 sh)
{
    if (srcdev == 0) {
        return;
    }

    TQRect srcRect = TQRect(sx, sy, sw, sh);

    if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
        srcRect &= srcdev->extent();
    }

    if (srcRect.isEmpty()) {
        return;
    }

    dx += srcRect.x() - sx;
    dy += srcRect.y() - sy;

    sx = srcRect.x();
    sy = srcRect.y();
    sw = srcRect.width();
    sh = srcRect.height();

    addDirtyRect(TQRect(dx, dy, sw, sh));

    KisColorSpace * srcCs = srcdev->colorSpace();

    TQ_INT32 dstY = dy;
    TQ_INT32 srcY = sy;
    TQ_INT32 rowsRemaining = sh;

    while (rowsRemaining > 0) {

        TQ_INT32 dstX = dx;
        TQ_INT32 srcX = sx;
        TQ_INT32 columnsRemaining = sw;
        TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
        TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);

        TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
        rows = TQMIN(rows, rowsRemaining);

        while (columnsRemaining > 0) {

            TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
            TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);

            TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
            columns = TQMIN(columns, columnsRemaining);

            TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
            //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY);
            KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
            const TQ_UINT8 *srcData = srcIt.rawData();

            //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
            TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
            KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
            TQ_UINT8 *dstData = dstIt.rawData();


            m_colorSpace->bitBlt(dstData,
                          dstRowStride,
                          srcCs,
                          srcData,
                          srcRowStride,
                          0,
                          0,
                          opacity,
                          rows,
                          columns,
                          op);

            srcX += columns;
            dstX += columns;
            columnsRemaining -= columns;
        }

        srcY += rows;
        dstY += rows;
        rowsRemaining -= rows;
    }
}

void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy,
                  const KisCompositeOp &op,
                  KisPaintDeviceSP srcdev,
                  KisSelectionSP seldev,
                  TQ_UINT8 opacity,
                  TQ_INT32 sx, TQ_INT32 sy,
                  TQ_INT32 sw, TQ_INT32 sh)
{
    // Better use a probablistic method than a too slow one
    if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) {
/*
        kdDebug() << "Blitting outside selection rect\n";

        kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")"
        << ", seldev: " << seldev << " (" << seldev->name() << ")"
        << ". dx, dy " << dx << "," << dy
        << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl;
*/
        return;
    }
    bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh);
}

void KisPainter::bltMask(TQ_INT32 dx, TQ_INT32 dy,
                     const KisCompositeOp &op,
                     KisPaintDeviceSP srcdev,
                     KisPaintDeviceSP seldev,
                     TQ_UINT8 opacity,
                     TQ_INT32 sx, TQ_INT32 sy,
                     TQ_INT32 sw, TQ_INT32 sh)

{
    if (srcdev == 0) return;

    if (seldev == 0) return;

    if (m_device == 0) return;


    TQRect srcRect = TQRect(sx, sy, sw, sh);

    if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
        srcRect &= srcdev->extent();
    }

    if (srcRect.isEmpty()) {
        return;
    }

    dx += srcRect.x() - sx;
    dy += srcRect.y() - sy;

    sx = srcRect.x();
    sy = srcRect.y();
    sw = srcRect.width();
    sh = srcRect.height();

    addDirtyRect(TQRect(dx, dy, sw, sh));

    KisColorSpace * srcCs = srcdev->colorSpace();

    TQ_INT32 dstY = dy;
    TQ_INT32 srcY = sy;
    TQ_INT32 rowsRemaining = sh;

    while (rowsRemaining > 0) {

        TQ_INT32 dstX = dx;
        TQ_INT32 srcX = sx;
        TQ_INT32 columnsRemaining = sw;
        TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
        TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
        TQ_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1);

        TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
        rows = TQMIN(numContiguousSelRows, rows);
        rows = TQMIN(rows, rowsRemaining);

        while (columnsRemaining > 0) {

            TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
            TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
            TQ_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1);

            TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
            columns = TQMIN(numContiguousSelColumns, columns);
            columns = TQMIN(columns, columnsRemaining);

            //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
            TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
            KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
            TQ_UINT8 *dstData = dstIt.rawData();

            //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY);
            TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
            KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
            const TQ_UINT8 *srcData = srcIt.rawData();

            //const TQ_UINT8 *selData = seldev->pixel(dstX, dstY);
            TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY);
            KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false);
            const TQ_UINT8 *selData = selIt.rawData();

            m_colorSpace->bitBlt(dstData,
                                   dstRowStride,
                                   srcCs,
                                   srcData,
                                   srcRowStride,
                                   selData,
                                   selRowStride,
                                   opacity,
                                   rows,
                                   columns,
                                   op);

            srcX += columns;
            dstX += columns;
            columnsRemaining -= columns;
        }

        srcY += rows;
        dstY += rows;
        rowsRemaining -= rows;
    }
}


void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy,
                  const KisCompositeOp& op,
                  KisPaintDeviceSP srcdev,
                  TQ_UINT8 opacity,
                  TQ_INT32 sx, TQ_INT32 sy,
                  TQ_INT32 sw, TQ_INT32 sh)
{
    if (m_device == 0) return;
    if (!m_device->hasSelection()) {
        bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh);
    }
    else
        bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh);
}

double KisPainter::paintLine(const KisPoint & pos1,
                 const double pressure1,
                 const double xTilt1,
                 const double yTilt1,
                 const KisPoint & pos2,
                 const double pressure2,
                 const double xTilt2,
                 const double yTilt2,
                 const double inSavedDist)
{
    if (!m_device) return 0;
    if (!m_paintOp) return 0;
    if (!m_brush) return 0;

    double savedDist = inSavedDist;
    KisVector2D end(pos2);
    KisVector2D start(pos1);

    KisVector2D dragVec = end - start;
    KisVector2D movement = dragVec;

    if (savedDist < 0) {
        m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement));
        savedDist = 0;
    }

    double xSpacing = 0;
    double ySpacing = 0;

     if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) {
        // XXX: The spacing should vary as the pressure changes along the
        // line.
        // This is a quick simplification.
        xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2);
        ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2);
     }
     else {
         xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT );
         ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT );
     }

    if (xSpacing < 0.5) {
        xSpacing = 0.5;
    }
    if (ySpacing < 0.5) {
        ySpacing = 0.5;
    }

    double xScale = 1;
    double yScale = 1;
    double spacing;
    // Scale x or y so that we effectively have a square brush
    // and calculate distance in that coordinate space. We reverse this scaling
    // before drawing the brush. This produces the correct spacing in both
    // x and y directions, even if the brush's aspect ratio is not 1:1.
    if (xSpacing > ySpacing) {
        yScale = xSpacing / ySpacing;
        spacing = xSpacing;
    }
    else {
        xScale = ySpacing / xSpacing;
        spacing = ySpacing;
    }

    dragVec.setX(dragVec.x() * xScale);
    dragVec.setY(dragVec.y() * yScale);

    double newDist = dragVec.length();
    double dist = savedDist + newDist;
    double l_savedDist = savedDist;

    if (dist < spacing) {
        return dist;
    }

    dragVec.normalize();
    KisVector2D step(0, 0);

    while (dist >= spacing) {
        if (l_savedDist > 0) {
            step += dragVec * (spacing - l_savedDist);
            l_savedDist -= spacing;
        }
        else {
            step += dragVec * spacing;
        }

        KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale));

        double distanceMoved = step.length();
        double t = 0;

        if (newDist > DBL_EPSILON) {
            t = distanceMoved / newDist;
        }

        double pressure = (1 - t) * pressure1 + t * pressure2;
        double xTilt = (1 - t) * xTilt1 + t * xTilt2;
        double yTilt = (1 - t) * yTilt1 + t * yTilt2;

        m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement));
        dist -= spacing;
    }

    if (dist > 0)
        return dist;
    else
        return 0;
}

void KisPainter::paintPolyline (const vKisPoint &points,
                                int index, int numPoints)
{
    if (index >= (int) points.count ())
        return;

    if (numPoints < 0)
        numPoints = points.count ();

    if (index + numPoints > (int) points.count ())
        numPoints = points.count () - index;


    for (int i = index; i < index + numPoints - 1; i++)
    {
        paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1],
               0/*pressure*/, 0, 0);
    }
}

void KisPainter::getBezierCurvePoints(const KisPoint &pos1,
                      const KisPoint &control1,
                      const KisPoint &control2,
                      const KisPoint &pos2,
                      vKisPoint& points)
{
    double d1 = pointToLineDistance(control1, pos1, pos2);
    double d2 = pointToLineDistance(control2, pos1, pos2);

    if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
        points.push_back(pos1);
    } else {
        // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
        KisVector2D p1 = pos1;
        KisVector2D p2 = control1;
        KisVector2D p3 = control2;
        KisVector2D p4 = pos2;

        KisVector2D l2 = (p1 + p2) / 2;
        KisVector2D h = (p2 + p3) / 2;
        KisVector2D l3 = (l2 + h) / 2;
        KisVector2D r3 = (p3 + p4) / 2;
        KisVector2D r2 = (h + r3) / 2;
        KisVector2D l4 = (l3 + r2) / 2;
        KisVector2D r1 = l4;
        KisVector2D l1 = p1;
        KisVector2D r4 = p4;

        getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points);
        getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points);
    }
}

double KisPainter::paintBezierCurve(const KisPoint &pos1,
                    const double pressure1,
                    const double xTilt1,
                    const double yTilt1,
                    const KisPoint &control1,
                    const KisPoint &control2,
                    const KisPoint &pos2,
                    const double pressure2,
                    const double xTilt2,
                    const double yTilt2,
                    const double savedDist)
{
    double newDistance;
    double d1 = pointToLineDistance(control1, pos1, pos2);
    double d2 = pointToLineDistance(control2, pos1, pos2);

    if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
        newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist);
    } else {
        // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
        KisVector2D p1 = pos1;
        KisVector2D p2 = control1;
        KisVector2D p3 = control2;
        KisVector2D p4 = pos2;

        KisVector2D l2 = (p1 + p2) / 2;
        KisVector2D h = (p2 + p3) / 2;
        KisVector2D l3 = (l2 + h) / 2;
        KisVector2D r3 = (p3 + p4) / 2;
        KisVector2D r2 = (h + r3) / 2;
        KisVector2D l4 = (l3 + r2) / 2;
        KisVector2D r1 = l4;
        KisVector2D l1 = p1;
        KisVector2D r4 = p4;

        double midPressure = (pressure1 + pressure2) / 2;
        double midXTilt = (xTilt1 + xTilt2) / 2;
        double midYTilt = (yTilt1 + yTilt2) / 2;

        newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1,
                           l2.toKisPoint(), l3.toKisPoint(),
                           l4.toKisPoint(), midPressure, midXTilt, midYTilt,
                           savedDist);
        newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt,
                           r2.toKisPoint(),
                           r3.toKisPoint(),
                           r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance);
    }

    return newDistance;
}

void KisPainter::paintRect (const KisPoint &startPoint,
                            const KisPoint &endPoint,
                            const double /*pressure*/,
                const double /*xTilt*/,
                const double /*yTilt*/)
{
    KoRect normalizedRect = KisRect (startPoint, endPoint).normalize ();

    vKisPoint points;

    points.push_back(normalizedRect.topLeft());
    points.push_back(normalizedRect.bottomLeft());
    points.push_back(normalizedRect.bottomRight());
    points.push_back(normalizedRect.topRight());

    paintPolygon(points);
}

void KisPainter::paintEllipse (const KisPoint &startPoint,
                               const KisPoint &endPoint,
                               const double /*pressure*/,
                   const double /*xTilt*/,
                   const double /*yTilt*/)
{
    KisRect r = KisRect(startPoint, endPoint).normalize();

    // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
    // kappa = (4/3*(sqrt(2)-1))
    const double kappa = 0.5522847498;
    const double lx = (r.width() / 2) * kappa;
    const double ly = (r.height() / 2) * kappa;

    KisPoint center = r.center();

    KisPoint p0(r.left(), center.y());
    KisPoint p1(r.left(), center.y() - ly);
    KisPoint p2(center.x() - lx, r.top());
    KisPoint p3(center.x(), r.top());

    vKisPoint points;

    getBezierCurvePoints(p0, p1, p2, p3, points);

    KisPoint p4(center.x() + lx, r.top());
    KisPoint p5(r.right(), center.y() - ly);
    KisPoint p6(r.right(), center.y());

    getBezierCurvePoints(p3, p4, p5, p6, points);

    KisPoint p7(r.right(), center.y() + ly);
    KisPoint p8(center.x() + lx, r.bottom());
    KisPoint p9(center.x(), r.bottom());

    getBezierCurvePoints(p6, p7, p8, p9, points);

    KisPoint p10(center.x() - lx, r.bottom());
    KisPoint p11(r.left(), center.y() + ly);

    getBezierCurvePoints(p9, p10, p11, p0, points);

    paintPolygon(points);
}

void KisPainter::paintAt(const KisPoint & pos,
                         const double pressure,
                         const double xTilt,
                         const double yTilt)
{
    if (!m_paintOp) return;
    m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D()));
}

double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
{
    double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
    double distance = 0;

    if (lineLength > DBL_EPSILON) {
        distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
        distance = fabs(distance);
    }

    return distance;
}

/*
 * Concave Polygon Scan Conversion
 * by Paul Heckbert
 * from "Graphics Gems", Academic Press, 1990
 */

/*
 * concave: scan convert nvert-sided concave non-simple polygon with vertices at
 * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by
 * calling spanproc for each visible span of pixels.
 * Polygon can be clockwise or counterclockwise.
 * Algorithm does uniform point sampling at pixel centers.
 * Inside-outside test done by Jordan's rule: a point is considered inside if
 * an emanating ray intersects the polygon an odd number of times.
 * drawproc should fill in pixels from xl to xr inclusive on scanline y,
 * e.g:
 *    drawproc(y, xl, xr)
 *    int y, xl, xr;
 *    {
 *        int x;
 *        for (x=xl; x<=xr; x++)
 *        pixel_write(x, y, pixelvalue);
 *    }
 *
 *  Paul Heckbert    30 June 81, 18 Dec 89
 */

typedef struct {    /* a polygon edge */
    double x;       /* x coordinate of edge's intersection with current scanline */
    double dx;      /* change in x with respect to y */
    int i;            /* edge number: edge i goes from pt[i] to pt[i+1] */
} Edge;

static int n;            /* number of vertices */
static const KisPoint *pt;    /* vertices */

static int nact;        /* number of active edges */
static Edge *active;        /* active edge list:edges crossing scanline y */

/* comparison routines for qsort */
static int compare_ind(const void *pu, const void *pv)
{
    const int *u = static_cast<const int *>(pu);
    const int *v = static_cast<const int *>(pv);

    return pt[*u].y() <= pt[*v].y() ? -1 : 1;
}

static int compare_active(const void *pu, const void *pv)
{
    const Edge *u = static_cast<const Edge *>(pu);
    const Edge *v = static_cast<const Edge *>(pv);

    return u->x <= v->x ? -1 : 1;
}

static void cdelete(int i)        /* remove edge i from active list */
{
    int j;

    for (j=0; j<nact && active[j].i!=i; j++);
    if (j>=nact) return;        /* edge not in active list; happens at win->y0*/
    nact--;
    bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]);
}

static void cinsert(int i, int y)        /* append edge i to end of active list */
{
    int j;
    double dx;
    const KisPoint *p, *q;

    j = i<n-1 ? i+1 : 0;
    if (pt[i].y() < pt[j].y()) {
        p = &pt[i]; q = &pt[j];
    } else {
        p = &pt[j]; q = &pt[i];
    }
    /* initialize x position at intersection of edge with scanline y */
    active[nact].dx = dx = (q->x()-p->x())/(q->y()-p->y());
    active[nact].x = dx*(y+.5-p->y())+p->x();
    active[nact].i = i;
    nact++;
}

void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle)
{
    int nvert = points.count();
    int k, y0, y1, y, i, j, xl, xr;
    int *ind;        /* list of vertex indices, sorted by pt[ind[j]].y */

    n = nvert;
    pt = &(points[0]);
    if (n<3) return;
    if (fillStyle == FillStyleNone) {
        return;
    }

    ind = new int[n];
    TQ_CHECK_PTR(ind);
    active = new Edge[n];
    TQ_CHECK_PTR(active);

    /* create y-sorted array of indices ind[k] into vertex list */
    for (k=0; k<n; k++)
        ind[k] = k;
    qsort(ind, n, sizeof ind[0], compare_ind);  /* sort ind by pt[ind[k]].y */

    nact = 0;                /* start with empty active list */
    k = 0;                    /* ind[k] is next vertex to process */
    y0 = static_cast<int>(ceil(pt[ind[0]].y()-.5));            /* ymin of polygon */
    y1 = static_cast<int>(floor(pt[ind[n-1]].y()-.5));        /* ymax of polygon */

    int x0 = INT_MAX;
    int x1 = INT_MIN;

    for (int i = 0; i < nvert; i++) {
        int pointHighX = static_cast<int>(ceil(points[i].x() - 0.5));
        int pointLowX = static_cast<int>(floor(points[i].x() - 0.5));

        if (pointLowX < x0) {
            x0 = pointLowX;
        }
        if (pointHighX > x1) {
            x1 = pointHighX;
        }
    }

    // Fill the polygon bounding rectangle with the required contents then we'll
    // create a mask for the actual polygon coverage.

    KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon");
    TQ_CHECK_PTR(polygon);

    KisFillPainter fillPainter(polygon);
    TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);

    // Clip to the image bounds.
    if (m_device->image()) {
        boundingRectangle &= m_device->image()->bounds();
    }

    switch (fillStyle) {
    default:
        // Fall through
    case FillStyleGradient:
        // Currently unsupported, fall through
    case FillStyleStrokes:
        // Currently unsupported, fall through
        kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n";
    case FillStyleForegroundColor:
        fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE);
        break;
    case FillStyleBackgroundColor:
        fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE);
        break;
    case FillStylePattern:
        Q_ASSERT(m_pattern != 0);
        fillPainter.fillRect(boundingRectangle, m_pattern);
        break;
    }

    KisSelectionSP polygonMask = new KisSelection(polygon);

    for (y=y0; y<=y1; y++) {        /* step through scanlines */
        /* scanline y is at y+.5 in continuous coordinates */

        /* check vertices between previous scanline and current one, if any */
        for (; k<n && pt[ind[k]].y()<=y+.5; k++) {
            /* to simplify, if pt.y=y+.5, pretend it's above */
            /* invariant: y-.5 < pt[i].y <= y+.5 */
            i = ind[k];
            /*
             * insert or delete edges before and after vertex i (i-1 to i,
             * and i to i+1) from active list if they cross scanline y
             */
            j = i>0 ? i-1 : n-1;        /* vertex previous to i */
            if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
                cdelete(j);
            else if (pt[j].y() > y+.5)    /* new edge, add to active list */
                cinsert(j, y);
            j = i<n-1 ? i+1 : 0;        /* vertex next after i */
            if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
                cdelete(i);
            else if (pt[j].y() > y+.5)    /* new edge, add to active list */
                cinsert(i, y);
        }

        /* sort active edge list by active[j].x */
        qsort(active, nact, sizeof active[0], compare_active);

        /* draw horizontal segments for scanline y */
        for (j=0; j<nact; j+=2) {    /* draw horizontal segments */
            /* span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside */
            xl = static_cast<int>(ceil(active[j].x-.5));        /* left end of span */
            xr = static_cast<int>(floor(active[j+1].x-.5));        /* right end of span */

            if (xl<=xr) {
                KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true);

                while (!it.isDone()) {
                    // We're using a selection here, that means alpha colorspace, that means one byte.
                    it.rawData()[0] = MAX_SELECTED;
                    ++it;
                }
            }

            active[j].x += active[j].dx;        /* increment edge coords */
            active[j+1].x += active[j+1].dx;
        }
    }
    delete [] ind;
    delete [] active;

    polygon->applySelectionMask(polygonMask);

    TQRect r = polygon->extent();

    // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt,
    // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves...
    // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect

    bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height());
}

void KisPainter::paintPolygon(const vKisPoint& points)
{
    if (m_fillStyle != FillStyleNone) {
        fillPolygon(points, m_fillStyle);
    }

    if (m_strokeStyle != StrokeStyleNone) {
        if (points.count() > 1) {
            double distance = -1;

            for (uint i = 0; i < points.count() - 1; i++) {
                distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance);
            }
            paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance);
        }
    }
}

