// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using UnityEngine;

namespace HoloToolkit.Unity
{
    /// <summary>
    /// Utility component to animate and visualize a clipping plane that can be used with 
    /// the "MixedRealityToolkit/Standard" shader "_ClippingPlane" feature.
    /// </summary>
    [ExecuteInEditMode]
    public class ClipPlane : MonoBehaviour
    {
        [SerializeField]
        private Renderer[] renderers = null;

        private int clipPlaneID;
        private Material[] materials;
        private MaterialPropertyBlock materialPropertyBlock;

        private void OnEnable()
        {
            Initialize();
            UpdatePlanePosition();
            ToggleClippingPlane(true);
        }

        private void OnDisable()
        {
            UpdatePlanePosition();
            ToggleClippingPlane(false);
        }

#if UNITY_EDITOR
        private void Update()
        {
            if (Application.isPlaying)
            {
                return;
            }

            Initialize();
            UpdatePlanePosition();
        }
#endif

        private void LateUpdate()
        {
            UpdatePlanePosition();
        }

        private void OnDrawGizmosSelected()
        {
            if (!enabled)
            {
                return;
            }

            Gizmos.matrix = transform.localToWorldMatrix;
            Gizmos.DrawLine(Vector3.zero, Vector3.up * 0.5f);
            Gizmos.DrawWireCube(Vector3.zero, new Vector3(1.0f, 0.0f, 1.0f));
        }

        private void OnDestroy()
        {
            if (materials != null)
            {
                foreach (Material material in materials)
                {
                    if (Application.isPlaying)
                    {
                        Destroy(material);
                    }
                }

                materials = null;
            }
        }

        private void Initialize()
        {
            clipPlaneID = Shader.PropertyToID("_ClipPlane");

            materials = new Material[renderers.Length];

            for (int i = 0; i < renderers.Length; ++i)
            {
                if (Application.isPlaying)
                {
                    materials[i] = renderers[i].material;
                }
                else
                {
                    materials[i] = renderers[i].sharedMaterial;
                }
            }

            materialPropertyBlock = new MaterialPropertyBlock();
        }

        private void UpdatePlanePosition()
        {
            if (renderers == null)
            {
                return;
            }

            Vector3 up = transform.up;
            Vector4 plane = new Vector4(up.x, up.y, up.z, Vector3.Dot(up, transform.position));

            foreach (Renderer renderer in renderers)
            {
                if (renderer == null)
                {
                    continue;
                }

                renderer.GetPropertyBlock(materialPropertyBlock);
                materialPropertyBlock.SetVector(clipPlaneID, plane);
                renderer.SetPropertyBlock(materialPropertyBlock);
            }
        }

        private void ToggleClippingPlane(bool isClippingPlaneOn)
        {
            if (materials == null)
            {
                return;
            }

            foreach (Material material in materials)
            {
                if (material == null)
                {
                    continue;
                }

                const string clippingPlaneKeyword = "_CLIPPING_PLANE";

                if (isClippingPlaneOn)
                {
                    if (!material.IsKeywordEnabled(clippingPlaneKeyword))
                    {
                        material.EnableKeyword(clippingPlaneKeyword);
                    }
                }
                else
                {
                    if (material.IsKeywordEnabled(clippingPlaneKeyword))
                    {
                        material.DisableKeyword(clippingPlaneKeyword);
                    }
                }
            }
        }
    }
}
