﻿//----------------------------------------------------------------------------------
// GVRNavi | Garpix Virtual Reality Navigation
// Copyright (c) 2017 Garpix Ltd.
//
// Plugin Homepage: https://gvrnavi.garpix.com
// Author Homepage: https://garpix.com
// Support: support@garpix.com
// License: Asset Store Terms of Service and EULA
// License URI: See LICENSE file in the project root for full license information.
//----------------------------------------------------------------------------------

using UnityEngine;
using System;
using System.Collections.Generic;

namespace GVRNavi
{
    public class VRReticlePointerImpl : GvrBasePointer
    {
        #region variables

        public Action<VRObjectGaze> startCoroutine;
        public Action stopCoroutine;

        public const float RETICLE_DISTANCE_MAX = 10.0f;

        public VRReticlePointerType type = VRReticlePointerType.Press;

        private List<GameObject> _listGameObjects;
        private List<VRObjectGaze> _listGazeObjects;


        #endregion


        public override void OnStart()
        {
            base.OnStart();
            _listGameObjects = new List<GameObject>();
            _listGazeObjects = new List<VRObjectGaze>();
        }

        /// This is called when the 'BaseInputModule' system should be enabled.
        public override void OnInputModuleEnabled() { }

        /// This is called when the 'BaseInputModule' system should be disabled.
        public override void OnInputModuleDisabled() { }

        public override float MaxPointerDistance { get { return RETICLE_DISTANCE_MAX; } }

        /// Called when the user is pointing at valid GameObject. This can be a 3D
        /// or UI element.
        ///
        /// The targetObject is the object the user is pointing at.
        /// The intersectionPosition is where the ray intersected with the targetObject.
        /// The intersectionRay is the ray that was cast to determine the intersection.
        public override void OnPointerEnter(GameObject targetObject, Vector3 intersectionPosition, Ray intersectionRay, bool isInteractive)
        {
            GameObject currentGameObject = _listGameObjects.Count > 0 ? _listGameObjects[0] : null;
            VRObjectGaze currentGaze = _listGazeObjects.Count > 0 ? _listGazeObjects[0] : null;
            if (currentGameObject == targetObject || targetObject == null)
                return;

            VRObjectGaze targetGaze = targetObject.GetComponent<VRObjectGaze>();
            if (targetGaze == null)
                return;

            if (currentGameObject == null)
            {
                _listGameObjects.Insert(0, targetObject);
                _listGazeObjects.Insert(0, targetGaze);
                targetGaze.OnGazeEnter();
                if (startCoroutine != null)
                    startCoroutine(targetGaze);
                return;
            }

            if (!(currentGaze is IVRContainer) || !(currentGaze as IVRContainer).IsChild(targetGaze))
            {
                foreach (var gaze in _listGazeObjects)
                    gaze.OnGazeExit();
                _listGameObjects.Clear();
                _listGazeObjects.Clear();
            }

            _listGameObjects.Insert(0, targetObject);
            _listGazeObjects.Insert(0, targetGaze);
            targetGaze.OnGazeEnter();
            if (startCoroutine != null)
                startCoroutine(targetGaze);
        }

        /// Called every frame the user is still pointing at a valid GameObject. This
        /// can be a 3D or UI element.
        ///
        /// The targetObject is the object the user is pointing at.
        /// The intersectionPosition is where the ray intersected with the targetObject.
        /// The intersectionRay is the ray that was cast to determine the intersection.
        public override void OnPointerHover(GameObject targetObject, Vector3 intersectionPosition, Ray intersectionRay, bool isInteractive) { }

        /// Called when the user's look no longer intersects an object previously
        /// intersected with a ray projected from the camera.
        /// This is also called just before **OnInputModuleDisabled** and may have have any of
        /// the values set as **null**.
        public override void OnPointerExit(GameObject targetObject)
        {
            VRObjectGaze targetGaze = targetObject.GetComponent<VRObjectGaze>();
            if (targetGaze == null)
                return;

            if (_listGazeObjects.Count > 0)
            {
                Camera c = VR3DRoot.Instance != null ?
                               VR3DRoot.Instance.mainCamera != null ?
                                   VR3DRoot.Instance.mainCamera :
                                   Camera.current :
                               Camera.current;
                Ray ray = new Ray(c.transform.position, c.transform.forward);
                RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Collide);

                while (_listGazeObjects.Count > 0)
                {
                    bool b = false;
                    if (_listGazeObjects[0] is IVRContainer)
                    {
                        foreach (var hit in hits)
                            if (hit.transform == _listGazeObjects[0].transform)
                            {
                                b = true;
                                break;
                            }
                    }
                    if (b)
                        break;
                    else
                    {
                        if (_listGazeObjects[0] == targetGaze && type == VRReticlePointerType.Gaze)
                            StopTimeCoroutine();
                        _listGazeObjects[0].OnGazeExit();
                        _listGameObjects.Remove(_listGazeObjects[0].gameObject);
                        _listGazeObjects.RemoveAt(0);
                    }
                }
            }

            if (_listGameObjects.Contains(targetObject) && !_listGazeObjects.Contains(targetGaze))
            {
                if (type == VRReticlePointerType.Gaze)
                    StopTimeCoroutine();
                targetGaze.OnGazeExit();
                _listGameObjects.Remove(targetObject);
                _listGazeObjects.Remove(targetGaze);
            }
        }

        /// Called when a trigger event is initiated. This is practically when
        /// the user begins pressing the trigger.
        public override void OnPointerClickDown() { }

        /// Called when a trigger event is finished. This is practically when
        /// the user releases the trigger.
        public override void OnPointerClickUp()
        {
            if (type == VRReticlePointerType.Press && _listGazeObjects.Count > 0)
                _listGazeObjects[0].OnGazeTrigger();
        }

        public override void GetPointerRadius(out float innerRadius, out float outerRadius)
        {
            innerRadius = 0;
            outerRadius = 0;
        }

        public void ExitAll()
        {
            if (stopCoroutine != null)
                stopCoroutine();
            foreach (var v in _listGazeObjects)
                v.OnGazeExit();
            _listGameObjects.Clear();
            _listGazeObjects.Clear();
        }

        private void StopTimeCoroutine()
        {
            if (this != null)
            {
                if (stopCoroutine != null)
                    stopCoroutine();
            }
        }
    }
}