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

//瀑布流Scroll，没有居中功能（Todo），使用方法：添加回调onCreate，onUpdate
public class UILoop : MonoBehaviour
{
    //返回Index和相应的Gameobject
    public delegate void UILoopCallBack(int globalIndex, GameObject go);

    enum Direction
    {
        Horizontal,
        Vertical
    }

    [SerializeField]
    private RectTransform m_Cell;

    [SerializeField]
    private Vector2 m_CellGap;

    [SerializeField, Range(1, 50)]
    private int m_row = 5;

    [SerializeField, Range(1, 50)]
    private int m_column = 5;

    [SerializeField]
    private Direction direction = Direction.Horizontal;

    [SerializeField]
    private bool autoCollider = true;

    private Vector2 cellSize { get { return m_Cell.sizeDelta + m_CellGap; } }

    private float lineGap { get { return direction == Direction.Horizontal ? cellSize.x : cellSize.y; } }

    private float startPos { get { return direction == Direction.Horizontal ? -cacheRect.anchoredPosition.x : cacheRect.anchoredPosition.y; } }

    private int pageCapacity { get { return m_row * m_column; } }

    private int pageLine { get { return direction == Direction.Horizontal ? m_column : m_row; } }

    private int lineCapacity { get { return direction == Direction.Horizontal ? m_row : m_column; } }

    private int maxLineIndex
    {
        get
        {
            return Mathf.Max(0, (ItemsCount - 1) / lineCapacity);
        }
    }

    private int currentLineStartIndex
    {
        get
        {
            return Mathf.Max(0, Mathf.FloorToInt(startPos / lineGap));
        }
    }

    private int currentLineEndIndex
    {
        get
        {
            return Mathf.Min(currentLineStartIndex + pageLine, maxLineIndex);
        }
    }

    private int currentItemStartIndex
    {
        get
        {
            return currentLineStartIndex * lineCapacity;
        }
    }

    private int currentItemEndIndex
    {
        get
        {
            return Mathf.Min((currentLineEndIndex + 1) * lineCapacity - 1, ItemsCount - 1);
        }
    }

    private int m_ItemsCount = 0;
    public int ItemsCount
    {
        get
        {
            return m_ItemsCount;
        }
        set
        {
            m_ItemsCount = value;
            UpdateAll();
        }
    }

    private int m_lastLineStartIndex = -1;

    //创建每个Item后的回调
    public UILoopCallBack onCreate;
    //更新每个Item后的回调
    public UILoopCallBack onUpdate;

    private List<RectTransform> m_poolItems = new List<RectTransform>();
    private List<RectTransform> m_showItems = new List<RectTransform>();
    private int m_showIndexStart = 0;
    private int m_createIndex = 0;

    private static readonly Vector2 InvalidPos = new Vector2(99999f, 99999f);

    private RectTransform mRect;
    private RectTransform cacheRect
    {
        get
        {
            if (mRect == null)
            {
                mRect = GetComponent<RectTransform>();
                mRect.anchoredPosition = Vector2.zero;
                mRect.anchorMax = Vector2.up;
                mRect.anchorMin = Vector2.up;
                mRect.pivot = Vector2.up;
            }
            return mRect;
        }
    }

    private List<RectTransform> m_delayShowItems = new List<RectTransform>();
    private ScrollRect m_scrollRect;
    private ScrollRect scrollRect
    {
        get
        {
            if (m_scrollRect == null)
                m_scrollRect = this.GetComponentInParent<ScrollRect>();
            return m_scrollRect;
        }
    }

    void Awake()
    {
        if (m_Cell.gameObject.activeSelf)
            m_Cell.gameObject.SetActive(false);

        if (autoCollider)
        {
            Image img = this.GetComponent<Image>();
            if (img == null)
            {
                img = this.gameObject.AddComponent<Image>();
                img.color = new Color(1f, 1f, 1f, 0f);
            }
        }

        ResetBound();

        ScrollToGlobalIndex(0);
    }

    private RectTransform CreateItem()
    {
        RectTransform item = GameObject.Instantiate(m_Cell);
        item.SetParent(cacheRect, false);
        item.anchorMax = Vector2.up;
        item.anchorMin = Vector2.up;
        item.pivot = new Vector2(0.5f, 0.5f);
        item.anchoredPosition3D = Vector3.zero;
        //item.gameObject.SetActive(true);
        m_delayShowItems.Add(item);

        //回调
        if (onCreate != null)
            onCreate(m_createIndex, item.gameObject);

        m_createIndex++;
        return item;
    }

    private RectTransform GetItemFromPool()
    {
        if (m_poolItems.Count > 0)
        {
            RectTransform rt = m_poolItems[m_poolItems.Count - 1];
            m_poolItems.RemoveAt(m_poolItems.Count - 1);
            return rt;
        }
        return CreateItem();
    }

    void Update()
    {
        //Debug.LogWarning(maxLineIndex + " " + currentLineStartIndex + " " + currentLineEndIndex + " " + currentItemStartIndex + " " + currentItemEndIndex);

        if (m_delayShowItems.Count > 0)
        {
            m_delayShowItems[0].gameObject.SetActive(true);
            m_delayShowItems.RemoveAt(0);
        }

        if (m_lastLineStartIndex != currentLineStartIndex)
        {
            m_lastLineStartIndex = currentLineStartIndex;

            int itemStartIndex = currentItemStartIndex;
            int itemEndIndex = currentItemEndIndex;
            int count = itemEndIndex - itemStartIndex + 1;
            if (count > 0)
            {
                int oldShowStartIndex = m_showIndexStart;
                int oldShowEndIndex = m_showIndexStart + m_showItems.Count - 1;
                for (int i = oldShowEndIndex; i >= m_showIndexStart; i--)
                {
                    if (i >= itemStartIndex && i <= itemEndIndex)
                        continue;
                    int localIndex = i - m_showIndexStart;
                    RectTransform rt = m_showItems[localIndex];
                    m_showItems.RemoveAt(localIndex);
                    m_poolItems.Add(rt);
                    rt.anchoredPosition = InvalidPos;

                    if (i > itemEndIndex)
                        oldShowEndIndex--;
                    else
                        oldShowStartIndex++;
                }

                m_showIndexStart = itemStartIndex;

                for (int i = itemStartIndex; i <= itemEndIndex; i++)
                {
                    int localIndex = i - itemStartIndex;
                    if (i >= oldShowStartIndex && i <= oldShowEndIndex)
                    {
                        RectTransform rt = m_showItems[localIndex];
                        rt.anchoredPosition = GetPosByGlobalIndex(i);
                    }
                    else
                    {
                        RectTransform rt = GetItemFromPool();
                        rt.anchoredPosition = GetPosByGlobalIndex(i);
                        if (i < oldShowStartIndex)
                        {
                            m_showItems.Insert(localIndex, rt);
                        }
                        else
                        {
                            m_showItems.Add(rt);
                        }

                        if (onUpdate != null)
                            onUpdate(i, rt.gameObject);
                    }

                    // 测试编号显示
                    //Text text = m_showItems[localIndex].GetComponentInChildren<Text>();
                    //if (text)
                    //    text.text = i.ToString();
                }
            }
        }
    }

    private Vector2 GetPosByGlobalIndex(int globalIndex)
    {
        if (globalIndex < 0)
            globalIndex = 0;

        Vector2 nativeSize = m_Cell.sizeDelta;

        float x;
        float y;
        if (direction == Direction.Horizontal)
        {
            x = globalIndex / m_row;
            y = globalIndex % m_row;
        }
        else
        {
            x = globalIndex % m_column;
            y = globalIndex / m_column;
        }
        x = x * cellSize.x + nativeSize.x * 0.5f;
        y = -(y * cellSize.y + nativeSize.y * 0.5f);
        return new Vector2(x, y);
    }

    private void ResetBound()
    {
        int row = m_row;
        int column = m_column;

        if (direction == Direction.Horizontal)
        {
            column = m_ItemsCount / m_row;
            if (m_ItemsCount % m_row != 0)
                column++;

            // perfect line count for drag bound
            column = Mathf.Max(column, m_column - 1);
        }
        else
        {
            row = m_ItemsCount / m_column;
            if (m_ItemsCount % m_column != 0)
                row++;

            // perfect line count for drag bound
            row = Mathf.Max(row, m_row - 1);
        }

        cacheRect.sizeDelta = new Vector2(column * cellSize.x - m_CellGap.x, row * cellSize.y - m_CellGap.y);
    }

    //-------------------------------------对外接口

    //滚动到某个Item位置
    public void ScrollToGlobalIndex(int globalIndex)
    {
        Vector2 pos = GetPosByGlobalIndex(globalIndex);
        Vector2 curPos = cacheRect.anchoredPosition;
        Vector2 nativeSize = m_Cell.sizeDelta;

        // pivot offset
        pos.x -= 0.5f * nativeSize.x;
        pos.y += 0.5f * nativeSize.y;

        if (direction == Direction.Horizontal)
        {
            curPos.x = -pos.x;

            if (scrollRect)
            {
                float maxScroll = cacheRect.sizeDelta.x - (scrollRect.transform as RectTransform).sizeDelta.x;
                if (maxScroll > 0 && curPos.x < -maxScroll)
                    curPos.x = -maxScroll;
                else if (maxScroll < 0)
                    curPos.x = 0;
            }
        }
        else
        {
            curPos.y = -pos.y;

            if (scrollRect)
            {
                float maxScroll = cacheRect.sizeDelta.y - (scrollRect.transform as RectTransform).sizeDelta.y;
                if (maxScroll > 0 && curPos.y > maxScroll)
                    curPos.y = maxScroll;
                else if (maxScroll < 0)
                    curPos.y = 0;
            }
        }

        // 为了修正ScrollRect的Bound计算问题
        if (curPos.x == 0)
            curPos.x = 0.01f;
        if (curPos.y == 0)
            curPos.y = -0.01f;

        cacheRect.anchoredPosition = curPos;
    }

    public int GetItemGlobalIndex(GameObject go)
    {
        for (int i = m_showItems.Count - 1; i >= 0; i--)
        {
            if (m_showItems[i].gameObject == go)
                return currentItemStartIndex + i;
        }
        return -1;
    }

    public GameObject GetItemByLocalIndex(int localIndex)
    {
        if (localIndex >= 0 && localIndex < m_showItems.Count)
        {
            return m_showItems[localIndex].gameObject;
        }
        return null;
    }

    public GameObject GetItemByGlobalIndex(int globalIndex)
    {
        int localIndex = globalIndex - currentItemStartIndex;
        if (localIndex >= 0 && localIndex < m_showItems.Count)
            return m_showItems[localIndex].gameObject;
        return null;
    }

    public void UpdateAll()
    {
        m_lastLineStartIndex = -1;

        if (m_showItems.Count > 0)
        {
            for (int i = m_showItems.Count - 1; i >= 0; i--)
            {
                m_showItems[i].anchoredPosition = InvalidPos;
                m_poolItems.Add(m_showItems[i]);
            }
            m_showItems.Clear();
        }

        ResetBound();

        // 确保物件创建出来
        Update();
    }
}

#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(UILoop), true)]
public class UILoopEditor : UnityEditor.Editor
{
    int itemCount;

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        var comp = target as UILoop;
        UnityEditor.EditorGUILayout.HelpBox("以下只有编辑器有效", UnityEditor.MessageType.Info);
        UnityEditor.EditorGUILayout.LabelField("Item Count = " + comp.ItemsCount);
        GUILayout.BeginHorizontal();
        itemCount = UnityEditor.EditorGUILayout.IntField("New Count = ", itemCount);
        if (GUILayout.Button("Reset Count"))
        {
            comp.ItemsCount = itemCount;
        }
        GUILayout.EndHorizontal();
    }
}
#endif