# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations

from PySide6.QtCore import Signal, Property, QSize
from PySide6.QtGui import QColor
from PySide6.QtQuick3D import QQuick3DTextureData
from PySide6.QtQml import QmlElement

QML_IMPORT_NAME = "ProceduralTextureModule"
QML_IMPORT_MAJOR_VERSION = 1


@QmlElement
class GradientTexture(QQuick3DTextureData):

    heightChanged = Signal(int)
    widthChanged = Signal(int)
    startColorChanged = Signal(QColor)
    endColorChanged = Signal(QColor)

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self._height = 256
        self._width = 256
        self._startcolor = QColor("#d4fc79")
        self._endcolor = QColor("#96e6a1")
        self.updateTexture()

    @Property(int, notify=heightChanged)
    def height(self):
        return self._height

    @height.setter
    def height(self, val):
        if self._height == val:
            return
        self._height = val
        self.updateTexture()
        self.heightChanged.emit(self._height)

    @Property(int, notify=widthChanged)
    def width(self):
        return self._width

    @width.setter
    def width(self, val):
        if self._width == val:
            return
        self._width = val
        self.updateTexture()
        self.widthChanged.emit(self._width)

    @Property(QColor, notify=startColorChanged)
    def startColor(self):
        return self._startcolor

    @startColor.setter
    def startColor(self, val):
        if self._startcolor == val:
            return
        self._startcolor = val
        self.updateTexture()
        self.startColorChanged.emit(self._startcolor)

    @Property(QColor, notify=endColorChanged)
    def endColor(self):
        return self._endcolor

    @endColor.setter
    def endColor(self, val):
        if self._endcolor == val:
            return
        self._endcolor = val
        self.updateTexture()
        self.endColorChanged.emit(self._endcolor)

    def updateTexture(self):
        self.setSize(QSize(self._width, self._height))
        self.setFormat(QQuick3DTextureData.Format.RGBA8)
        self.setHasTransparency(False)
        self.setTextureData(self.generate_texture())

    def generate_texture(self):
        # Generate a horizontal gradient by interpolating between start and end colors.
        gradientScanline = [
            self.linear_interpolate(self._startcolor, self._endcolor, x / self._width)
            for x in range(self._width)
        ]
        # Convert the gradient colors to a flattened list of RGBA values.
        flattenedGradient = [
            component
            for color in gradientScanline
            for component in (color.red(), color.green(), color.blue(), 255)
        ]
        # Repeat the gradient vertically to form the texture.
        return bytearray(flattenedGradient * self._height)

    def linear_interpolate(self, color1, color2, value):
        output = QColor()

        output.setRedF(color1.redF() + (value * (color2.redF() - color1.redF())))
        output.setGreenF(color1.greenF() + (value * (color2.greenF() - color1.greenF())))
        output.setBlueF(color1.blueF() + (value * (color2.blueF() - color1.blueF())))

        return output
