using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace SC.XR.Unity.Module_InputSystem {

    public class SCInputModule : StandaloneInputModule {

        public static SCInputModule Instance { get; private set; }

        public static bool IsRayCastResultsSortByDistance = false;

        public static bool IsUseSCScrollRectMode = false;

        private Camera mRayCasterCamera;
        public Camera RayCasterCamera {
            get {
                if(mRayCasterCamera == null) {
                    mRayCasterCamera = EnsureUIEventCamera();
                }
                mRayCasterCamera.orthographic = true;
                return mRayCasterCamera;
            }
        }

        private PhysicsRaycaster mPhysicsRaycaster;
        public PhysicsRaycaster PhysicsRaycaster {
            get {
                if(mPhysicsRaycaster == null) {
                    mPhysicsRaycaster = EnsurePhysicsRaycaster();
                }
                return mPhysicsRaycaster;
            }
        }

        public LayerMask layerMask; 
        public float maxDetectDistance=0;

        Vector3? lastMousePoint3d;
        Vector2 newPos, lastPosition;
        Vector3 viewportPos = new Vector3(0.5f, 0.5f, 1f);
        Rect camerarect = new Rect(0, 0, 1, 1);
        static Rect constCamerarect = new Rect(0, 0, 1, 1);

        protected override void Start() {
            base.Start();
            if(Instance) {
                DestroyImmediate(this);
                return;
            }
            DontDestroyOnLoad(gameObject);
            Instance = this;
            camerarect = new Rect(0, 0, 1, 1);
        }

        public override void Process() {

        }

        public virtual void ProcessCS(SCPointEventData scPointEventData, Transform posture, int layerMask = 1 << 8, float maxDetectDistance = 30,bool isSimlate=false, RaycastResult simlateResult=new RaycastResult()) {
            //if(!eventSystem.isFocused)
            //    return;

            bool usedEvent = SendUpdateEventToSelectedObject();

            ProcessSCEventInit(scPointEventData, posture, layerMask, maxDetectDistance);
            ProcessSCEvent(scPointEventData, isSimlate, simlateResult);

            if(eventSystem.sendNavigationEvents) {
                /*
                if(!usedEvent)
                    usedEvent |= SendMoveEventToSelectedObject();

                if(!usedEvent)
                    SendSubmitEventToSelectedObject();*/
            }

        }

        void ProcessSCEventInit(SCPointEventData scPointEventData, Transform posture, int layerMask, float maxDetectDistance) {
            if(this.layerMask.value != 0) {
                layerMask = this.layerMask;
            }
            if(this.maxDetectDistance > 0) {
                maxDetectDistance = this.maxDetectDistance;
            }

            PhysicsRaycaster.eventMask = layerMask;

            RayCasterCamera.cullingMask = layerMask;
            RayCasterCamera.farClipPlane = maxDetectDistance;

            RayCasterCamera.transform.position = posture.position; // posture.position;// Vector3.Lerp(RayCasterCamera.transform.position, targetDetectBase.transform.position,0.3f); //targetDetectBase.transform.position;
            RayCasterCamera.transform.rotation = posture.rotation;
            ///Canvas分配wrold Camera
            foreach (var canvas in CanvasCollection.CanvasList) {
                canvas.worldCamera = RayCasterCamera;
            }

            mRayCasterCamera.orthographic = true;
            if (mRayCasterCamera.rect != constCamerarect) {
                mRayCasterCamera.rect = camerarect;
            }
            newPos = SCInputModule.Instance.RayCasterCamera.ViewportToScreenPoint(viewportPos);
            // Populate initial data or drag data
            if (lastMousePoint3d == null) {
                // For the first event, use the same position for 'last' and 'new'.
                lastPosition = newPos;
            } else {
                // Otherwise, re-project the last pointer position.
                lastPosition = SCInputModule.Instance.RayCasterCamera.WorldToScreenPoint(lastMousePoint3d.Value);
            }

            // Save off the 3D position of the cursor.
            lastMousePoint3d = SCInputModule.Instance.RayCasterCamera.ViewportToWorldPoint(viewportPos);

            scPointEventData.delta = newPos - lastPosition;
            scPointEventData.position = newPos;

            //if(RayCasterCamera != null && Application.platform != RuntimePlatform.Android) {
            //    Ray ray = new Ray();
            //    ray = RayCasterCamera.ScreenPointToRay(scPointEventData.position);
            //    Debug.DrawRay(ray.origin, ray.direction * maxDetectDistance, Color.blue);
            //}
        }

        protected static RaycastResult FindFirstRaycastByDistance(List<RaycastResult> candidates) {
            candidates.Sort(s_RaycastComparer);
            return FindFirstRaycast(candidates);
        }

        private static readonly Comparison<RaycastResult> s_RaycastComparer = RaycastComparer;
        private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs) {
            return lhs.distance.CompareTo(rhs.distance);
        }



        /// <summary>
        /// Process all mouse events.
        /// </summary>
        protected void ProcessSCEvent(SCPointEventData scPointEventData, bool isSimlate = false, RaycastResult simlateResult = new RaycastResult()) {

            scPointEventData.MouseButtonEventData.buttonData = scPointEventData;
            scPointEventData.MouseButtonEventData.buttonState = StateForMouseButton(scPointEventData.inputDevicePartBase);

            if(isSimlate) {
                scPointEventData.pointerCurrentRaycast = simlateResult;
            } else {

                try {
                    mRayCasterCamera.orthographic = true;
                    eventSystem.RaycastAll(scPointEventData, m_RaycastResultCache);

                } catch (Exception e) {
                    Debug.Log(e);

                    Debug.Log("============>Start:" + Time.frameCount);
                    if (mRayCasterCamera) {
                        //Debug.Log("============>Start:"+Time.frameCount);

                        Debug.Log("parent:" + mRayCasterCamera.transform.name);
                        Debug.Log("nearClipPlane:" + mRayCasterCamera.nearClipPlane);
                        Debug.Log("farClipPlane:" + mRayCasterCamera.farClipPlane);
                        Debug.Log("orthographic:" + mRayCasterCamera.orthographic);
                        Debug.Log("orthographicSize:" + mRayCasterCamera.orthographicSize);
                        Debug.Log("aspect:" + mRayCasterCamera.aspect);
                        Debug.Log("rect:" + mRayCasterCamera.rect);

                        Debug.Log("============>End:" + Time.frameCount);
                    }

                }
                if (!IsRayCastResultsSortByDistance) {
                    scPointEventData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
                } else {
                    scPointEventData.pointerCurrentRaycast = FindFirstRaycastByDistance(m_RaycastResultCache);
                }
                m_RaycastResultCache.Clear();
            }

            


            scPointEventData.Forward = RayCasterCamera.transform.forward;

            if(scPointEventData.MouseButtonEventData.PressedThisFrame() && scPointEventData.pointerCurrentRaycast.gameObject) {

                scPointEventData.HitPointerRelativeRayCasterCamera = RayCasterCamera.transform.InverseTransformPoint(scPointEventData.pointerCurrentRaycast.worldPosition);
                scPointEventData.Position3D = scPointEventData.PressPosition3D = RayCasterCamera.transform.TransformPoint(scPointEventData.HitPointerRelativeRayCasterCamera);
                scPointEventData.pressForward = RayCasterCamera.transform.forward;
                scPointEventData.DownPressGameObject = scPointEventData.pointerCurrentRaycast.gameObject;

            } else if(scPointEventData.MouseButtonEventData.ReleasedThisFrame()) {

                scPointEventData.HitPointerDeltaDragObjCenter = scPointEventData.HitPointerRelativeRayCasterCamera = scPointEventData.dragObjPosition = Vector3.zero;
                scPointEventData.Position3D = Vector3.zero;
                scPointEventData.PressPosition3D = Vector3.zero;
                scPointEventData.DownPressGameObject = null;
                scPointEventData.pressForward = Vector3.zero;
                scPointEventData.downtimer = 0;
            }


            if(scPointEventData.DownPressGameObject) {
                scPointEventData.downtimer += Time.deltaTime;
                scPointEventData.Position3D = RayCasterCamera.transform.TransformPoint(scPointEventData.HitPointerRelativeRayCasterCamera);
            }

            // Process the first mouse button fully
            ProcessMousePress(scPointEventData.MouseButtonEventData);
            ProcessMove(scPointEventData);


            if (scPointEventData.MouseButtonEventData.PressedThisFrame() && scPointEventData.pointerCurrentRaycast.gameObject) {
                if (scPointEventData.pointerDrag) {
                    ///碰撞点指向碰撞物体中心的向量
                    scPointEventData.HitPointerDeltaDragObjCenter = scPointEventData.pointerDrag.transform.position - scPointEventData.pointerCurrentRaycast.worldPosition;
                } else {
                    ///碰撞点指向碰撞物体中心的向量
                    scPointEventData.HitPointerDeltaDragObjCenter = scPointEventData.pointerCurrentRaycast.gameObject.transform.position - scPointEventData.pointerCurrentRaycast.worldPosition;
                }
                ///第一次赋值 ,must
                scPointEventData.dragObjPosition = scPointEventData.HitPointerDeltaDragObjCenter + scPointEventData.Position3D;
            }

            ///必须在ProcessDrag前,否则,延迟一帧导致停顿感
            if(scPointEventData.dragging) {
                scPointEventData.dragObjPosition = scPointEventData.HitPointerDeltaDragObjCenter + scPointEventData.Position3D;
            }


            ///拖拽不启动阈值,否则不能触发IDragHandler
            //scPointEventData.useDragThreshold = false;

            ProcessDrag(scPointEventData);

            ///更新dragHitPinterPosition,必须在ProcessDrag后,ProcessDrag调用的DragHandler可能会更改PointerDrag对象的Transform
            if(scPointEventData.dragging) {
                scPointEventData.dragAnchorPosition3D = scPointEventData.pointerDrag.transform.position - scPointEventData.HitPointerDeltaDragObjCenter;
            } else {
                scPointEventData.dragAnchorPosition3D = Vector3.zero;
            }

            if (!Mathf.Approximately(scPointEventData.scrollDelta.sqrMagnitude, 0.0f)) {
                if (IsUseSCScrollRectMode) {
                    m_scrollrectRight = FindRightScrollRect(scPointEventData.pointerCurrentRaycast.gameObject, scPointEventData);
                    if (m_scrollrectRight != null) {
                        ExecuteEvents.ExecuteHierarchy(m_scrollrectRight.gameObject, scPointEventData, ExecuteEvents.scrollHandler);
                    } else {
                        /// for implementation IScrollHandler But not ScrollRect
                        var scrollHandlerG = ExecuteEvents.GetEventHandler<IScrollHandler>(scPointEventData.pointerCurrentRaycast.gameObject);
                        ExecuteEvents.ExecuteHierarchy(scrollHandlerG, scPointEventData, ExecuteEvents.scrollHandler);
                    }
                } else {
                    var scrollHandlerG = ExecuteEvents.GetEventHandler<IScrollHandler>(scPointEventData.pointerCurrentRaycast.gameObject);
                    ExecuteEvents.ExecuteHierarchy(scrollHandlerG, scPointEventData, ExecuteEvents.scrollHandler);
                }

            }

            scPointEventDataxx = scPointEventData;
        }

        ScrollRect scrollrectRight; 
        ScrollRect m_scrollrectRight;
        ScrollRect FindRightScrollRect(GameObject gameobject,PointerEventData eventdata) {
            if (gameobject == null)
                return null;

            scrollrectRight = gameobject.GetComponentInParent<ScrollRect>();
            if (scrollrectRight!= null && scrollrectRight.gameObject.activeSelf &&
                ((scrollrectRight.horizontal == false && eventdata.scrollDelta.x != 0) ||
                    (scrollrectRight.vertical == false && eventdata.scrollDelta.y != 0)
                    )
                    ) {
                if (scrollrectRight.transform.parent == null)
                    return null;

                scrollrectRight = FindRightScrollRect(scrollrectRight.transform.parent.gameObject, eventdata);
            }
            return scrollrectRight;
        }



        private static bool ShouldStartDrag(Vector3 pressPos, Vector3 currentPos, float threshold, bool useDragThreshold, PointerEventData pointerEventData) {

            if (useDragThreshold) {

                if (!useDragThreshold)
                    return true;

                return (pressPos - currentPos).magnitude >= threshold * 0.0005f;

            }
            return true;
        }

        protected override void ProcessDrag(PointerEventData pointerEvent) {

            if(!pointerEvent.IsPointerMoving() ||
                Cursor.lockState == CursorLockMode.Locked ||
                pointerEvent.pointerDrag == null)
                return;

            if(!pointerEvent.dragging
                && ShouldStartDrag((pointerEvent as SCPointEventData).PressPosition3D, (pointerEvent as SCPointEventData).Position3D, eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold, pointerEvent)) {
                ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);

                pointerEvent.dragging = true;
            }

            // Drag notification
            if(pointerEvent.dragging) {

                // Before doing drag we should cancel any pointer down state
                // And clear selection!

                //&& ((pointerEvent as SCPointEventData).downtimer >= (pointerEvent as SCPointEventData).TriggerDeltaTime) ? true : false 解决3D环境下误触发Drag导致后,松开不能正常触发click的问题
                if (pointerEvent.pointerPress != pointerEvent.pointerDrag && ((pointerEvent as SCPointEventData).downtimer >= (pointerEvent as SCPointEventData).TriggerDeltaTime) ? true : false) {
                    ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);

                    pointerEvent.eligibleForClick = false;
                    pointerEvent.pointerPress = null;
                    pointerEvent.rawPointerPress = null;
                }
                ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler);
            }

        }


        protected PointerEventData.FramePressState StateForMouseButton(InputDevicePartBase devicePart) {
            var pressed = devicePart.inputDataBase.inputKeys.GetKeyDown(InputKeyCode.Enter);
            var released = devicePart.inputDataBase.inputKeys.GetKeyUp(InputKeyCode.Enter);

            if(pressed && released)
                return PointerEventData.FramePressState.PressedAndReleased;
            if(pressed)
                return PointerEventData.FramePressState.Pressed;
            if(released)
                return PointerEventData.FramePressState.Released;
            return PointerEventData.FramePressState.NotChanged;
        }

        protected PhysicsRaycaster EnsurePhysicsRaycaster() {

            mPhysicsRaycaster = GetComponent<PhysicsRaycaster>();
            if(mPhysicsRaycaster == null) {
                mPhysicsRaycaster = gameObject.AddComponent<PhysicsRaycaster>();
            }
            return mPhysicsRaycaster;
        }

        protected Camera EnsureUIEventCamera(float maxDetectDistance = 30) {
            mRayCasterCamera = GetComponent<Camera>();
            if(!mRayCasterCamera) {
                mRayCasterCamera = gameObject.AddComponent<Camera>();
            }

            mRayCasterCamera.enabled = false;
            mRayCasterCamera.nearClipPlane = 0;
            mRayCasterCamera.farClipPlane = maxDetectDistance;
            mRayCasterCamera.clearFlags = CameraClearFlags.Color;
            mRayCasterCamera.backgroundColor = Color.black;
            mRayCasterCamera.orthographic = true;
            mRayCasterCamera.allowHDR = false;
            mRayCasterCamera.allowMSAA = false;
            mRayCasterCamera.orthographicSize = 0.1f;
            mRayCasterCamera.aspect = 1f;
            return mRayCasterCamera;
        }



        SCPointEventData scPointEventDataxx;
        //void OnDrawGizmos() {
        //    if(Application.isPlaying) {

        //        // Now show the input position.
        //        //Gizmos.color = Color.red;
        //        //foreach(var item in cornerLocalPostion) {
        //        //    Gizmos.DrawSphere(item, 0.01f);
        //        //}
        //        //Gizmos.color = Color.yellow;
        //        //foreach(var item in midPointLocalPostion) {
        //        //    Gizmos.DrawSphere(item, 0.01f);
        //        //}
        //        if(scPointEventDataxx != null && scPointEventDataxx.DownPressGameObject) {
        //            Gizmos.color = Color.black;
        //            Gizmos.DrawSphere(scPointEventDataxx.PressPosition3D, 0.01f);
        //            //Gizmos.color = Color.red * 0.5f;
        //            Gizmos.DrawSphere(scPointEventDataxx.Position3D, 0.01f);
        //            //Gizmos.color = Color.yellow * 0.5f;
        //            if(scPointEventDataxx.pointerDrag != null) {
        //                Gizmos.DrawSphere(scPointEventDataxx.dragObjPosition, 0.04f);
        //                //Gizmos.color = Color.blue * 0.8f;
        //                Gizmos.DrawSphere(scPointEventDataxx.dragAnchorPosition3D, 0.02f);
        //            }

        //        }
        //        //if(scPointEventDataxx.pointerDrag)
        //        //Gizmos.DrawSphere(scPointEventDataxx.pointerDrag.transform.position, 0.01f);
        //    }
        //}


    }
}