#if CREST_UNITY_INPUT && ENABLE_INPUT_SYSTEM
#define INPUT_SYSTEM_ENABLED
#endif

using System;
using UnityEngine;
#if INPUT_SYSTEM_ENABLED
using UnityEngine.InputSystem;
#endif
#if ENABLE_VR && ENABLE_VR_MODULE
using UnityEngine.XR;
#endif

namespace Deconstruction.Runtime.Player
{
    /// <summary>
    /// 一个简单且功能有限的摄像机脚本，可通过 WASD 键和鼠标进行控制。
    /// </summary>
    public class CameraController : MonoBehaviour
    {
        /// <summary>
        /// 此资产的版本。可用于不同版本之间的迁移。只有在编辑器升级版本时才应更改此值。
        /// </summary>
        [SerializeField, HideInInspector]
#pragma warning disable 414
        int _version = 0;
#pragma warning restore 414

        public float linSpeed = 10f;
        public float rotSpeed = 70f;

        public bool simForwardInput = false;
        public bool _requireLMBToMove = false;

        public bool enableWalkMode = false;
        
        Vector2 _lastMousePos = -Vector2.one;
        bool _dragging = false;

        public float _fixedDt = 1 / 60f;

        Transform _targetTransform;
        
        public CharacterController _moveComponent;
        
        [Space(10)]

        [SerializeField]
        DebugFields _debug = new DebugFields();

#pragma warning disable CS0108
        // 在编辑器中，我们需要使用“new”来屏蔽警告，但在构建时会再次给出警告，所以应该使用“pragma”来替代。
        private Camera camera;
#pragma warning restore CS0108

        [System.Serializable]
        class DebugFields
        {
            [Tooltip("Allows the camera to roll (rotating on the z axis).")]
            public bool _enableCameraRoll = false;

            [Tooltip("Disables the XR occlusion mesh for debugging purposes. Only works with legacy XR.")]
            public bool _disableOcclusionMesh = false;

            [Tooltip("Sets the XR occlusion mesh scale. Useful for debugging refractions. Only works with legacy XR."), UnityEngine.Range(1f, 2f)]
            public float _occlusionMeshScale = 1f;
        }

        private void OnValidate()
        {
            if (_moveComponent == null)
            {
                _moveComponent = GetComponent<CharacterController>();
            }
            else if (Application.isEditor)
            {
                _moveComponent.enabled = enableWalkMode;
            }
        }

        void Awake()
        {
            _targetTransform = transform;

            if (!TryGetComponent(out camera))
            {
                enabled = false;
                return;
            }

            if (_moveComponent != null)
            {
                _moveComponent.enabled = enableWalkMode;
            }
#if ENABLE_VR && ENABLE_VR_MODULE
            if (XRSettings.enabled)
            {
                // Seems like the best place to put this for now. Most XR debugging happens using this component.
                // @FixMe: useOcclusionMesh doesn't work anymore. Might be a Unity bug.
                XRSettings.useOcclusionMesh = !_debug._disableOcclusionMesh;
                XRSettings.occlusionMaskScale = _debug._occlusionMeshScale;
            }
#endif
        }

        void Update()
        {
            float dt = Time.deltaTime;
            if (_fixedDt > 0f)
                dt = _fixedDt;

            UpdateMovement(dt);
            
#if ENABLE_VR && ENABLE_VR_MODULE
            // These aren't useful and can break for XR hardware.
            if (!XRSettings.enabled || XRSettings.loadedDeviceName.Contains("MockHMD"))
#endif
            {
                UpdateDragging(dt);
                UpdateKillRoll();
            }

#if ENABLE_VR && ENABLE_VR_MODULE
            if (XRSettings.enabled)
            {
                // Check if property has changed.
                if (XRSettings.useOcclusionMesh == _debug._disableOcclusionMesh)
                {
                    // @FixMe: useOcclusionMesh doesn't work anymore. Might be a Unity bug.
                    XRSettings.useOcclusionMesh = !_debug._disableOcclusionMesh;
                }

                XRSettings.occlusionMaskScale = _debug._occlusionMeshScale;
            }
#endif
        }

        void UpdateMovement(float dt)
        {
            // New input system works even when game view is not focused.
            if (!Application.isFocused)
            {
                return;
            }

#if INPUT_SYSTEM_ENABLED
            if (!Mouse.current.leftButton.isPressed && _requireLMBToMove) return;
            float forward = (Keyboard.current.wKey.isPressed ? 1 : 0) - (Keyboard.current.sKey.isPressed ? 1 : 0);
#else
            if (!Input.GetMouseButton(1) && _requireLMBToMove) return;
            float forward = (Input.GetKey(KeyCode.W) ? 1 : 0) - (Input.GetKey(KeyCode.S) ? 1 : 0);
#endif
            if (simForwardInput)
            {
                forward = 1f;
            }

            _targetTransform.position += linSpeed * _targetTransform.forward * forward * dt;
            var speed = linSpeed;

#if INPUT_SYSTEM_ENABLED
            if (Keyboard.current.leftShiftKey.isPressed)
#else
            if (Input.GetKey(KeyCode.LeftShift))
#endif
            {
                speed *= 3f;
            }

            var offset = _targetTransform.forward * forward;
            // _targetTransform.position += speed * _targetTransform.forward * forward * dt;
            //_transform.position += linSpeed * _transform.right * Input.GetAxis( "Horizontal" ) * dt;
#if INPUT_SYSTEM_ENABLED
            offset += _targetTransform.up * (Keyboard.current.eKey.isPressed ? 1 : 0);
            offset -= _targetTransform.up * (Keyboard.current.qKey.isPressed ? 1 : 0);
            offset -= _targetTransform.right * (Keyboard.current.aKey.isPressed ? 1 : 0);
            offset += _targetTransform.right * (Keyboard.current.dKey.isPressed ? 1 : 0);
#else
            offset += _targetTransform.up * (Input.GetKey(KeyCode.E) ? 1 : 0);
            offset -= _targetTransform.up * (Input.GetKey(KeyCode.Q) ? 1 : 0);
            offset -= _targetTransform.right * (Input.GetKey(KeyCode.A) ? 1 : 0);
            offset += _targetTransform.right * (Input.GetKey(KeyCode.D) ? 1 : 0);
#endif
            offset *= speed * dt;
            
            // 使用控制器移动
            if (enableWalkMode)
            {
                UpdateWalk(offset);
            }
            // 直接设置坐标移动
            else
            {
                _targetTransform.position += offset;
            }
            {
                float rotate = 0f;
#if INPUT_SYSTEM_ENABLED
                rotate += (Keyboard.current.rightArrowKey.isPressed ? 1 : 0);
                rotate -= (Keyboard.current.leftArrowKey.isPressed ? 1 : 0);
#else
                rotate += (Input.GetKey(KeyCode.RightArrow) ? 1 : 0);
                rotate -= (Input.GetKey(KeyCode.LeftArrow) ? 1 : 0);
#endif

                rotate *= 5f;
                Vector3 ea = _targetTransform.eulerAngles;
                ea.y += 0.1f * rotSpeed * rotate * dt;
                _targetTransform.eulerAngles = ea;
            }
        }

        void UpdateDragging(float dt)
        {
            // New input system works even when game view is not focused.
            if (!Application.isFocused)
            {
                return;
            }

            Vector2 mousePos =
#if INPUT_SYSTEM_ENABLED
                Mouse.current.position.ReadValue();
#else
                Input.mousePosition;
#endif

            var wasLeftMouseButtonPressed =
#if INPUT_SYSTEM_ENABLED
                Mouse.current.leftButton.wasPressedThisFrame;
#else
                Input.GetMouseButtonDown(1);
#endif

            if (!_dragging && wasLeftMouseButtonPressed && camera.rect.Contains(camera.ScreenToViewportPoint(mousePos)) )
                //!Crest.OceanDebugGUI.OverGUI(mousePos))
            {
                _dragging = true;
                _lastMousePos = mousePos;
            }
#if INPUT_SYSTEM_ENABLED
            if (_dragging && Mouse.current.leftButton.wasReleasedThisFrame)
#else
            if (_dragging && Input.GetMouseButtonUp(1))
#endif
            {
                _dragging = false;
                _lastMousePos = -Vector2.one;
            }

            if (_dragging)
            {
                Vector2 delta = mousePos - _lastMousePos;

                Vector3 ea = _targetTransform.eulerAngles;
                ea.x += -0.1f * rotSpeed * delta.y * dt;
                ea.y += 0.1f * rotSpeed * delta.x * dt;
                _targetTransform.eulerAngles = ea;

                _lastMousePos = mousePos;
            }
        }

        void UpdateKillRoll()
        {
            if (_debug._enableCameraRoll) return;
            Vector3 ea = _targetTransform.eulerAngles;
            ea.z = 0f;
            transform.eulerAngles = ea;
        }

        private float gravity = 0;
        void UpdateWalk(Vector3 offset)
        {
            if (_moveComponent != null)
            {
                _moveComponent.enabled = enableWalkMode;
                if (enableWalkMode)
                {
                    if (offset.y > 0)
                        gravity = 0;
                    else
                    {
                        gravity -= 0.01f;
                        gravity = Mathf.Max(gravity, -0.98f);
                    }
                    offset.y = offset.y <= 0 ? gravity : offset.y;
                    _moveComponent.Move(offset);
                }
            }
        }
    }
}
