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

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

namespace HoloToolkit.Unity
{
    public struct ComparableRaycastResult
    {
        public readonly int LayerMaskIndex;
        public readonly RaycastResult RaycastResult;

        public ComparableRaycastResult(RaycastResult raycastResult, int layerMaskIndex = 0)
        {
            RaycastResult = raycastResult;
            LayerMaskIndex = layerMaskIndex;
        }
    }

    public class RaycastResultComparer : IComparer<ComparableRaycastResult>
    {
        private static readonly List<Func<ComparableRaycastResult, ComparableRaycastResult, int>> Comparers = new List<Func<ComparableRaycastResult, ComparableRaycastResult, int>>
        {
            CompareRaycastsByLayerMaskPrioritization,
            CompareRaycastsBySortingLayer,
            CompareRaycastsBySortingOrder,
            CompareRaycastsByCanvasDepth,
            CompareRaycastsByDistance,
        };

        public int Compare(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            for (var i = 0; i < Comparers.Count; i++)
            {
                var result = Comparers[i](left, right);
                if (result != 0)
                {
                    return result;
                }
            }
            return 0;
        }

        private static int CompareRaycastsByLayerMaskPrioritization(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            //Lower is better, -1 is not relevant
            return right.LayerMaskIndex.CompareTo(left.LayerMaskIndex);
        }

        private static int CompareRaycastsBySortingLayer(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            //Higher is better
            return left.RaycastResult.sortingLayer.CompareTo(right.RaycastResult.sortingLayer);
        }

        private static int CompareRaycastsBySortingOrder(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            //Higher is better
            return left.RaycastResult.sortingOrder.CompareTo(right.RaycastResult.sortingOrder);
        }

        private static int CompareRaycastsByCanvasDepth(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            //Module is the graphic raycaster on the canvases.
            if (left.RaycastResult.module.transform.IsParentOrChildOf(right.RaycastResult.module.transform))
            {
                //Higher is better
                return left.RaycastResult.depth.CompareTo(right.RaycastResult.depth);
            }
            return 0;
        }

        private static int CompareRaycastsByDistance(ComparableRaycastResult left, ComparableRaycastResult right)
        {
            //Lower is better
            return right.RaycastResult.distance.CompareTo(left.RaycastResult.distance);
        }
    }
}
