// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

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

namespace HoloToolkit.Unity.InputModule
{
    /// <summary>
    /// A cursor that looks and acts more like the shell cursor.
    /// A two part cursor with visual feedback for all cursor states
    /// </summary>
    public class InteractiveMeshCursor : Cursor
    {
        [Tooltip("The ring or outer element")]
        public GameObject Ring;

        [Tooltip("Inner cursor element")]
        public GameObject Dot;

        [Tooltip("Point light")]
        public GameObject Light;

        [Tooltip("The scale factor to soften the distance scaling, we want the cursor to scale in the distance, but not disappear.")]
        public float DistanceScaleFactor = 0.3f;

        [Tooltip("The scale both elements will be at their default state")]
        public float DefaultScale = 0.75f;

        [Tooltip("The scale both elements will when pressed")]
        public float DownScale = 0.5f;

        [Tooltip("The scale both elements will a hand is visible")]
        public float UpScale = 1;

        [Tooltip("Time to scale between states")]
        public float ScaleTime = 0.5f;

        /// <summary>
        /// internal state and element management
        /// </summary>
        private float mTimer = 0;

        private bool mHasHover = false;
        private bool mHasHand = false;
        private bool mIsDown = false;
        private Vector3 mBaseScale = new Vector3(1, 1, 1);
        private Vector3 mTargetScale;
        private bool mIsVisible = true;

        private Vector3 mAwakeScale;

        private void Awake()
        {
            mAwakeScale = transform.localScale;
        }

        /// <summary>
        /// Decide which element (ring or dot) should be visible and at what scale
        /// </summary>
        /// <param name="state"></param>
        public override void OnCursorStateChange(CursorStateEnum state)
        {
            base.OnCursorStateChange(state);

            // the cursor state has changed, reset the animation timer
            if (mHasHand != this.IsHandVisible || mIsDown != this.IsInputSourceDown || mHasHover != (this.TargetedObject != null))
            {
                mTimer = 0;
            }

            mHasHand = this.IsHandVisible;
            mIsDown = this.IsInputSourceDown;
            mHasHover = this.TargetedObject != null;

            mTargetScale = mBaseScale * DefaultScale;
            bool showRing = false;

            switch (state)
            {
                case CursorStateEnum.None:
                    break;
                case CursorStateEnum.Observe:
                    break;
                case CursorStateEnum.ObserveHover:
                    showRing = true;
                    break;
                case CursorStateEnum.Interact:
                    showRing = true;
                    mTargetScale = mBaseScale * DownScale;
                    break;
                case CursorStateEnum.InteractHover:
                    showRing = true;
                    mTargetScale = mBaseScale * UpScale;
                    break;
                case CursorStateEnum.Select:
                    mTargetScale = mBaseScale * UpScale;
                    break;
                case CursorStateEnum.Release:
                    break;
                case CursorStateEnum.Contextual:
                    break;
                default:
                    break;
            }

            if (!mIsVisible)
            {
                return;
            }

            Ring.SetActive(showRing);
            Dot.SetActive(!showRing);

            // added observation of CursorModifier
            if (TargetedCursorModifier != null && mHasHover)
            {
                ElementVisibility(!TargetedCursorModifier.GetCursorVisibility());
            }
        }

        /// <summary>
        /// scale the cursor elements
        /// </summary>
        protected override void UpdateCursorTransform()
        {
            base.UpdateCursorTransform();

            // animate scale of ring and dot
            if (mTimer < ScaleTime)
            {
                mTimer += Time.deltaTime;
                if (mTimer > ScaleTime)
                {
                    mTimer = ScaleTime;
                }

                Ring.transform.localScale = Vector3.Lerp(mBaseScale * DefaultScale, mTargetScale, mTimer/ScaleTime);
                Dot.transform.localScale = Vector3.Lerp(mBaseScale * DefaultScale, mTargetScale, mTimer / ScaleTime);
            }

            // handle scale of main cursor go
            float distance = Vector3.Distance(GazeManager.Instance.GazeOrigin, transform.position);
            float smoothscaling = 1 - DefaultCursorDistance * DistanceScaleFactor;
            transform.localScale = mAwakeScale * (distance * DistanceScaleFactor + smoothscaling);
        }

        /// <summary>
        /// override the base class for custom visibility
        /// </summary>
        /// <param name="visible"></param>
        public override void SetVisibility(bool visible)
        {
            base.SetVisibility(visible);

            mIsVisible = visible;
            ElementVisibility(visible);

            if (visible)
            {
                OnCursorStateChange(CursorState);
            }
        }

        /// <summary>
        /// controls the visibility of cursor elements in one place
        /// </summary>
        /// <param name="visible"></param>
        private void ElementVisibility(bool visible)
        {
            if (Ring != null)
            {
                Ring.SetActive(visible);
            }

            if (Dot != null)
            {
                Dot.SetActive(visible);
            }

            if (Light != null)
            {
                Light.SetActive(visible);
            }
        }
    }
}
