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

using UnityEngine;

#if UNITY_2017_2_OR_NEWER
using System.Collections;
using UnityEngine.XR;
#else
using UnityEngine.VR;
#endif

namespace HoloToolkit.Unity.Boundary
{
    /// <summary>
    /// Use this script on GameObjects you wish to be aligned in certain ways depending on the application space type.
    /// For example, if you want to place an object at the height of the user's head in a room scale application, check alignWithHeadHeight.
    /// In a stationary scale application, this is equivalent to placing the object at a height of 0.
    /// You can also specify specific locations to place the object based on space type.
    /// 
    /// This script runs once, on GameObject creation.
    /// 
    /// See https://developer.microsoft.com/en-us/windows/mixed-reality/coordinate_systems_in_unity for more information.
    /// <see cref="BoundaryManager"/> for TrackingSpaceType settings.
    /// </summary>
    public class SceneContentAdjuster : MonoBehaviour
    {
        private enum AlignmentType
        {
            AlignWithHeadHeight,
            UsePresetPositions,
            UsePresetXAndZWithHeadHeight
        }

        [SerializeField]
        [Tooltip("Optional container object reference. If null, this script will move the object it's attached to.")]
        private Transform containerObject = null;

#if UNITY_2017_2_OR_NEWER
        [SerializeField]
        [Tooltip("Select this if the container should be placed in front of the head on app launch in a room scale app.")]
        private AlignmentType alignmentType = AlignmentType.AlignWithHeadHeight;

        [SerializeField]
        [Tooltip("Use this to set the desired position of the container in a stationary app. This will be ignored if AlignWithHeadHeight is set.")]
        private Vector3 stationarySpaceTypePosition = Vector3.zero;

        [SerializeField]
        [Tooltip("Use this to set the desired position of the container in a room scale app. This will be ignored if AlignWithHeadHeight is set.")]
        private Vector3 roomScaleSpaceTypePosition = Vector3.zero;

        private Vector3 contentPosition = Vector3.zero;

        private int frameWaitHack = 0;
#endif

        private void Awake()
        {
            if (containerObject == null)
            {
                containerObject = transform;
            }

#if UNITY_2017_2_OR_NEWER
            // If no XR device is present, the editor will default to (0, 0, 0) and no adjustment is needed.
            // This script runs on both opaque and transparent display devices, since the floor offset is based on
            // TrackingSpaceType and not display type.
            if (XRDevice.isPresent)
            {
                StartCoroutine(SetContentHeight());
                return;
            }
#endif
            Destroy(this);
        }

#if UNITY_2017_2_OR_NEWER
        private IEnumerator SetContentHeight()
        {
            if (frameWaitHack < 1)
            {
                // Not waiting a frame often caused the camera's position to be incorrect at this point. This seems like a Unity bug.
                frameWaitHack++;
                yield return null;
            }

            if (alignmentType == AlignmentType.UsePresetPositions || alignmentType == AlignmentType.UsePresetXAndZWithHeadHeight)
            {
                if (XRDevice.GetTrackingSpaceType() == TrackingSpaceType.RoomScale)
                {
                    containerObject.position = roomScaleSpaceTypePosition;
                }
                else if (XRDevice.GetTrackingSpaceType() == TrackingSpaceType.Stationary)
                {
                    containerObject.position = stationarySpaceTypePosition;
                }
            }

            if (alignmentType == AlignmentType.AlignWithHeadHeight || alignmentType == AlignmentType.UsePresetXAndZWithHeadHeight)
            {
                contentPosition.x = containerObject.position.x;
                contentPosition.y = containerObject.position.y + CameraCache.Main.transform.position.y;
                contentPosition.z = containerObject.position.z;

                containerObject.position = contentPosition;
            }
        }
#endif
    }
}
