﻿//----------------------------------------------------------------------------------
// 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.Collections.Generic;

namespace GVRNavi
{
    public class VR3DRoot : MonoBehaviour
    {
        #region variables

        public static VR3DRoot Instance { get { return _instance; } }
        private static VR3DRoot _instance;

        public Camera mainCamera;

        private List<VRRingBase> _listRings;
        private bool _needUpdateList = false;
        private bool _needUpdateRings = false;
        private bool _needCheckRingsVisibility = false;
        private VRRingBase _currentOpenedRing = null;

        #endregion


        #region MonoBehaviour Methods

        void Awake()
        {
            if (_instance == null)
            {
                _instance = this;
                _listRings = new List<VRRingBase>();
                VRRingBase.OnCreated.AddListener(OnRingCreated);
                VRRingBase.OnDestroyed.AddListener(OnRingDestroyed);
            }
            else
            {
                Debug.LogWarning("VR3DRoot exist already!");
                Destroy(this);
            }
        }

        void Start()
        {
            _needCheckRingsVisibility = true;
        }

        void OnDestroy()
        {
            if (this != null && _instance == this)
            {
                VRRingBase.OnCreated.RemoveListener(OnRingCreated);
                VRRingBase.OnDestroyed.RemoveListener(OnRingDestroyed);
                if (_listRings != null)
                    foreach (var ring in _listRings)
                        if (ring != null)
                            ring.OnLocalScaleChanged.RemoveListener(OnRingScaleChanged);
            }
        }

        void LateUpdate()
        {
            if (_needUpdateList)
            {
                _needUpdateList = false;
                _listRings.Sort(delegate (VRRingBase x, VRRingBase y)
                {
                    return -x.localPosition.y.CompareTo(y.localPosition.y);
                });
            }
            if (_needUpdateRings)
            {
                _needUpdateRings = false;
                for (int i = 1; i < _listRings.Count; i++)
                {
                    _listRings[i].localPosition = _listRings[i - 1].localPosition -
                        new Vector3(0f, _listRings[i - 1].collider.size.z * 0.5f + _listRings[i].collider.size.z * 0.5f, 0f);
                }
            }
            if (_needCheckRingsVisibility)
            {
                _needCheckRingsVisibility = false;
                CheckRingsVisibility(_currentOpenedRing);
            }
        }

        #endregion MonoBehaviour Methods

        #region Events

        private void OnRingCreated(VRRingBase ring)
        {
            if (ring != null)
            {
                ring.OnLocalScaleChanged.AddListener(OnRingScaleChanged);
                ring.OnRingEnter.AddListener(OnRingGazeEnter);
                ring.OnRingExit.AddListener(OnRingGazeExit);
                _listRings.Add(ring);
                _needUpdateList = true;
            }
        }

        private void OnRingDestroyed(VRRingBase ring)
        {
            if (ring != null)
            {
                ring.OnLocalScaleChanged.RemoveListener(OnRingScaleChanged);
                ring.OnRingEnter.RemoveListener(OnRingGazeEnter);
                ring.OnRingExit.RemoveListener(OnRingGazeExit);
                _listRings.Remove(ring);
                _needUpdateList = true;
            }
        }

        private void OnRingGazeEnter(VRRingBase ring)
        {
            if (_currentOpenedRing != ring)
            {
                _currentOpenedRing = ring;
                _needCheckRingsVisibility = true;
            }
        }

        private void OnRingGazeExit(VRRingBase ring)
        {
            if (_currentOpenedRing == ring)
            {
                _currentOpenedRing = null;
                _needCheckRingsVisibility = true;
            }
        }

        private void OnRingScaleChanged(VRRingBase ring, Vector3 v)
        {
            _needUpdateRings = true;
        }

        #endregion Events

        public virtual void UpdateRingsPosition()
        {
            _needUpdateList = true;
            _needUpdateRings = true;
        }

        protected virtual void CheckRingsVisibility(VRRingBase targetRing)
        {
            if (targetRing != null)
            {
                int index = _listRings.IndexOf(targetRing);
                if (index >= 0)
                    for (int i = 0; i < _listRings.Count; i++)
                        _listRings[i].SetGlobalAlpha(i == index ? 1f : (i == index + 1 || i == index - 1) ? 0.5f : 0f);
                else
                    Debug.LogError("Incorrect index: " + index);
            }
            else for (int i = 0; i < _listRings.Count; i++)
                    _listRings[i].SetGlobalAlpha(i == 0 ? 1f : 0f);
        }
    }
}