/************************************************************************
 *
 * Copyright 2010-2013 Jakob Leben (jakob.leben@gmail.com)
 * Copyright 2010 Ivan Leben (ivan.leben@gmail.com) (QPen_ArcTo)
 *
 * This file is part of SuperCollider Qt GUI.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 ************************************************************************/

#include "primitives.h"
#include "../painting.h"
#include "../image.h"
#include "../type_codec.hpp"
#include "PyrKernel.h"

#include <QPainter>
#include <QPainterPath>
#include <QVector2D>
#include <QVector3D>
#include <cmath>

static bool announced = false;
static QObject* paintingObject = NULL;
static QPainter* painter = 0;
static QPainterPath path;

namespace QtCollider {

void announcePainting() { announced = true; }
bool paintingAnnounced() { return announced; }
bool isPaintingObject(QObject* obj) { return obj == paintingObject; }

bool beginPainting(QPainter* p, QObject* obj) {
    if (painter) {
        qcErrorMsg(QStringLiteral("Painting already in progress!"));
        return false;
    }

    painter = p;
    paintingObject = obj;

    painter->setRenderHint(QPainter::Antialiasing, true);
    QColor black(0, 0, 0);
    QPen pen(black);
    pen.setCapStyle(Qt::FlatCap);
    painter->setPen(pen);
    painter->setBrush(black);

    path = QPainterPath();

    return true;
}

void endPainting() {
    painter = 0;
    announced = false;
    paintingObject = NULL;
}

QPainter* globalPainter() { return painter; }
}

typedef QVector2D vec2;
typedef QVector3D vec3;
static const double PI = 3.14159265358979323846264338327950288419717;

inline static qreal globalAngle(const vec2& v) {
    // assuming v is normalized
    qreal cosa = v.x();
    qreal sina = -v.y();
    return sina >= 0.0 ? acosf(cosa) : 2.0 * PI - acosf(cosa);
}

inline static qreal vectorAngle(const vec2& v1, const vec2& v2) {
    // assuming v1,v2 are normalized
    return acosf(vec2::dotProduct(v1, v2));
}

inline static qreal signedAngle(const vec2& v1, const vec2& v2) {
    // assuming v1,v2 are normalized
    qreal a = vectorAngle(v1, v2);
    vec3 c = vec3::crossProduct(vec3(v1), vec3(v2));
    return c.z() > 0.0 ? a : -a;
}

inline static qreal radToDeg(qreal rad) { return rad * 180.0 / PI; }

inline static bool isPenValid() {
    if (!painter) {
        qcErrorMsg(QStringLiteral("Usage of QPen is not allowed at this point!"));
        return false;
    }
    return true;
}

#define QC_QPEN_PRIMITIVE(NAME, ARGC, RECEIVER, ARGS, GLOBAL)                                                          \
    struct NAME {};                                                                                                    \
    template <> struct LangPrimitive<NAME> {                                                                           \
        static int implementation(RECEIVER, ARGS, GLOBAL);                                                             \
        static int mediate(VMGlobals* g, int i) {                                                                      \
            if (!isPenValid())                                                                                         \
                return errFailed;                                                                                      \
            PyrSlot* stack = g->sp - i + 1;                                                                            \
            return implementation(stack, i > 1 ? stack + 1 : 0, g);                                                    \
        }                                                                                                              \
        static void define(int base, int index) { definePrimitive(base, index, "_" #NAME, &mediate, ARGC + 1, 0); }    \
    };                                                                                                                 \
    int LangPrimitive<NAME>::implementation(RECEIVER, ARGS, GLOBAL)

namespace QtCollider {

QC_QPEN_PRIMITIVE(QPen_Save, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    painter->save();
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Restore, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    painter->restore();
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Clear, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    path = QPainterPath();
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_FillColor, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QColor color = QtCollider::get(a);
    painter->setBrush(color);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_StrokeColor, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QColor color = QtCollider::get(a);
    QPen pen = painter->pen();
    pen.setColor(color);
    painter->setPen(pen);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Width, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float width;
    if (slotFloatVal(a, &width))
        return errWrongType;
    QPen pen = painter->pen();
    pen.setWidthF(width);
    painter->setPen(pen);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetJoinStyle, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    int style = QtCollider::get(a);
    QPen pen = painter->pen();

    switch (style) {
    case 0:
        pen.setJoinStyle(Qt::MiterJoin);
        break;
    case 1:
        pen.setJoinStyle(Qt::RoundJoin);
        break;
    case 2:
        pen.setJoinStyle(Qt::BevelJoin);
        break;
    default:
        return errFailed;
    }

    painter->setPen(pen);

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetCapStyle, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    int style = QtCollider::get(a);
    QPen pen = painter->pen();

    switch (style) {
    case 0:
        pen.setCapStyle(Qt::FlatCap);
        break;
    case 1:
        pen.setCapStyle(Qt::RoundCap);
        break;
    case 2:
        pen.setCapStyle(Qt::SquareCap);
        break;
    default:
        return errFailed;
    }

    painter->setPen(pen);

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetDashPattern, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    if (!IsObj(a))
        return errWrongType;
    PyrObject* obj = slotRawObject(a);
    if (obj->classptr != class_floatarray)
        return errWrongType;
    PyrFloatArray* farray = reinterpret_cast<PyrFloatArray*>(obj);

    int s = farray->size;
    float* f = farray->f;
    QVector<qreal> pattern;

    int i = 1;
    while (i < s) {
        pattern << *f << *(f + 1);
        f += 2;
        i += 2;
    }

    if (pattern.size()) {
        QPen pen = painter->pen();
        pen.setDashPattern(pattern);
        painter->setPen(pen);
    }

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetOpacity, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float opacity = QtCollider::get(a);
    painter->setOpacity(opacity);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Clip, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    painter->setClipPath(path);
    path = QPainterPath();
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AntiAliasing, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    bool b = IsTrue(a);
    if (!b && !IsFalse(a))
        return errWrongType;
    painter->setRenderHint(QPainter::Antialiasing, b);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetFont, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    painter->setFont(QtCollider::get(a));
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Translate, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float x, y;
    if (slotFloatVal(a, &x))
        return errWrongType;
    if (slotFloatVal(a + 1, &y))
        return errWrongType;
    painter->translate(x, y);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Scale, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float x, y;
    if (slotFloatVal(a, &x))
        return errWrongType;
    if (slotFloatVal(a + 1, &y))
        return errWrongType;
    painter->scale(x, y);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Shear, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float x, y;
    if (slotFloatVal(a, &x))
        return errWrongType;
    if (slotFloatVal(a + 1, &y))
        return errWrongType;
    painter->shear(x, y);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Rotate, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float angle, x, y;
    if (slotFloatVal(a, &angle))
        return errWrongType;
    if (slotFloatVal(a + 1, &x))
        return errWrongType;
    if (slotFloatVal(a + 2, &y))
        return errWrongType;

    painter->translate(x, y);
    painter->rotate(angle);
    painter->translate(-x, -y);

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_SetTransform, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QVariantList list = QtCollider::get(a);
    if (list.count() < 6)
        return errWrongType;
    float f[6];
    int i = 6;
    while (i) {
        --i;
        const QVariant& var = list[i];
        if (!var.canConvert(QVariant::Double))
            return errWrongType;
        f[i] = var.value<float>();
    }
    QTransform transform(f[0], f[1], f[2], f[3], f[4], f[5]);
    painter->setWorldTransform(transform, true);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Transform, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    const QTransform& transform = painter->worldTransform();

    PyrDoubleArray* doubleArray = newPyrDoubleArray(g->gc, 6, 0, true);
    SetObject(r, doubleArray);

    double* val = doubleArray->d;
    val[0] = transform.m11();
    val[1] = transform.m12();
    val[2] = transform.m21();
    val[3] = transform.m22();
    val[4] = transform.dx();
    val[5] = transform.dy();
    doubleArray->size = 6;

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_MoveTo, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF point = QtCollider::get(a);
    path.moveTo(point);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_LineTo, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF point = QtCollider::get(a);
    path.lineTo(point);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_CubicTo, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF endPoint = QtCollider::get(a);
    QPointF cPoint1 = QtCollider::get(a + 1);
    QPointF cPoint2 = QtCollider::get(a + 2);
    path.cubicTo(cPoint1, cPoint2, endPoint);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_QuadTo, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF endPoint = QtCollider::get(a);
    QPointF cPoint = QtCollider::get(a + 1);
    path.quadTo(cPoint, endPoint);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_ArcTo, 3, PyrSlot* r, PyrSlot* arg, VMGlobals* g) {
    QPointF pt1 = QtCollider::get(arg);
    QPointF pt2 = QtCollider::get(arg + 1);
    float radius;
    if (slotFloatVal(arg + 2, &radius))
        return errWrongType;
    radius = qMax(0.f, radius);

    vec2 a(path.currentPosition());
    vec2 b(pt1);
    vec2 c(pt2);

    vec2 va = (a - b).normalized();
    vec2 vc = (c - b).normalized();
    vec2 m = (va + vc).normalized();

    qreal lineAngle = vectorAngle(va, vc);
    qreal dm = radius / sinf(lineAngle * 0.5f);
    qreal dv = radius / tanf(lineAngle * 0.5f);

    vec2 center = b + dm * m;
    vec2 start = b + dv * va;
    vec2 end = b + dv * vc;

    vec2 vs = (start - center).normalized();
    vec2 ve = (end - center).normalized();

    qreal arcAngle = signedAngle(ve, vs);
    qreal arcStart = globalAngle(vs);

    path.lineTo(start.x(), start.y());

    path.arcTo(center.x() - radius, center.y() - radius, 2 * radius, 2 * radius, radToDeg(arcStart),
               radToDeg(arcAngle));

    // The current SC API does not want to pull the line to the last point.
    // Personally, I think it would be better:

    // path.lineTo( pt2 );

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddRect, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QRectF rect = QtCollider::get(a);
    path.addRect(rect);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddRoundedRect, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    float radiusX, radiusY;
    QRectF rect;

    rect = QtCollider::get(a);
    if (slotFloatVal(a + 1, &radiusX))
        return errWrongType;
    if (slotFloatVal(a + 2, &radiusY))
        return errWrongType;

    path.addRoundedRect(rect, radiusX, radiusY);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddEllipse, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QRectF rect = QtCollider::get(a);
    path.addEllipse(rect);
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddArc, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF center = QtCollider::get(a);
    float radius, startAngle, arcAngle;
    if (slotFloatVal(a + 1, &radius))
        return errWrongType;
    if (slotFloatVal(a + 2, &startAngle))
        return errWrongType;
    if (slotFloatVal(a + 3, &arcAngle))
        return errWrongType;

    // have to swap angle direction for sinf()
    QPointF start(radius * cosf(startAngle), -radius * sinf(startAngle));
    start += center;

    QRectF rect;
    rect.setSize(QSizeF(2 * radius, 2 * radius));
    rect.moveCenter(center);

    path.moveTo(start);
    path.arcTo(rect, radToDeg(startAngle), radToDeg(arcAngle));

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddWedge, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF center = QtCollider::get(a);
    float radius, startAngle, sweepLength;
    if (slotFloatVal(a + 1, &radius))
        return errWrongType;
    if (slotFloatVal(a + 2, &startAngle))
        return errWrongType;
    if (slotFloatVal(a + 3, &sweepLength))
        return errWrongType;
    path.moveTo(center);
    QRectF rect;
    rect.setSize(QSizeF(2 * radius, 2 * radius));
    rect.moveCenter(center);
    path.arcTo(rect, startAngle, sweepLength);
    path.closeSubpath();
    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_AddAnnularWedge, 5, PyrSlot*, PyrSlot* a, VMGlobals* g) {
    QPointF c = QtCollider::get(a);
    float innerRadius, outerRadius, startAngle, arcAngle;
    if (slotFloatVal(a + 1, &innerRadius))
        return errWrongType;
    if (slotFloatVal(a + 2, &outerRadius))
        return errWrongType;
    if (slotFloatVal(a + 3, &startAngle))
        return errWrongType;
    if (slotFloatVal(a + 4, &arcAngle))
        return errWrongType;

    float saDeg = radToDeg(startAngle);
    float aaDeg = radToDeg(arcAngle);

    QPointF start(outerRadius * cosf(startAngle), -outerRadius * sinf(startAngle));
    start += c;
    path.moveTo(start);

    QPointF pt(innerRadius, innerRadius);
    path.arcTo(QRectF(c - pt, c + pt), saDeg, aaDeg);

    pt = QPointF(outerRadius, outerRadius);
    path.arcTo(QRectF(c - pt, c + pt), saDeg + aaDeg, -aaDeg);

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_Draw, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    if (path.isEmpty())
        return errNone;

    int style = QtCollider::get(a);
    QPen pen = painter->pen();
    QBrush brush = painter->brush();

    switch (style) {
    case 0:
    case 1:
        painter->setPen(Qt::NoPen);
        break;
    case 2:
        painter->setBrush(Qt::NoBrush);
        break;
    case 3:
    case 4:
    default:;
    }
    if (style == 1 || style == 4) {
        path.setFillRule(Qt::OddEvenFill);
    } else {
        path.setFillRule(Qt::WindingFill);
    }

    painter->drawPath(path);

    path = QPainterPath();

    painter->setPen(pen);
    painter->setBrush(brush);

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_FillAxialGradient, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF pt1 = QtCollider::get(a + 0);
    QPointF pt2 = QtCollider::get(a + 1);
    QColor c1 = QtCollider::get(a + 2);
    QColor c2 = QtCollider::get(a + 3);

    QLinearGradient grad(pt1, pt2);
    grad.setColorAt(0, c1);
    grad.setColorAt(1, c2);

    QPen pen = painter->pen();
    QBrush brush = painter->brush();

    painter->setPen(Qt::NoPen);
    painter->setBrush(grad);

    painter->drawPath(path);

    painter->setPen(pen);
    painter->setBrush(brush);

    path = QPainterPath();

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_FillRadialGradient, 6, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QPointF pt1 = QtCollider::get(a + 0);
    QPointF pt2 = QtCollider::get(a + 1);
    float r1 = QtCollider::get(a + 2);
    float r2 = QtCollider::get(a + 3);
    QColor c1 = QtCollider::get(a + 4);
    QColor c2 = QtCollider::get(a + 5);

    QRadialGradient grad(pt2, r2, pt1);
    grad.setColorAt((r2 > 0 ? r1 / r2 : 0), c1);
    grad.setColorAt(1, c2);

    QPen pen = painter->pen();
    QBrush brush = painter->brush();

    painter->setPen(Qt::NoPen);
    painter->setBrush(grad);

    painter->drawPath(path);

    painter->setPen(pen);
    painter->setBrush(brush);

    path = QPainterPath();

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_StringAtPoint, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QString str = QtCollider::get(a);
    if (str.isEmpty())
        return errNone;
    QPointF pt = QtCollider::get(a + 1);

    painter->save();
    if (NotNil(a + 2))
        painter->setFont(QtCollider::get(a + 2));
    QPen pen(painter->pen());
    pen.setColor(NotNil(a + 3) ? QtCollider::get(a + 3) : painter->brush().color());
    painter->setPen(pen);

    QFont f(painter->font());
    QFontMetrics fm(f);
    QRect rect = fm.boundingRect(str);
    painter->drawText(pt - rect.topLeft(), str);

    painter->restore();

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_StringInRect, 5, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QString str = QtCollider::get(a);
    if (str.isEmpty())
        return errNone;
    QRectF rect = QtCollider::get(a + 1);

    painter->save();
    if (NotNil(a + 2))
        painter->setFont(QtCollider::get(a + 2));
    QPen pen(painter->pen());
    pen.setColor(NotNil(a + 3) ? QtCollider::get(a + 3) : painter->brush().color());
    painter->setPen(pen);

    Qt::Alignment align;
    if (NotNil(a + 4))
        align = static_cast<Qt::Alignment>(QtCollider::get<int>(a + 4));
    else
        align = Qt::AlignTop | Qt::AlignLeft;

    painter->drawText(rect, align, str);

    painter->restore();

    return errNone;
}

QC_QPEN_PRIMITIVE(QPen_DrawImage, 5, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QtCollider::SharedImage image = QtCollider::get(a + 1);
    if (!image)
        return errWrongType;

    if (image->isPainting()) {
        qcErrorMsg("QImage: can not draw while being painted.");
        return errFailed;
    }

    QPixmap& pixmap = image->pixmap();

    QRectF source;

    if (IsNil(a + 2))
        source = QRectF(pixmap.rect());
    else if (isKindOfSlot(a + 2, SC_CLASS(Rect)))
        source = QtCollider::read<QRectF>(a + 2);
    else
        return errWrongType;

    int composition = QtCollider::get(a + 3);
    float opacity = QtCollider::get(a + 4);

    painter->save();
    painter->setCompositionMode((QPainter::CompositionMode)composition);
    painter->setRenderHint(QPainter::SmoothPixmapTransform, image->transformationMode == Qt::SmoothTransformation);
    painter->setOpacity(opacity);

    int result = errNone;

    if (isKindOfSlot(a + 0, SC_CLASS(Point))) {
        QPointF point = QtCollider::read<QPointF>(a + 0);
        painter->drawPixmap(point, pixmap, source);
    } else if (isKindOfSlot(a + 0, SC_CLASS(Rect))) {
        QRectF target = QtCollider::read<QRectF>(a + 0);
        painter->drawPixmap(target, pixmap, source);
    } else {
        result = errWrongType;
    }

    painter->restore();

    return result;
}

QC_QPEN_PRIMITIVE(QPen_TileImage, 5, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
    QtCollider::SharedImage image = QtCollider::get(a + 1);
    if (!image)
        return errWrongType;

    if (image->isPainting()) {
        qcErrorMsg("QImage: can not draw while being painted.");
        return errFailed;
    }

    QPixmap& pixmap = image->pixmap();

    QRectF source;

    if (IsNil(a + 2))
        source = QRectF(pixmap.rect());
    else if (isKindOfSlot(a + 2, SC_CLASS(Rect)))
        source = QtCollider::read<QRectF>(a + 2);
    else
        return errWrongType;

    QRectF target;

    if (isKindOfSlot(a + 0, SC_CLASS(Rect)))
        target = QtCollider::read<QRectF>(a + 0);
    else
        return errWrongType;

    if (target.isEmpty() || source.isEmpty())
        return errNone;

    int composition = QtCollider::get(a + 3);
    float opacity = QtCollider::get(a + 4);

    painter->save();
    painter->setCompositionMode((QPainter::CompositionMode)composition);
    painter->setRenderHint(QPainter::SmoothPixmapTransform, image->transformationMode == Qt::SmoothTransformation);
    painter->setOpacity(opacity);
    painter->setClipRect(target);

    QRectF area = source;
    area.moveTo(target.topLeft());
    float max_x = target.right();
    float max_y = target.bottom();
    int iter_x, iter_y;
    for (iter_y = 0;; ++iter_y) {
        area.moveTop(iter_y * source.height() + target.top());
        if (area.top() > max_y)
            break;
        for (iter_x = 0;; ++iter_x) {
            area.moveLeft(iter_x * source.width() + target.left());
            if (area.left() > max_x)
                break;
            painter->drawPixmap(area, pixmap, source);
        }
    }

    painter->restore();

    return errNone;
}

void defineQPenPrimitives() {
    LangPrimitiveDefiner definer;
    definer.define<QPen_Save>();
    definer.define<QPen_Restore>();
    definer.define<QPen_Clear>();
    definer.define<QPen_FillColor>();
    definer.define<QPen_StrokeColor>();
    definer.define<QPen_Width>();
    definer.define<QPen_SetJoinStyle>();
    definer.define<QPen_SetCapStyle>();
    definer.define<QPen_SetDashPattern>();
    definer.define<QPen_SetOpacity>();
    definer.define<QPen_Clip>();
    definer.define<QPen_AntiAliasing>();
    definer.define<QPen_SetFont>();
    definer.define<QPen_Translate>();
    definer.define<QPen_Scale>();
    definer.define<QPen_Shear>();
    definer.define<QPen_Rotate>();
    definer.define<QPen_Transform>();
    definer.define<QPen_SetTransform>();
    definer.define<QPen_MoveTo>();
    definer.define<QPen_LineTo>();
    definer.define<QPen_CubicTo>();
    definer.define<QPen_QuadTo>();
    definer.define<QPen_ArcTo>();
    definer.define<QPen_AddRect>();
    definer.define<QPen_AddRoundedRect>();
    definer.define<QPen_AddEllipse>();
    definer.define<QPen_AddArc>();
    definer.define<QPen_AddWedge>();
    definer.define<QPen_AddAnnularWedge>();
    definer.define<QPen_Draw>();
    definer.define<QPen_FillAxialGradient>();
    definer.define<QPen_FillRadialGradient>();
    definer.define<QPen_StringAtPoint>();
    definer.define<QPen_StringInRect>();
    definer.define<QPen_DrawImage>();
    definer.define<QPen_TileImage>();
}

} // namespace QtCollider
