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

public interface IListItemData
{
   Vector2 size { get; set; }//每项大小，此变量可不赋值，不用时直接用pref中的size，若pref中有ContentSizeFitter,则需要赋值
   GameObject pref { get; set; }
   GameObject gameObject { get; set; }

   void InitContent();//更新内容的方法
}

[RequireComponent(typeof(ScrollRect))]
public class ListItemsShow : MonoBehaviour,IBeginDragHandler,IEndDragHandler{

    ScrollRect scrollView;

    List<GameObject> itemList = new List<GameObject>();
    int fitItemNum;//总共需要生成的条目数

    public List<GameObject> VisiableItemList 
    {
        get
        {
            return itemList;
        }
    }

    public bool GenerateAll = false;//是否生成所有的item

    public bool ActiveAllOnInit = false;//生成所有的item时，是否初始化时全部SetActive（true）

    public Vector2 ViewportSize { get; set; }//Viewport的高宽

    public List<IListItemData> itemDataList = new List<IListItemData>();

    public bool AutoStop = false;//是否自动停靠
   
    List<Vector2> posList = new List<Vector2>();
    Vector2 posOffset;
    int itemsPerRow;//每行条目数
    
    Vector2 contenOriginalPos;

    int preFirstItemIndex = 0;

    Action<float> OnPositionUpdate;

    /// <summary>
    /// Item是否可见
    /// </summary>
    Dictionary<int, bool> m_ItemVisiable = new Dictionary<int, bool>();

    int _setItemsNum = 0;//手动设定总共生成的条目数,需在init前设置
    public void SetVisiableItemsNum(int num)
    {
        _setItemsNum = num;
    }

    bool _delayInit;//是否延迟一阵初始化UI，有些界面的 viewport 的size需要延迟一帧才能获取正确的大小
    public bool DelayInit { get { return this._delayInit; }set { this._delayInit = value; } }

    void Awake()
    {
        InitUI();
        contenOriginalPos = scrollView.content.anchoredPosition;
    }

    void Start()
    {
    }

    void InitUI()
    {
        scrollView = GetComponent<ScrollRect>();
        ViewportSize = getViewRect();
    }

    bool hasInit = false;

    static int numOverflow = 2;

    /// <summary>
    /// 底部位置
    /// </summary>
    float bottomPos
    {
        get 
        {
            if(Upward)
                return  ViewportSize.y - GetTotalHeight() ;
            else
                return  GetTotalHeight() - ViewportSize.y;
        }
    }

    public bool Upward = false;//是否向上
    
    /// <summary>
    /// 
    /// </summary>
    /// <param name="itemDatas"></param>
    /// <param name="itemPref"></param>
    /// <param name="defaultPosition">第一个条目序号</param>
    /// <param name="upward">一开始便显示最后一条</param>
    /// <param name="posOffset">位置偏移</param>
    /// <param name="numPerRow">每行几个</param>
    public void Init(List<IListItemData> itemDatas, Action<float> onPositionUpdate = null, float defaultPosition = 0,bool upward = false,Vector2 posOffset = new Vector2(),int numPerRow = 1)
    {
        Reset();

        this.itemDataList = itemDatas;
        this.Upward = upward;
        this.posOffset = posOffset;
        this.itemsPerRow = numPerRow;

        for (int i = 0; i < itemDataList.Count; i++)
            this.m_ItemVisiable[i] = false ;

        if (_delayInit)
        {
            if (delayInitCoroutine != null)
                StopCoroutine(delayInitCoroutine);

            delayInitCoroutine = StartCoroutine(PlayDelayInit(onPositionUpdate, defaultPosition));
        }
        else
        {
            Initialize(onPositionUpdate, defaultPosition);
        }

        _delayInit = false;

        Debug.Log("itemDataList.Count:" + itemDataList.Count);
    }

    void Initialize(Action<float> onPositionUpdate = null, float defaultPosition = 0)
    {
        InitUI();
        SetPivot(scrollView.content, Upward);

        if (AutoStop)
            scrollView.inertia = false;

        this.OnPositionUpdate = onPositionUpdate;

        fitItemNum = GetFitItemNum();

        SetContentSize();
        CalculateItemsPos(Upward);

        InitItemList(defaultPosition, Upward);

        scrollView.onValueChanged.AddListener(OnValueChange);
        //scrollView.OnEndDrag();
        hasInit = true;

    }

    Coroutine delayInitCoroutine;
    IEnumerator PlayDelayInit(Action<float> onPositionUpdate = null, float defaultPosition = 0)
    {
        yield return 0;
        Initialize(onPositionUpdate, defaultPosition);
    }

    public bool CheckIfShowing(IListItemData data)
    {
        return itemList.Contains(data.gameObject);
    }

    public void GotoIndex(int index)
    {
        if (index <= 0 || index >= posList.Count)
            return;

        var pos = IndexToContentPos(index,Upward) ;
        Init(itemDataList, OnPositionUpdate, pos.y, Upward, posOffset, itemsPerRow);
    }

    public GameObject GotoItem(int index) 
    {
       // var oldFirstItemIndex = preFirstItemIndex;

        if (index < 0 || index >= posList.Count)
            return null;

        GotoIndex(index);

        return itemDataList[index].gameObject;
    }


    /// <summary>
    /// 获取总共生成的条目数
    /// </summary>
    /// <returns></returns>
    int  GetFitItemNum() 
    {
        ///自动计算，根据第一个项高度计算
        int num  = Mathf.FloorToInt(ViewportSize.y / GetItemHeight(0)) + numOverflow;
        num = num < 0 ? 0 : num;
        num *= itemsPerRow;

        //应用手动设置的
        if (_setItemsNum > 0)
            num = _setItemsNum;

        num = Mathf.Min(itemDataList.Count, num);

        return num;
    }
        

    /// <summary>
    /// 回到初始位置；停止工作
    /// </summary>
    public void Reset()
    {
        hasInit = false;
        preFirstItemIndex = 0; 
        contentHeight = 0;
        itemDataList = new List<IListItemData>();
        if (scrollView!=null)
        {
            scrollView.StopMovement();
            scrollView.content.anchoredPosition = contenOriginalPos;
        }
        foreach(var go in itemList)
        {
            Destroy(go.gameObject);
        }
        itemList = new List<GameObject>();
        ClearCache();
    }

    float GetItemHeight(int index)//条目的高度
    {
        if (itemDataList.Count <= 0 )
            return 100;
        
        if (itemDataList[index].size.y != 0)
        {
            return itemDataList[index].size.y;
        }

        return itemDataList[index].pref.GetComponent<RectTransform>().rect.height;//.sizeDelta.y;
    }

    float GetItemWidth(int index)//条目的宽度
    {
        if (itemDataList.Count <= 0)
            return 100;

        if (index < 0)
            index = 0;

        if (index > itemDataList.Count-1)
            index = itemDataList.Count - 1;

        if (itemDataList[index].size.x != 0)
        {
            return itemDataList[index].size.x;
        }

        return itemDataList[index].pref.GetComponent<RectTransform>().rect.width;//.sizeDelta.x;
    }

    /// <summary>
    /// 设置初始位置
    /// </summary>
     void SetInitPos(int defaultIndex, float defaultPosition,bool upward = false)
    {

        preFirstItemIndex = defaultIndex;
        SetItemPosition(defaultIndex);

        SetContentPosition(new Vector2(contenOriginalPos.x, defaultPosition));
    }

    void InitItemList(float defaultPosition = 0,bool upward = false)
    {
        if (upward)
        {
            if (defaultPosition <  -GetTotalHeight())//条目数比原来少，位置超出，则显示最后一条
                defaultPosition = -GetTotalHeight();

            if (defaultPosition > 0)//限制初始范围
                defaultPosition = 0;
        }
        else
        {
           // if (defaultPosition - contenOriginalPos.y > GetTotalHeight())
           if (defaultPosition  > GetTotalHeight())//条目数比原来少，位置超出，则显示最后一条
                defaultPosition = GetTotalHeight();
            
            if (defaultPosition < 0)//限制初始范围
                defaultPosition = 0;
        }

        var defaultIndex = ContentPosToFirstItemIndex(defaultPosition,upward);

        if (defaultIndex + fitItemNum > itemDataList.Count)//超过了则回退
        {
            defaultIndex = itemDataList.Count - fitItemNum;
        }
        for (int i=0 ; i < fitItemNum; i++)
        {
            GenerateItem(defaultIndex + i);
        }

        SetInitPos(defaultIndex, defaultPosition);

        if (GenerateAll)//若是生成全部，则在协程把item都生成
        {
            if (generateCoroutine != null)
                StopCoroutine(generateCoroutine);

            if(gameObject.activeInHierarchy)
                generateCoroutine = StartCoroutine(GenerateAllOnBackground());
        }
    }

    Coroutine generateCoroutine;
    IEnumerator GenerateAllOnBackground()
    {
        yield return null;
        for (int j = 0; j < itemDataList.Count; j++)
        {
            if (!cacheAll.ContainsKey(itemDataList[j]))
            {
                var go = InstantiateItem(itemDataList[j]);
                if (ActiveAllOnInit)
                {
                    go.SetActive(true);
                    if (j < posList.Count)
                        go.GetComponent<RectTransform>().anchoredPosition = posList[j];
                }
                else
                {
                    go.SetActive(false);
                }
                go.name = j.ToString();
                PutIntoCache(itemDataList[j], itemDataList[j].gameObject);
                yield return null;
            }
        }
    }

    /// <summary>
    /// 获取框大小
    /// </summary>
    /// <param name="rect"></param>
    /// <returns></returns>
    Vector2 getViewRect()
    {
        if (scrollView.viewport.rect.width > 0 && scrollView.viewport.rect.height > 0)
        {
            return scrollView.viewport.rect.size;
        }
        if (scrollView.content.sizeDelta.x > 0 && scrollView.content.sizeDelta.y > 0)
        {
            Debug.Log("scrollView.content.sizeDelta:" + scrollView.content.sizeDelta);
            return scrollView.content.sizeDelta;
        }

        var scrollViewRect = scrollView.GetComponent<RectTransform>();
        Debug.Log("scrollViewRect.rect.size:" + scrollViewRect.rect.size);
        return scrollViewRect.rect.size;
    }

    /// <summary>
    /// 设置滑动板大小
    /// </summary>
    void SetContentSize()
    {
        var size = scrollView.content.sizeDelta;
      // DYDebug.Log("GetTotalHeight:" + GetTotalHeight());
        scrollView.content.sizeDelta = new Vector2(size.x, GetTotalHeight() + posOffset.y);
    }

    /// <summary>
    /// 总高度
    /// </summary>
    float contentHeight = 0;
    float GetTotalHeight() 
    {
        if (contentHeight != 0)
        {
            return contentHeight;
        }
        float totlaHeight = 0;
        for (int i = 0; i < itemDataList.Count; i += itemsPerRow)
        {
            totlaHeight += GetItemHeight(i)+spacing.y;
        }
        contentHeight = totlaHeight;

        return contentHeight;
    }

    public Vector2 spacing = new Vector2(0, 0);
    public enum Align 
    { 
        left,middle,right
    }
    public Align align = Align.left;
    /// <summary>
    /// 计算出每个序号对应条目的位置
    /// </summary>
   void CalculateItemsPos(bool upward = false)
    {

        posList = new List<Vector2>();
        float curHeight = 0;
        for (int i = 0; i < itemDataList.Count;i ++)
        {
            var itemPos = itemDataList[i].pref.GetComponent<RectTransform>().anchoredPosition;

            int col = i % itemsPerRow;
            int j = i % itemsPerRow;
            float posX = 0;

            var curRowFirst = i - j;
            while (j > 0)//计算X坐标
            {
                posX += GetItemWidth(curRowFirst + j - 1) + spacing.x;
                j--;
            }

            float adjustX = GetAlignAjustX(i / itemsPerRow);

            if (upward)
            {
                posList.Add(posOffset + new Vector2(posX + adjustX, curHeight));

                if (col == itemsPerRow - 1)
                    curHeight += GetItemHeight(i) + spacing.y;
            }
            else
            {
                posList.Add(-posOffset + new Vector2(posX + adjustX, curHeight));

                if (col == itemsPerRow - 1)
                    curHeight -= GetItemHeight(i) + spacing.y;
            }
            
        }
    }

    /// <summary>
   /// 根据对齐计算X坐标偏移值，row 从0开始
    /// </summary>
   float  GetAlignAjustX(int row) 
   {
       if (align == Align.left)
           return 0;

       float contentWidth = 0;

       int curRowFirst = row  * itemsPerRow;
       for (int i = curRowFirst; i < curRowFirst + itemsPerRow; i++)
       {
           contentWidth += GetItemWidth(i);
       }
       contentWidth += spacing.x * (itemsPerRow - 1);

       var adjustX = ViewportSize.x - contentWidth;

     // DYDebug.Log("ViewportSize.x:" + ViewportSize.x + "   GetAlignAjustX row:" + row + "  contentWidth:" + contentWidth + "  adjustX:" + adjustX);

       adjustX = adjustX < 0 ? 0 : adjustX;

       switch (align)
       {
           case Align.middle:return  adjustX /= 2; 
           case Align.right: return adjustX; 
       }
       return 0;
   }

    /// <summary>
    /// 根据方向设置锚点
    /// </summary>
    /// <param name="rect"></param>
   void SetPivot(RectTransform rect,bool upward)
   {
       var originalPivot = rect.pivot;
       var originalSize = rect.sizeDelta;

       if (upward)
       {
           rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom,0, originalSize.y);
           rect.pivot = new Vector2(originalPivot.x, 0);
       }
       else
       {
           rect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, originalSize.y);
           rect.pivot = new Vector2(originalPivot.x, 1);
       }
   }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="index">数据列表中的序号</param>
    /// <param name="addAtEnd">在最后加或在最前加</param>
   void GenerateItem(int index, bool addAtEnd = true)
    {
        if (index >= itemDataList.Count || index < 0)
            return;

       // DYDebug.Log("GenerateItem:" + index);
        var go = InstantiateItem(itemDataList[index]);
        
        itemToData[go] = itemDataList[index];
        itemList.Add(go);
        go.name = index.ToString();
        PutIntoCache(itemDataList[index], go);//放入缓存

    }

   GameObject InstantiateItem(IListItemData data)
   {
       var go = Instantiate(data.pref.gameObject, scrollView.content.transform) as GameObject; 
       go.SetActive(true);
       data.gameObject = go;
       go.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
       try
       {
           data.InitContent();
       }
       catch (Exception e)
        {
           Debug.Log(e.Message);
        }
        SetPivot(go.GetComponent<RectTransform>(), Upward);
       return go;
   }

    private Dictionary<GameObject, IListItemData> itemToData = new Dictionary<GameObject, IListItemData>();

    public Action<GameObject> onItemJustShow;//item刚进入屏幕显示出来的时候
    /// <summary>
    /// 
    /// </summary>
    /// <param name="index">序号</param>
    /// <param name="addAtEnd">往后或往前</param>
   void SetItemListContent(int index)
   {
      // DYDebug.Log("SetItemListContent:" + index);
       if (index >= itemDataList.Count - fitItemNum)
             index = itemDataList.Count - fitItemNum;

       if (index < 0)
           index = 0;
       
        //从缓存中获取GameObject，有新生成的，将
       for (int i = 0; i < fitItemNum;i++)
       {
            var curIndex = index + i;

            if (curIndex >= itemDataList.Count)
                continue;
            
            var curGo = GetGameObjectFromCache(itemDataList[curIndex]);

            itemList[i] = curGo;

            if (!GenerateAll || itemDataList[curIndex].gameObject == null)//GenerateAll的时候若已生成了对象就不再刷新内容
            {
                itemToData[curGo] = itemDataList[curIndex];

                itemDataList[curIndex].gameObject = curGo;
                itemList[i].transform.SetAsLastSibling();
                try
                {
                    itemDataList[curIndex].InitContent();
                }
                catch (Exception e)
                {
                    Debug.Log(e.Message);
                }
            }
            
            if (!m_ItemVisiable[curIndex])
            {
                if (onItemJustShow != null)
                    onItemJustShow(curGo);
            }
            m_ItemVisiable[curIndex] = true;

        }

        for (int i = 0; i < index; i++)
            m_ItemVisiable[i] = false;

        for (int i = index+ fitItemNum; i < m_ItemVisiable.Count; i++)
            m_ItemVisiable[i] = false;

        //有剩余的都隐藏，然后把当前用着的重新放入缓存

        if (GenerateAll)
       {
           foreach (var list in cacheAll.Values)
           {
               foreach (var go in list)
               {
                   if(go != null)
                    go.SetActive(false);
               }
           }
       }
       else
       {
           foreach (var list in cache.Values)
           {
               foreach (var go in list)
               {
                   if (go != null)
                    go.SetActive(false);
               }
           }
       }
       for (int j = 0; j < fitItemNum; j++)
       {
           var curIndex = index + j;
           PutIntoCache(itemDataList[curIndex],itemDataList[curIndex].gameObject);
       }
      // DYDebug.Log("itemList.Count:" + itemList.Count);
   }

   Dictionary<GameObject, List<GameObject>> cache = new Dictionary<GameObject, List<GameObject>>();
   Dictionary<IListItemData, List<GameObject>> cacheAll = new Dictionary<IListItemData, List<GameObject>>();//每个数据都生成一个对象，只是划到的时候显示，看不见的时候隐藏

   void PutIntoCache(IListItemData data,GameObject go) 
   {
       if (GenerateAll)
       {
           if (cacheAll.ContainsKey(data))
           {
               cacheAll[data].Add(go);
           }
           else
           {
               cacheAll[data] = new List<GameObject>();
               cacheAll[data].Add(go);
           }
       }
       else
       {
           if (cache.ContainsKey(data.pref))
           {
               cache[data.pref].Add(go);
           }
           else
           {
               cache[data.pref] = new List<GameObject>();
               cache[data.pref].Add(go);
           }
       }
   }

    /// <summary>
    /// 从缓存中获取GameObject,没有则生成一个
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
   GameObject GetGameObjectFromCache(IListItemData data)
   {
       if (GenerateAll)
       {
           if (cacheAll.ContainsKey(data))
           {
               if (cacheAll[data].Count > 0)
               {
                   var go = cacheAll[data][0];
                   go.SetActive(true);
                   cacheAll[data].Remove(go);
                   return go;
               }
           }
       }
       else
       {
           if (cache.ContainsKey(data.pref))
           {
				int count = cache[data.pref].Count;

				while(count > 0)
				{
					var go = cache[data.pref][count - 1];
					if (go == null)
					{
						cache[data.pref].RemoveAt(count - 1);
						count = cache[data.pref].Count;
						continue;
					}

					go.SetActive(true);
					cache[data.pref].RemoveAt(count - 1);

					return go;
				}
           }
       }
       var newgo = InstantiateItem(data);

       return newgo;
   }

   //将缓存中的GameObejct都销毁
   void ClearCache()
   {
       foreach(var list in cache.Values)
       {
           foreach(var go in list)
           {
               Destroy(go);
           }
       }
       cache = new Dictionary<GameObject, List<GameObject>>();

        foreach (var list in cacheAll.Values)
        {
            foreach (var go in list)
            {
                Destroy(go);
            }
        }
        cacheAll = new Dictionary<IListItemData, List<GameObject>>();
    }

    /// <summary>
    /// 设置条目位置
    /// </summary>
    /// <param name="firstVisiableItemIndex"></param>
   void SetItemPosition(int firstItemIndex,bool upward = false) 
   {
       int i = 0;

       if (firstItemIndex + itemList.Count > posList.Count)
           firstItemIndex = posList.Count - itemList.Count;

        foreach (var itemGo in itemList)
        {
            itemGo.GetComponent<RectTransform>().anchoredPosition = posList[firstItemIndex + i];
            i++;
        }
   }

   /// <summary>
   /// 设置content位置
   /// </summary>
   /// <param name="targetPos"></param>
   void SetContentPosition(Vector2 targetPos) 
   {
      // DYDebug.Log("SetContentPosition:" + targetPos);
       scrollView.content.anchoredPosition = targetPos;
   }

    /// <summary>
   /// 根据位置计算出第一个条目
    /// </summary>
    /// <param name="y"></param>
   int ContentPosToFirstItemIndex(float posY, bool upward = false) 
    {
        int index = itemDataList.Count - 1 - itemDataList.Count % itemsPerRow;

       float addUp = upward ? -posOffset.y : posOffset.y;

       for (int i = 0; i < posList.Count; i++)
       {
           if (upward)
           {
                if (posY > -posList[i].y)
                {
                    index = i - itemsPerRow ;
                    break;
                }
           }
           else 
           {
               if (posY < -posList[i].y)
               {
                   index = i - itemsPerRow ;
                   break;
               }
           }
       }

       index = index < 0 ? 0 : index;
   
     //  DYDebug.Log("posY:" + posY + "  index:" + index + "    addUp:" + addUp );
       return index ;
    }

   Vector2 IndexToContentPos(int index, bool upward)
   {
       return new Vector2( contenOriginalPos.x,-posList[index].y);
   }

   // public int 
    void OnValueChange(Vector2 value)
    {
        if (!hasInit)
            return;

        var curFirstItemIndex = ContentPosToFirstItemIndex(scrollView.content.anchoredPosition.y,Upward);

        if (preFirstItemIndex != curFirstItemIndex
            && curFirstItemIndex >= 0
            && curFirstItemIndex <= itemDataList.Count - fitItemNum + (itemsPerRow - itemDataList.Count % itemsPerRow))
        {
            if (itemList.Count <= 0)
                return;
            
            if (curFirstItemIndex != preFirstItemIndex)
            {
                preFirstItemIndex = curFirstItemIndex;

                SetItemListContent(curFirstItemIndex);
            }

            SetItemPosition(curFirstItemIndex);
        }

        if (OnPositionUpdate != null)
            OnPositionUpdate(scrollView.content.anchoredPosition.y);

    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        EndAnim();

      //  scrollView.inertia = true;
    }

    public void OnEndDrag(PointerEventData eventData)
    {

    }
    

    void Update()
    {
        Refresh();
    }

   // public bool SelfRefresh = false;

    void Refresh()
    {
        foreach (var visiable in itemList)
        {
            if (visiable == null || !visiable.activeInHierarchy)
            {
                itemDataList.Remove(itemToData[visiable]);
                Init(itemDataList,this.OnPositionUpdate,scrollView.content.anchoredPosition.y,this.Upward,this.posOffset,this.itemsPerRow);
            }
        }
    }
    

    void EndAnim()
    {
        scrollView.StopMovement();
    }
    
}
