﻿using System;
using UnityEngine;

namespace JLGames.RocketDriver.Actions.Component2D
{
    public class Sortable2DContainer : MonoBehaviour
    {
        /// <summary>
        /// Sort mode
        /// 排序模式
        /// </summary>
        public Sortable2D.SortMode SortMode;

        /// <summary>
        /// Use local coordinate system
        /// 使用本地坐标系
        /// </summary>
        public bool UseLocalPosition = false;

        /// <summary>
        /// Whether to sort in reverse
        /// 是否反向排序
        /// </summary>
        public bool Reverse = false;

        /// <summary>
        /// Refresh mode
        /// 刷新模式
        /// </summary>
        public Sortable2D.RefreshMode RefreshMode;

        /// <summary>
        /// Minimum Z value in ZIndex mode
        /// ZIndex模式下最小Z值 
        /// </summary>
        public float MinZ = 0;

        /// <summary>
        /// Maximum Z value in ZIndex mode
        /// ZIndex模式下最大Z值 
        /// </summary>
        public float MaxZ = 10;

        //-------------------

        /// <summary>
        /// Whether to sort refresh in Update method
        /// 是否在Update方法中排序刷新
        /// </summary>
        public bool SortInUpdate = true;

        /// <summary>
        /// Sort refresh frequency
        /// 排序刷新频率
        /// </summary>
        public float Frequency = 0.4f;

        private float m_AddTime;

        private void Update()
        {
            if (!SortInUpdate) return;

            var childCount = transform.childCount;
            if (childCount <= 1)
            {
                return;
            }

            m_AddTime += Time.deltaTime;
            if (m_AddTime < Frequency)
            {
                return;
            }

            m_AddTime = 0;
            SortChildren();
        }

        public void SortChildren()
        {
            var childCount = transform.childCount;
            if (childCount <= 1)
            {
                return;
            }

            var children = new Transform[childCount];
            for (var index = 0; index < childCount; index++)
            {
                children[index] = transform.GetChild(index);
            }

            Array.Sort(children, CompareTransform);
            RefreshChildren(children);
        }

        private void RefreshChildren(Transform[] children)
        {
            switch (RefreshMode)
            {
                case Sortable2D.RefreshMode.SiblingIndex:
                    RefreshChildrenBySiblingIndex(children);
                    return;
                case Sortable2D.RefreshMode.ZIndex:
                    RefreshChildrenByZIndex(children);
                    return;
            }
        }

        private void RefreshChildrenBySiblingIndex(Transform[] children)
        {
            for (var index = children.Length - 1; index >= 0; index--)
            {
                if (index == children[index].GetSiblingIndex())
                {
                    continue;
                }

                children[index].SetSiblingIndex(index);
            }
        }

        private void RefreshChildrenByZIndex(Transform[] children)
        {
            var zSize = Math.Abs(MaxZ - MinZ);
            var zMin = Math.Min(MinZ, MaxZ);
            var add = zSize >= children.Length ? 1 : zSize / children.Length;

            if (UseLocalPosition)
            {
                for (var index = children.Length - 1; index >= 0; index--)
                {
                    var pos = children[index].localPosition;
                    pos.z = zMin + index * add;
                    children[index].localPosition = pos;
                }
            }
            else
            {
                for (var index = children.Length - 1; index >= 0; index--)
                {
                    var pos = children[index].position;
                    pos.z = zMin + index * add;
                    children[index].position = pos;
                }
            }
        }

        private int CompareTransform(Transform a, Transform b)
        {
            var aPos = UseLocalPosition ? a.localPosition : a.position;
            var bPos = UseLocalPosition ? b.localPosition : b.position;
            var rs = 0;
            switch (SortMode)
            {
                case Sortable2D.SortMode.X:
                    rs = aPos.x.CompareTo(bPos.x);
                    break;
                case Sortable2D.SortMode.Y:
                    rs = aPos.y.CompareTo(bPos.y);
                    break;
                case Sortable2D.SortMode.XY:
                    var rx = aPos.x.CompareTo(bPos.x);
                    rs = 0 == rx ? aPos.y.CompareTo(bPos.y) : rx;
                    break;
                case Sortable2D.SortMode.YX:
                    var ry = aPos.y.CompareTo(bPos.y);
                    rs = 0 == ry ? aPos.x.CompareTo(bPos.x) : ry;
                    break;
            }

            return Reverse ? -rs : rs;
            ;
        }
    }
}