// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qsgopenvgpublicnodes.h"
#include "qsgopenvghelpers.h"
#include <VG/openvg.h>
#include <QtGui/QPixmap>

QT_BEGIN_NAMESPACE

QSGOpenVGRectangleNode::QSGOpenVGRectangleNode()
{
    // Set Dummy material and geometry to avoid asserts
    setMaterial((QSGMaterial*)1);
    setGeometry((QSGGeometry*)1);

    m_rectPath = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1, 0, 0, 0,
                              VG_PATH_CAPABILITY_APPEND_TO);
    m_rectPaint = vgCreatePaint();
}

QSGOpenVGRectangleNode::~QSGOpenVGRectangleNode()
{
    vgDestroyPaint(m_rectPaint);
    vgDestroyPath(m_rectPath);
}

void QSGOpenVGRectangleNode::setRect(const QRectF &rect)
{
    m_rect = rect;
    m_pathDirty = true;
    markDirty(DirtyMaterial);
}

QRectF QSGOpenVGRectangleNode::rect() const
{
    return m_rect;
}

void QSGOpenVGRectangleNode::setColor(const QColor &color)
{
    m_color = color;
    m_paintDirty = true;
    markDirty(DirtyMaterial);
}

QColor QSGOpenVGRectangleNode::color() const
{
    return m_color;
}

void QSGOpenVGRectangleNode::setTransform(const QOpenVGMatrix &transform)
{
    // if there transform matrix is not affine, regenerate the path
    if (transform.isAffine())
        m_pathDirty = true;
    markDirty(DirtyGeometry);

    QSGOpenVGRenderable::setTransform(transform);
}

void QSGOpenVGRectangleNode::render()
{
    // Set Transform
    if (transform().isAffine()) {
        // Use current transform matrix
        vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
        vgLoadMatrix(transform().constData());
    } else {
        // map the path's to handle the perspective matrix
        vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
        vgLoadIdentity();
    }

    if (m_pathDirty) {
        vgClearPath(m_rectPath, VG_PATH_CAPABILITY_APPEND_TO);

        if (transform().isAffine()) {
        // Create command list
        static const VGubyte rectCommands[] = {
            VG_MOVE_TO_ABS,
            VG_HLINE_TO_REL,
            VG_VLINE_TO_REL,
            VG_HLINE_TO_REL,
            VG_CLOSE_PATH
        };

        // Create command data
        QVector<VGfloat> coordinates(5);
        coordinates[0] = m_rect.x();
        coordinates[1] = m_rect.y();
        coordinates[2] = m_rect.width();
        coordinates[3] = m_rect.height();
        coordinates[4] = -m_rect.width();

        vgAppendPathData(m_rectPath, 5, rectCommands, coordinates.constData());

        } else {
            // Pre-transform path
            static const VGubyte rectCommands[] = {
                VG_MOVE_TO_ABS,
                VG_LINE_TO_ABS,
                VG_LINE_TO_ABS,
                VG_LINE_TO_ABS,
                VG_CLOSE_PATH
            };

            QVector<VGfloat> coordinates(8);
            const QPointF topLeft = transform().map(m_rect.topLeft());
            const QPointF topRight = transform().map(m_rect.topRight());
            const QPointF bottomLeft = transform().map(m_rect.bottomLeft());
            const QPointF bottomRight = transform().map(m_rect.bottomRight());
            coordinates[0] = bottomLeft.x();
            coordinates[1] = bottomLeft.y();
            coordinates[2] = bottomRight.x();
            coordinates[3] = bottomRight.y();
            coordinates[4] = topRight.x();
            coordinates[5] = topRight.y();
            coordinates[6] = topLeft.x();
            coordinates[7] = topLeft.y();

            vgAppendPathData(m_rectPath, 5, rectCommands, coordinates.constData());
        }

        m_pathDirty = false;
    }

    if (m_paintDirty) {
        vgSetPaint(m_rectPaint, VG_FILL_PATH);
        vgSetParameteri(m_rectPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
        vgSetParameterfv(m_rectPaint, VG_PAINT_COLOR, 4, QSGOpenVGHelpers::qColorToVGColor(m_color).constData());

        m_paintDirty = false;
    }

    vgSetPaint(m_rectPaint, VG_FILL_PATH);
    vgDrawPath(m_rectPath, VG_FILL_PATH);

}

QSGOpenVGImageNode::QSGOpenVGImageNode() : m_texture(nullptr), m_owns(false)
{
    // Set Dummy material and geometry to avoid asserts
    setMaterial((QSGMaterial*)1);
    setGeometry((QSGGeometry*)1);

}

QSGOpenVGImageNode::~QSGOpenVGImageNode()
{
    if (m_owns)
        delete m_texture;
}

void QSGOpenVGImageNode::setRect(const QRectF &rect)
{
    m_rect = rect;
    markDirty(DirtyMaterial);
}

QRectF QSGOpenVGImageNode::rect() const
{
    return m_rect;
}

void QSGOpenVGImageNode::setSourceRect(const QRectF &r)
{
    m_sourceRect = r;
}

QRectF QSGOpenVGImageNode::sourceRect() const
{
    return m_sourceRect;
}

void QSGOpenVGImageNode::setTexture(QSGTexture *texture)
{
    if (m_owns)
        delete m_texture;
    m_texture = texture;
    markDirty(DirtyMaterial);
}

QSGTexture *QSGOpenVGImageNode::texture() const
{
    return m_texture;
}

void QSGOpenVGImageNode::setFiltering(QSGTexture::Filtering filtering)
{
    m_filtering = filtering;
    markDirty(DirtyMaterial);
}

QSGTexture::Filtering QSGOpenVGImageNode::filtering() const
{
    return m_filtering;
}

void QSGOpenVGImageNode::setMipmapFiltering(QSGTexture::Filtering)
{
}

QSGTexture::Filtering QSGOpenVGImageNode::mipmapFiltering() const
{
    return QSGTexture::None;
}

void QSGOpenVGImageNode::setAnisotropyLevel(QSGTexture::AnisotropyLevel)
{
}

QSGTexture::AnisotropyLevel QSGOpenVGImageNode::anisotropyLevel() const
{
    return QSGTexture::AnisotropyNone;
}

void QSGOpenVGImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode)
{
    if (m_transformMode == transformNode)
        return;
    m_transformMode = transformNode;
    markDirty(DirtyGeometry);
}

QSGImageNode::TextureCoordinatesTransformMode QSGOpenVGImageNode::textureCoordinatesTransform() const
{
    return m_transformMode;
}

void QSGOpenVGImageNode::setOwnsTexture(bool owns)
{
    m_owns = owns;
}

bool QSGOpenVGImageNode::ownsTexture() const
{
    return m_owns;
}

void QSGOpenVGImageNode::render()
{
    if (!m_texture) {
        return;
    }

    // Set Draw Mode
    if (opacity() < 1.0) {
        //Transparent
        vgSetPaint(opacityPaint(), VG_FILL_PATH);
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY);
    } else {
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
    }

    // Set Transform
    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadMatrix(transform().constData());

    VGImage image = static_cast<VGImage>(m_texture->comparisonKey());

    //Apply the TextureCoordinateTransform Flag
    if (m_transformMode != QSGImageNode::NoTransform) {
        float translateX = 0.0f;
        float translateY = 0.0f;
        float scaleX = 1.0f;
        float scaleY = 1.0f;

        if (m_transformMode & QSGImageNode::MirrorHorizontally) {
            translateX = m_rect.width();
            scaleX = -1.0;
        }

        if (m_transformMode & QSGImageNode::MirrorVertically) {
            translateY = m_rect.height();
            scaleY = -1.0;
        }

        vgTranslate(translateX, translateY);
        vgScale(scaleX, scaleY);
    }

    // If the the source rect is the same as the target rect
    if (m_sourceRect == m_rect) {
        vgDrawImage(image);
    } else {
        // Scale
        float scaleX = m_rect.width() / m_sourceRect.width();
        float scaleY = m_rect.height() / m_sourceRect.height();
        vgScale(scaleX, scaleY);
        VGImage subImage = vgChildImage(image, m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.width(), m_sourceRect.height());
        vgDrawImage(subImage);
        vgDestroyImage(subImage);
    }

}

QSGOpenVGNinePatchNode::QSGOpenVGNinePatchNode() : m_texture(nullptr)
{
    // Set Dummy material and geometry to avoid asserts
    setMaterial((QSGMaterial*)1);
    setGeometry((QSGGeometry*)1);

}

QSGOpenVGNinePatchNode::~QSGOpenVGNinePatchNode()
{
    delete m_texture;
}

void QSGOpenVGNinePatchNode::setTexture(QSGTexture *texture)
{
    delete m_texture;
    m_texture = texture;
    markDirty(DirtyMaterial);
}

void QSGOpenVGNinePatchNode::setBounds(const QRectF &bounds)
{
    if (m_bounds == bounds)
        return;
    m_bounds = bounds;
    markDirty(DirtyGeometry);
}

void QSGOpenVGNinePatchNode::setDevicePixelRatio(qreal ratio)
{
    if (m_pixelRatio == ratio)
        return;
    m_pixelRatio = ratio;
    markDirty(DirtyGeometry);
}

void QSGOpenVGNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
{
    QMarginsF margins(left, top, right, bottom);
    if (m_margins == margins)
        return;
    m_margins = margins;
    markDirty(DirtyGeometry);
}

void QSGOpenVGNinePatchNode::update()
{

}

void QSGOpenVGNinePatchNode::render()
{
    if (!m_texture)
        return;

    // Set Draw Mode
    if (opacity() < 1.0) {
        //Transparent
        vgSetPaint(opacityPaint(), VG_FILL_PATH);
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY);
    } else {
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
    }

    // Set Transform
    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    vgLoadMatrix(transform().constData());

    VGImage image = static_cast<VGImage>(m_texture->comparisonKey());

    //Draw borderImage
    QSGOpenVGHelpers::qDrawBorderImage(image, m_texture->textureSize(), m_bounds, m_bounds.marginsRemoved(m_margins), QRectF(0, 0, 1, 1));
}

QRectF QSGOpenVGNinePatchNode::bounds() const
{
    return m_bounds;
}


QT_END_NAMESPACE
