// 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.Physics.Helpers as Helpers
import QtQuick.Controls
import QtQuick.Layouts


Window {
    width: 1280
    height: 720
    visible: true
    title: qsTr("QtQuick3DPhysics character controller test")


    PhysicsWorld {
        id: physicsWorld
        running: true
        forceDebugDraw: false
        //        enableCCD: true
        scene: viewport.scene
    }

    View3D {
        anchors.fill: parent
        id: viewport



    environment: SceneEnvironment {
            clearColor: "#d6dbdf"
            backgroundMode: SceneEnvironment.SkyBox
            antialiasingMode: SceneEnvironment.MSAA
            antialiasingQuality: SceneEnvironment.High
            lightProbe: Texture {
                source: "maps/OpenfootageNET_lowerAustria01-512.hdr"
            }
            skyboxBlurAmount: 0.22
            probeExposure: 5
        }

        StaticRigidBody {
            eulerRotation: Qt.vector3d(-90, 0, 0)
            collisionShapes: PlaneShape {}
        }

        PrincipledMaterial {
            id: stairMaterial
            metalness: 0
            roughness: 0.5
            baseColor: "orange"
            alphaMode: PrincipledMaterial.Opaque
        }
        PrincipledMaterial {
            id: wallMaterial
            metalness: 0
            roughness: 0.5
            baseColor: "gray"
            alphaMode: PrincipledMaterial.Opaque
        }

        Node {
            id: scene

            DirectionalLight {
                eulerRotation.x: -25
                eulerRotation.y: 45
                castsShadow: true
                brightness: 1
                shadowMapQuality: Light.ShadowMapQualityVeryHigh
                shadowFactor: 50
            }

            StaticRigidBody {
                collisionShapes: TriangleMeshShape {
                    id: roomShape
                    source: "meshes/room.mesh"
                }

                Model {
                    id: room
                    source: "meshes/room.mesh"
                    materials: [
                    wallMaterial
                    ]

                }
            }
            StaticRigidBody {
                    x: -600
                    y: 0
                    z: 600
                collisionShapes: TriangleMeshShape {
                    id: stairShape
                    source: "meshes/stairs.mesh"
                }

                Model {
                    id: stairs
                    source: "meshes/stairs.mesh"
                    materials: [
                    stairMaterial
                    ]
                }
            }

            Model {
                id: scenery
                source: "#Cube"
                materials: stairMaterial
                z: -5000
                scale: "10, 0.1, 10"
            }

            Model {
                id: scenery2
                source: "#Cube"
                materials: wallMaterial
                z: -7000
                scale: "10, 0.1, 10"
            }

            Model {
                id: scenery3
                source: "#Cube"
                materials: stairMaterial
                z: -15000
                scale: "10, 0.1, 100"
            }

            Repeater3D {
                model: 10
                StaticRigidBody {
                    id: platform
                    x: 300
                    z: -1300 - 600 * index
                    scale: "2, 0.3, 2"
                    y: 500 + 50 * index
                    collisionShapes: BoxShape {}
                    Model {
                        source: "#Cube"
                        materials: stairMaterial
                    }

                }
            }

            CharacterController {
                id: character
                position: "100, 100, 300"
                eulerRotation.y: wasd.cameraRotation.x

                gravity: flying ? Qt.vector3d(0,0,0) : physicsWorld.gravity
                midAirControl: midAirButton.checked
                property bool physicallyCorrect: correctnessButton.checked
                property bool flying: flyingButton.checked

                property bool grounded: (collisions & CharacterController.Down)
                property bool jumpingAllowed : false

                Timer {
                    id: groundednessTimer
                    // Wile E. Coyote mode: how long after you started to fall
                    // can you still jump? (Only in non-physically-correct mode.)
                    interval: 150
                }
                onGroundedChanged: {
                    groundednessTimer.stop()
                    if (grounded) {
                        // Landing is always immediate, but we don't jump in flying mode
                        jumpingAllowed = !flying
                    } else if (!midAirControl || wasd.jump) {
                        // Jumping is always immediate (no double jumping)
                        jumpingAllowed = false
                    } else {
                        // Walking off a ledge in mid-air mode has a grace period
                        groundednessTimer.restart()
                    }
                }

                property string collisionText: ""
                onCollisionsChanged: {
                    if (collisions === CharacterController.None) {
                        collisionText = " No collisions"
                    } else {
                        collisionText = (collisions & CharacterController.Up ? " Above" : "")
                                     + (collisions & CharacterController.Side ? " Side" : "")
                                     + ((collisions & CharacterController.Down) ? " Below" : "")
                    }
                    console.log("Collision state:" + collisionText + " (" + collisions + ")")
                }

                movement.x: wasd.xFactor * 500;
                movement.z: wasd.zFactor * 500;
                movement.y: ((wasd.jump && jumpingAllowed) ? 500 : 0)
                          + wasd.yFactor * 500
                Behavior on movement.z {
                    PropertyAnimation { duration: 200 }
                }
                Behavior on movement.x {
                    PropertyAnimation { duration: 200 }
                }

                collisionShapes:  CapsuleShape {
                    id: capsuleShape
                }

                Model {
                    // not visible from the camera, but casts a shadow
                    eulerRotation.z: 90
                    geometry: Helpers.CapsuleGeometry {}
                    materials: PrincipledMaterial {
                        baseColor: "red"
                    }
                }

                PerspectiveCamera {
                    id: camera2
                    position: Qt.vector3d(0, capsuleShape.height, 0)
                    eulerRotation.x: wasd.cameraRotation.y
                    clipFar: 10000
                    clipNear: 10
                }
            }
        }
        Wasd {
            id: wasd
            property real sprintFactor: sprintActive ? 3 : 1
            property real xFactor: (moveLeft ? -1 : moveRight ? 1 : 0) * sprintFactor
            property real zFactor: (moveForwards ? -1 : moveBackwards ? 1 : 0) * sprintFactor
            property real yFactor: (character.flying ? (moveUp ? 1 : moveDown ? -1 : 0) : 0) * sprintFactor

            property bool jump: false

            Timer {
                id: preJumpTimer
                interval: 200 // how long before you land can you press jump?
                onTriggered: wasd.jump = false
            }

            function startJump()
            {
                wasd.jump = true
                preJumpTimer.restart()
            }

            Keys.onPressed: (event)=> {
                handleKeyPress(event);
                if (event.key === Qt.Key_Space && !event.isAutoRepeat) {
                    wasd.startJump()
                } else if (event.key === Qt.Key_G) {
                }
            }

            Keys.onReleased: (event) => {
                handleKeyRelease(event)
                if (event.key === Qt.Key_Space) {
                    wasd.jump = false
                }
            }
        }

    } // View3D

    ButtonGroup {
        buttons: column.children
    }

    Column {
        id: column
        RadioButton {
            id: midAirButton
            checked: true
            text: qsTr("Mid-air controls")
            focusPolicy: Qt.NoFocus
        }
        RadioButton {
            id: correctnessButton
            text: qsTr("Physically accurate free fall")
            focusPolicy: Qt.NoFocus
        }
        RadioButton {
            id: flyingButton
            text: qsTr("Flying (no gravity)")
            focusPolicy: Qt.NoFocus
        }

        Button {
            text: "Teleport"
            onClicked: character.teleport(Qt.vector3d(-700, 3000, -6850))
            focusPolicy: Qt.NoFocus
        }
        Text {
            text: "Current position: " + character.position
        }
        Text {
            text: "Collision state:" + character.collisionText
        }
    }
}
