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

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick3D
import QtQuick3D.Helpers.impl

Pane {
    id: root
    property var source: null
    property bool resourceDetailsVisible: false
    opacity: 0.9

    ColumnLayout {
        id: layout
        RowLayout {
            Label {
                Layout.fillWidth: true
                text: root.source.renderStats.fps + " FPS"
                font.pointSize: 14
            }

            Label {
                text: "Details"
            }

            CheckBox {
                checked: root.resourceDetailsVisible
                onCheckedChanged: {
                    resourceDetailsVisible = checked;
                }
            }
        }

        component TimeLabel : RowLayout {
            id: timeLabel
            property alias text: label.text
            property real value: 0.0
            Label {
                id: label
                Layout.fillWidth: true
                text: "Frame: "

            }
            Label {
                text: timeLabel.value.toFixed(3) + "ms"
            }
        }

        TimeLabel {
            text: "Frame: "
            value: root.source.renderStats.frameTime
        }

        TimeLabel {
            text: "    Sync: "
            value: root.source.renderStats.syncTime
        }

        TimeLabel {
            text: "    Prep: "
            value: root.source.renderStats.renderPrepareTime
        }

        TimeLabel {
            text: "    Render: "
            value: root.source.renderStats.renderTime
        }

        TimeLabel {
            text: "Max: "
            value: root.source.renderStats.maxFrameTime
        }

        TimeLabel {
            text: "GPU: "
            value: root.source.renderStats.lastCompletedGpuTime
            visible: root.source.renderStats.lastCompletedGpuTime > 0
        }

        Page {
            Layout.fillWidth: true
            Layout.minimumWidth: 530
            visible: root.resourceDetailsVisible
            header: TabBar {
                id: tabBar
                TabButton {
                    text: "Summary"
                }
                TabButton {
                    text: "Passes"
                }
                TabButton {
                    text: "Textures"
                }
                TabButton {
                    text: "Meshes"
                }
                TabButton {
                    text: "Tools"
                }
                TabButton {
                    text: "Shadows"
                }
            }

            StackLayout {
                anchors.fill: parent
                anchors.margins: 10
                currentIndex: tabBar.currentIndex

                Pane {
                    id: summaryPane
                    ColumnLayout {
                        Label {
                            text: "Graphics API: " + root.source.renderStats.graphicsApiName
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: root.source.renderStats.renderPassCount + " render passes"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: root.source.renderStats.drawCallCount + " draw calls"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: root.source.renderStats.drawVertexCount + " vertices"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Image assets: " + (root.source.renderStats.imageDataSize / 1024).toFixed(2) + " KB"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Mesh assets: " + (root.source.renderStats.meshDataSize / 1024).toFixed(2) + " KB"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Pipelines: " + root.source.renderStats.pipelineCount
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Material build time: " + root.source.renderStats.materialGenerationTime + " ms"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Effect build time: " + root.source.renderStats.effectGenerationTime + " ms"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: "Pipeline build time: " + root.source.renderStats.pipelineCreationTime + " ms"
                            visible: root.resourceDetailsVisible
                        }
                        Label {
                            text: root.source.renderStats.vmemAllocCount + " vmem allocs with " + root.source.renderStats.vmemUsedBytes + " bytes"
                            visible: root.resourceDetailsVisible && root.source.renderStats.vmemAllocCount > 0
                        }
                    }
                }

                Pane {
                    id: passesPane
                    RenderStatsPassesModel {
                        id: passesModel
                        passData: root.source.renderStats.renderPassDetails
                    }
                    ColumnLayout {
                        anchors.fill: parent
                        spacing: 0
                        HorizontalHeaderView {
                            syncView: passesTableView
                            resizableColumns: false // otherwise QTBUG-111013 happens
                            boundsBehavior: Flickable.StopAtBounds
                            flickableDirection: Flickable.VerticalFlick
                        }
                        ListModel {
                            id: passesHeaderModel
                            ListElement {
                                columnWidth: 300 // name
                            }
                            ListElement {
                                columnWidth: 80 // size
                            }
                            ListElement {
                                columnWidth: 60 // vertices
                            }
                            ListElement {
                                columnWidth: 60 // draw calls
                            }
                        }
                        Item {
                            Layout.fillHeight: true
                            Layout.fillWidth: true
                            TableView {
                                id: passesTableView
                                anchors.fill: parent
                                // name, size, vertices, draw calls
                                property var columnFactors: [58, 14, 12, 12]; // == 96, leave space for the scrollbar
                                columnWidthProvider: function (column) {
                                    return passesPane.width * (columnFactors[column] / 100.0);
                                }
                                boundsBehavior: Flickable.StopAtBounds
                                flickableDirection: Flickable.VerticalFlick
                                ScrollBar.vertical: ScrollBar {
                                    parent: passesTableView.parent
                                    anchors.top: passesTableView.top
                                    anchors.bottom: passesTableView.bottom
                                    anchors.left: passesTableView.right
                                }
                                clip: true
                                model: passesModel
                                columnSpacing: 1
                                rowSpacing: 1
                                implicitWidth: parent.width + columnSpacing
                                implicitHeight: parent.height + rowSpacing
                                delegate: CustomTableItemDelegate {
                                    required property string display
                                    text: display
                                    color: TableView.view.palette.base
                                    textColor: TableView.view.palette.text
                                }
                            }
                        }
                    }
                }

                Pane {
                    id: texturesPane
                    RenderStatsTexturesModel {
                        id: texturesModel
                        textureData: root.source.renderStats.textureDetails
                    }
                    ColumnLayout {
                        anchors.fill: parent
                        spacing: 0
                        HorizontalHeaderView {
                            syncView: texturesTableView
                            resizableColumns: false // otherwise QTBUG-111013 happens
                            boundsBehavior: Flickable.StopAtBounds
                            flickableDirection: Flickable.VerticalFlick
                        }
                        Item {
                            Layout.fillHeight: true
                            Layout.fillWidth: true
                            TableView {
                                id: texturesTableView
                                anchors.fill: parent
                                // name, size, format, miplevels, flags
                                property var columnFactors: [48, 12, 12, 12, 12]; // == 96, leave space for the scrollbar
                                columnWidthProvider: function (column) {
                                    return texturesPane.width * (columnFactors[column] / 100.0);
                                }
                                boundsBehavior: Flickable.StopAtBounds
                                flickableDirection: Flickable.VerticalFlick
                                ScrollBar.vertical: ScrollBar {
                                    parent: texturesTableView.parent
                                    anchors.top: texturesTableView.top
                                    anchors.bottom: texturesTableView.bottom
                                    anchors.left: texturesTableView.right
                                }
                                ScrollBar.horizontal: ScrollBar { }
                                clip: true
                                model: texturesModel
                                columnSpacing: 1
                                rowSpacing: 1
                                implicitWidth: parent.width + columnSpacing
                                implicitHeight: parent.height + rowSpacing
                                delegate: CustomTableItemDelegate {
                                    required property string display
                                    text: display
                                    color: TableView.view.palette.base
                                    textColor: TableView.view.palette.text
                                }
                            }
                        }
                    }
                }

                Pane {
                    id: meshesPane
                    RenderStatsMeshesModel {
                        id: meshesModel
                        meshData: root.source.renderStats.meshDetails
                    }
                    ColumnLayout {
                        anchors.fill: parent
                        spacing: 0
                        HorizontalHeaderView {
                            syncView: meshesTableView
                            resizableColumns: false // otherwise QTBUG-111013 happens
                            boundsBehavior: Flickable.StopAtBounds
                            flickableDirection: Flickable.VerticalFlick
                        }
                        Item {
                            Layout.fillHeight: true
                            Layout.fillWidth: true
                            TableView {
                                id: meshesTableView
                                anchors.fill: parent
                                // name, submeshes, vertices, vbufsize, ibufsize
                                property var columnFactors: [48, 12, 12, 12, 12]; // == 96, leave space for the scrollbar
                                columnWidthProvider: function (column) {
                                    return meshesPane.width * (columnFactors[column] / 100.0);
                                }
                                boundsBehavior: Flickable.StopAtBounds
                                flickableDirection: Flickable.VerticalFlick
                                ScrollBar.vertical: ScrollBar {
                                    parent: meshesTableView.parent
                                    anchors.top: meshesTableView.top
                                    anchors.bottom: meshesTableView.bottom
                                    anchors.left: meshesTableView.right
                                }
                                clip: true
                                model: meshesModel
                                columnSpacing: 1
                                rowSpacing: 1
                                implicitWidth: parent.width + columnSpacing
                                implicitHeight: parent.height + rowSpacing
                                delegate: CustomTableItemDelegate {
                                    required property string display
                                    text: display
                                    color: TableView.view.palette.base
                                    textColor: TableView.view.palette.text
                                }
                            }
                        }
                    }
                }

                Pane {
                    id: visualizePane
                    ColumnLayout {
                        id: visCtrCol
                        width: parent.width
                        CheckBox {
                            text: "Wireframe mode"
                            onCheckedChanged: root.source.environment.debugSettings.wireframeEnabled = checked
                        }
                        RowLayout {
                            Label {
                                text: "Material override"
                            }
                            ComboBox {
                                id: materialOverrideComboBox
                                textRole: "text"
                                valueRole: "value"
                                implicitContentWidthPolicy: ComboBox.WidestText
                                onActivated: root.source.environment.debugSettings.materialOverride = currentValue
                                Component.onCompleted: materialOverrideComboBox.currentIndex = materialOverrideComboBox.indexOfValue(root.source.environment.debugSettings.materialOverride)
                                model: [
                                    { value: DebugSettings.None, text: "None"},
                                    { value: DebugSettings.BaseColor, text: "Base Color"},
                                    { value: DebugSettings.Roughness, text: "Roughness"},
                                    { value: DebugSettings.Metalness, text: "Metalness"},
                                    { value: DebugSettings.Diffuse, text: "Diffuse"},
                                    { value: DebugSettings.Specular, text: "Specular"},
                                    { value: DebugSettings.ShadowOcclusion, text: "Shadow Occlusion"},
                                    { value: DebugSettings.Emission, text: "Emission"},
                                    { value: DebugSettings.AmbientOcclusion, text: "Ambient Occlusion"},
                                    { value: DebugSettings.Normals, text: "Normals"},
                                    { value: DebugSettings.Tangents, text: "Tangents"},
                                    { value: DebugSettings.Binormals, text: "Binormals"},
                                    { value: DebugSettings.F0, text: "F0"}
                                ]
                            }
                        }
                        RowLayout {
                            spacing: 20
                            Button {
                                text: "Release cached resources"
                                onClicked: root.source.renderStats.releaseCachedResources()
                            }
                            Button {
                                text: "Bake lightmap"
                                onClicked: root.source.bakeLightmap()
                            }
                            Button {
                                text: "Denoise lightmap"
                                onClicked: root.source.denoiseLightmap()
                            }
                        }
                        RowLayout {
                            Label {
                                text: "Render mode override"
                            }
                            ComboBox {
                                id: renderModeOverrideComboBox
                                textRole: "text"
                                valueRole: "value"
                                implicitContentWidthPolicy: ComboBox.WidestText
                                onActivated: root.source.renderMode = currentValue
                                Component.onCompleted: renderModeOverrideComboBox.currentIndex = renderModeOverrideComboBox.indexOfValue(root.source.renderMode)
                                model: [
                                    { value: View3D.Offscreen, text: "Offscreen" },
                                    { value: View3D.Underlay, text: "Underlay" },
                                    { value: View3D.Overlay, text: "Overlay" },
                                    { value: View3D.Inline, text: "Inline" }
                                ]
                            }
                        }

                        Label {
                            text: "View3D logical size is " + root.source.width + "x" + root.source.height
                        }
                        Label {
                            text: "Backing texture pixel size is " + root.source.effectiveTextureSize.width + "x" + root.source.effectiveTextureSize.height
                            visible: root.source.renderMode === View3D.Offscreen
                        }
                        RowLayout {
                            CheckBox {
                                id: explicitTextureSizeCheckBox
                                visible: root.source.renderMode === View3D.Offscreen
                                text: "Explicit backing texture size"
                                property real aspectRatio: root.source.width / root.source.height
                                onCheckedChanged: updateSize()
                                function updateSize() {
                                    if (!explicitTextureSizeCheckBox.checked) {
                                        root.source.explicitTextureWidth = 0;
                                        root.source.explicitTextureHeight = 0;
                                        return;
                                    }
                                    var newWidth = explicitWidthSlider.value;
                                    var newHeight = explicitHeightSlider.value;
                                    if (keepAspectRatioCheckBox.checked) {
                                        var aspectRatio = explicitTextureSizeCheckBox.aspectRatio;
                                        if (newHeight * aspectRatio <= newWidth)
                                            newWidth = newHeight * aspectRatio;
                                        else
                                            newHeight = newWidth * (1.0 / aspectRatio);
                                    }
                                    root.source.explicitTextureWidth = newWidth;
                                    root.source.explicitTextureHeight = newHeight;
                                }
                                Connections {
                                    target: root.source
                                    function onWidthChanged() { explicitTextureSizeCheckBox.updateSize() }
                                    function onHeightChanged() { explicitTextureSizeCheckBox.updateSize() }
                                }
                            }
                            CheckBox {
                                id: keepAspectRatioCheckBox
                                visible: root.source.renderMode === View3D.Offscreen && explicitTextureSizeCheckBox.checked
                                text: "Keep aspect ratio (" + explicitTextureSizeCheckBox.aspectRatio.toFixed(2) + ")"
                                checked: false
                                onCheckedChanged: explicitTextureSizeCheckBox.updateSize()
                            }
                        }
                        RowLayout {
                            visible: root.source.renderMode === View3D.Offscreen && explicitTextureSizeCheckBox.checked
                            Label {
                                text: "Width: " + explicitWidthSlider.value.toFixed(0) + " px"
                            }
                            Slider {
                                id: explicitWidthSlider
                                from: 16
                                to: 4096
                                value: 1280
                                onValueChanged: explicitTextureSizeCheckBox.updateSize()
                                Layout.maximumWidth: 120
                            }
                            Label {
                                text: "Height: " + explicitHeightSlider.value.toFixed(0) + " px"
                            }
                            Slider {
                                id: explicitHeightSlider
                                from: 16
                                to: 4096
                                value: 720
                                onValueChanged: explicitTextureSizeCheckBox.updateSize()
                                Layout.maximumWidth: 120
                            }
                        }
                    }
                }

                Pane {
                    id: shadowsPane
                    ColumnLayout {
                        width: parent.width
                        CheckBox {
                            text: "Draw directional light shadow bounding boxes"
                            checked: root.source.environment.debugSettings.drawDirectionalLightShadowBoxes
                            onCheckedChanged: root.source.environment.debugSettings.drawDirectionalLightShadowBoxes = checked
                        }
                        CheckBox {
                            text: "Draw point light shadow bounding boxes"
                            checked: root.source.environment.debugSettings.drawPointLightShadowBoxes
                            onCheckedChanged: root.source.environment.debugSettings.drawPointLightShadowBoxes = checked
                        }
                        CheckBox {
                            text: "Draw shadow casting bounding box"
                            checked: root.source.environment.debugSettings.drawShadowCastingBounds
                            onCheckedChanged: root.source.environment.debugSettings.drawShadowCastingBounds = checked
                        }
                        CheckBox {
                            text: "Draw shadow receiving bounding box"
                            checked: root.source.environment.debugSettings.drawShadowReceivingBounds
                            onCheckedChanged: root.source.environment.debugSettings.drawShadowReceivingBounds = checked
                        }
                        CheckBox {
                            text: "Draw cascades"
                            checked: root.source.environment.debugSettings.drawCascades
                            onCheckedChanged: root.source.environment.debugSettings.drawCascades = checked
                        }
                        CheckBox {
                            text: "Draw scene cascade intersection"
                            checked: root.source.environment.debugSettings.drawSceneCascadeIntersection
                            onCheckedChanged: root.source.environment.debugSettings.drawSceneCascadeIntersection = checked
                        }
                        CheckBox {
                            text: "Disable Shadow Camera Update"
                            checked: root.source.environment.debugSettings.disableShadowCameraUpdate
                            onCheckedChanged: root.source.environment.debugSettings.disableShadowCameraUpdate = checked
                        }
                        CheckBox {
                            text: "Draw Culled Objects"
                            checked: root.source.environment.debugSettings.drawCulledObjects
                            onCheckedChanged: root.source.environment.debugSettings.drawCulledObjects = checked
                        }
                    }
                }
            }
        }
    }

    component CustomTableItemDelegate : Rectangle {
        property alias text: textLabel.text
        property alias textColor: textLabel.color
        implicitWidth: 100
        implicitHeight: textLabel.implicitHeight + 4
        color: palette.base
        Label {
            id: textLabel
            anchors.centerIn: parent
            color: palette.text
        }
    }

    function syncVisible() {
        if (source) {
            source.renderStats.extendedDataCollectionEnabled = visible && resourceDetailsVisible;
            if (source.renderStats.extendedDataCollectionEnabled)
                source.update();
        }
    }

    Component.onCompleted: syncVisible()
    onSourceChanged: syncVisible()
    onVisibleChanged: syncVisible()
    onResourceDetailsVisibleChanged: syncVisible()
}
