// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick3D
import QtQuick3D.Physics
import QtQuick3D.Helpers
import QtQuick.Controls
import QtQuick.Controls.Universal
import QtQuick.Layouts

ApplicationWindow {
    width: 1280
    height: 720
    visible: true
    title: qsTr("PhysX Physics Demo")

    Universal.theme: Universal.Dark
    Universal.accent: Universal.Orange

    PhysicsWorld {
        id: physicsWorld
        gravity: Qt.vector3d(0, -9.81, 0)
        running: false
        forceDebugDraw: true
        typicalLength: 0.2
        typicalSpeed: 10
        scene: scene
        viewport: viewport1.scene
    }

    Node {
        id: scene
        PerspectiveCamera {
            id: camera1
            position: Qt.vector3d(1, 3, 7)
            eulerRotation.x: -30
            eulerRotation.y: 10
            clipFar: 100
            clipNear: 0.01
        }

        DirectionalLight {
            eulerRotation.x: -45
            eulerRotation.y: 45
            castsShadow: true
            brightness: 1
            shadowMapQuality: Light.ShadowMapQualityMedium
            shadowBias: 0.05
            pcfFactor: 0.005
        }

        StaticRigidBody {
            id: floor
            position: Qt.vector3d(0, -1, 0)
            eulerRotation: Qt.vector3d(-90, 0, 0)
            collisionShapes: PlaneShape {
                enableDebugDraw: viewport.debugView
            }

            Model {
                source: "#Rectangle"
                scale: Qt.vector3d(0.3, 0.3, 1)
                materials: DefaultMaterial {
                    diffuseColor: "green"
                }
                castsShadows: false
                receivesShadows: true
            }
        }

        StaticRigidBody {
            id: wall
            position: Qt.vector3d(-4, 0, 0)
            eulerRotation: Qt.vector3d(0, 90, 0)

            collisionShapes: PlaneShape {
                id: wallShape
                enableDebugDraw: viewport.debugView
            }
            Model {
                source: "#Rectangle"
                scale: Qt.vector3d(0.3, 0.3, 1)
                materials: PrincipledMaterial {
                    baseColor: "gray"
                    cullMode: Material.NoCulling

                }
                opacity: 0.3
            }
        }

        DynamicRigidBody {
            id: dynamicTorus
            position: Qt.vector3d(0.5, 3.5, 1.0)
            property bool inArea: false

            mass: 10 // kg
            scale: Qt.vector3d(0.01, 0.01, 0.01).times(scaleSlider.value)
            Model {
                source: "meshes/newTorus.mesh"
                materials: PrincipledMaterial {
                    baseColor: "orange"
                }
            }
            collisionShapes: ConvexMeshShape {
                id: torusShape
                source: "meshes/newConvexTorus.mesh"
                enableDebugDraw: viewport.debugView
            }
        }

        DynamicRigidBody {
            id: kinematicTorus
            kinematicPosition: Qt.vector3d(-0.5, 2, 0.5)
            position: Qt.vector3d(-0.5, 2, 0.5)
            property bool inArea: false

            scale: Qt.vector3d(0.015, 0.015, 0.015).times(scaleSlider.value)
            mass: 10 // kg
            Model {
                source: "meshes/newTorus.mesh"
                materials: PrincipledMaterial {
                    baseColor: "lightGray"
                    metalness: 1.0
                    roughness: 0.5
                }
            }
            collisionShapes: TriangleMeshShape {
                id: torusShape2
                source: "meshes/newTorus.mesh"
                enableDebugDraw: viewport.debugView
            }
            isKinematic: true
        }

        DynamicRigidBody {
            id: dynamicTorusConvexHullOfNonConvexMesh
            position: Qt.vector3d(2, 3, 1.5)
            property bool inArea: false
            scale: Qt.vector3d(0.01, 0.01, 0.01)

            mass: 10 // kg
            Model {
                source: "meshes/newTorus.mesh"
                materials: PrincipledMaterial {
                    baseColor: "#c07030"
                    metalness: 1.0
                    roughness: 0.5
                }
            }
            collisionShapes: ConvexMeshShape {
                id: torusShape3
                source: "meshes/newTorus.mesh"
                enableDebugDraw: viewport.debugView
            }
        }

        // x-axis reference
        Model {
            materials: PrincipledMaterial {
                baseColor: "red"
                roughness: 1
            }
            source: "#Cube"
            scale: "0.002, 0.002, 0.002"
            position: "-0.5, 2, 4"
        }

        Model {
            materials: PrincipledMaterial {
                baseColor: "green"
                roughness: 1
            }
            source: "#Cube"
            scale: "0.002, 0.002, 0.002"
            position: "2, 2, 4"
        }

        Model {
            materials: PrincipledMaterial {
                baseColor: "blue"
                roughness: 1
            }
            source: "#Cube"
            scale: "0.002, 0.002, 0.002"
            position: "4.5, 2, 4"
        }

        // z-axis
        Model {
            materials: PrincipledMaterial {
                baseColor: "cyan"
                roughness: 1
            }
            source: "#Cube"
            scale: "0.002, 0.002, 0.002"
            position: "-0.5, 3, -1"
        }

        DynamicRigidBody {
            id: hfTest
            position: Qt.vector3d(0, 2, 1.5)
            kinematicPosition: Qt.vector3d(0, 2, 1.5)
            isKinematic: true

            Model {
                id: hfModel
                geometry: HeightFieldGeometry {
                    extents: Qt.vector3d(5, 2, 5).times(scaleSlider.value)
                    source: "maps/heightfield.png"
                    smoothShading: false
                }
                materials: PrincipledMaterial {
                    baseColor: "#ff77ff"
                    roughness: 0.3
                }
                opacity: 0.6
            }

            collisionShapes: HeightFieldShape {
                id: hfShape
                extents: Qt.vector3d(5, 2, 5).times(scaleSlider.value)
                source: "maps/heightfield.png"
                enableDebugDraw: viewport.debugView
            }
        }

        SequentialAnimation {
            running: animateCheckBox.checked && physicsWorld.running
            loops: -1
            NumberAnimation {
                target: hfTest
                property: "kinematicPosition.x"
                duration: 2000
                from: 0
                to: -2
                easing.type: Easing.InOutQuad
            }
            NumberAnimation {
                target: hfTest
                property: "kinematicPosition.x"
                duration: 2000
                from: -2
                to: 0
                easing.type: Easing.InOutQuad
            }
        }

        DynamicRigidBody {
            id: triangleMeshTest
            position: Qt.vector3d(0, 0.6, 0)
            eulerRotation: Qt.vector3d(0, 180, 0)
            kinematicPosition: Qt.vector3d(0, 0.6, 0)
            kinematicEulerRotation: Qt.vector3d(0, 180, 0)
            isKinematic: true
            property bool inArea: false

            scale: Qt.vector3d(0.015, 0.01, 0.015).times(scaleSlider.value)
            Model {
                id: testModel
                source: "meshes/field.mesh"
                materials: PrincipledMaterial {
                    baseColor: "#5070a0"
                    roughness: 0.3
                }
                opacity: 1
            }
            collisionShapes: TriangleMeshShape {
                id: triShape
                source: "meshes/field.mesh"
                enableDebugDraw: viewport.debugView
            }
        }

        SequentialAnimation {
            running: animateCheckBox.checked && physicsWorld.running
            loops: -1
            NumberAnimation {
                target: triangleMeshTest
                property: "kinematicEulerRotation.y"
                duration: 5000
                from: 180
                to: -180
                easing.type: Easing.InOutQuad
            }
            NumberAnimation {
                target: triangleMeshTest
                property: "kinematicEulerRotation.y"
                duration: 5000
                from: -180
                to: 180
                easing.type: Easing.InOutQuad
            }
        }


//            DynamicRigidBody {
//                id: table
//                y: -50

//                isKinematic: true

//                Model {
//                    id: tableModel
//                    source: "#Cube"
//                    materials: DefaultMaterial {
//                        diffuseColor: "gray"
//                    }
//                    scale: Qt.vector3d(4, 1, 4)
//                }
//                collisionShapes: BoxShape {
//                    id: tableShape
//                    extents: Qt.vector3d(200, 50, 200)
//                }
//            }

        Texture {
            id: qtLogoTexture
            source: "qt_logo_rect.png"
            scaleV: 0.5
            positionV: 0.2
        }
        Component {
            id: doughnutComponent

            Node {
                id: thisNode
                function randomInRange(min, max) {
                    return Math.random() * (max - min) + min;
                }
                eulerRotation: Qt.vector3d(randomInRange(0, 360),
                                      randomInRange(0, 360),
                                      randomInRange(0, 360))

                position: Qt.vector3d(randomInRange(-1.5, 1.5),
                                      randomInRange( 4, 30),
                                      randomInRange(-1.5, 1.5))

                property real sf: randomInRange(0.2, 0.45)
                property color baseCol: Qt.hsla(randomInRange(0, 1), randomInRange(0.3, 0.7), randomInRange(0.4, 0.7), 1.0)
                property bool grabbed: false
                property bool isSphere: randomInRange(0, 1) < 0.5
                property SphereShape sphereShape : SphereShape {
                    diameter: sf
                    enableDebugDraw: viewport.debugView
                }
                property ConvexMeshShape torusShape : ConvexMeshShape {
                    source: "meshes/newConvexTorus.mesh"
                    enableDebugDraw: viewport.debugView
                    scale: Qt.vector3d(sf, sf, sf).times(0.01)
                }
                DynamicRigidBody {
                    id: thisBody
                    mass: 1 //heavy donut
                    isKinematic: thisNode.grabbed
                    Model {
                        id: thisModel
                        source: isSphere ? "#Sphere" : "meshes/newTorus.mesh"
                        materials: PrincipledMaterial {
                            metalness: 1.0
                            roughness: randomInRange(0.3, 0.6)
                            baseColor: thisNode.grabbed ? "white" : baseCol
                            emissiveMap: qtLogoTexture
                            emissiveFactor: "0.2, 0.2, 0.2"
                            heightAmount: 0.5
                        }

                        scale: Qt.vector3d(sf, sf, sf).times(0.01)
                        pickable: true
                        property Node proxyNode: thisNode
                    }
                    collisionShapes: isSphere ? sphereShape : torusShape
                }
            }
        }

        Repeater3D {
            id: rep
            model: 400
            delegate: doughnutComponent
        }
    } // scene

    View3D {
        id: viewport
        width: radioSideBySide.checked ? parent.width/2 : parent.width
        height: parent.height
        visible: !radioDebug.checked
        importScene: scene

        environment: SceneEnvironment {
            clearColor: "#d6dbdf"
            backgroundMode: SceneEnvironment.Color
            antialiasingMode: SceneEnvironment.MSAA
            antialiasingQuality: SceneEnvironment.High
        }

        focus: true
        property bool debugView: false
    }

    View3D {
        id: viewport1
        width: radioSideBySide.checked ? parent.width/2 : parent.width
        height: parent.height
        x: radioSideBySide.checked ? parent.width/2 : 0
        visible: !radioScene.checked

        environment: SceneEnvironment {
            clearColor: "black"
            backgroundMode: SceneEnvironment.Color
            antialiasingMode: SceneEnvironment.MSAA
            antialiasingQuality: SceneEnvironment.High
        }

        PerspectiveCamera {
            id: camera2
            position: camera1.position
            eulerRotation: camera1.eulerRotation
            clipFar: camera1.clipFar
            clipNear: camera1.clipNear
        }

    }

    WasdController {
        keysEnabled: true
        controlledObject: camera1
        speed: 0.02
        shiftSpeed: 0.2

        Keys.onPressed: (event)=> {
            if (keysEnabled) handleKeyPress(event);
            if (event.key === Qt.Key_Space) {
                physicsWorld.running = !physicsWorld.running
            } else if (event.key === Qt.Key_G) {
                viewport.debugView = !viewport.debugView
            } else if (event.key === Qt.Key_H) {
                physicsWorld.forceDebugDraw = !physicsWorld.forceDebugDraw
            } else if (event.key === Qt.Key_T) {
                tableShape.extents = "150, 100, 150"
                tableModel.scale = "3, 2, 3"
            }
        }
        Keys.onReleased: (event)=> { if (keysEnabled) handleKeyRelease(event) }
    }

    MouseArea {
         anchors.fill: viewport
         acceptedButtons: Qt.RightButton
         property Node grabbedNode: null
         onPressed: (mouse) =>
         {
             var result = viewport.pick(mouse.x, mouse.y);
             if (result.objectHit) {
                 var pickedNode = result.objectHit.proxyNode;
                 console.log("PRESSED:", pickedNode)

                 pickedNode.grabbed = true
                 //pickedNode.y += 100
                 grabbedNode = pickedNode
             }
         }
         onReleased:
         {
             if (grabbedNode) {
                 grabbedNode.grabbed = false
                 grabbedNode = null
             }
         }
    }

    ColumnLayout {
        ColumnLayout {
            RadioButton {
                id: radioScene
                checked: true
                text: qsTr("Scene view")
            }
            RadioButton {
                id: radioSideBySide
                text: qsTr("Side by side")
            }
            RadioButton {
                id: radioDebug
                text: qsTr("Debug view")
            }
        }
        Label {
            text: "Scale: " + scaleSlider.value
        }
        Slider {
            id: scaleSlider
            focusPolicy: Qt.NoFocus
            from: 0.5
            to: 1.5
            value: 1
            stepSize: 0.1
        }
        CheckBox {
            id: animateCheckBox
            checked: false
            text: qsTr("Animate")
        }
    }
}
