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

[Serializable]
public sealed class PageChangedEvent : UnityEvent<int> {}

public class PageScrollRect : ScrollRect, IPointerDownHandler {

    [SerializeField]
    private float duration = 0.2f;
    [SerializeField]
    private Button prevButton = default;
    [SerializeField]
    private Button nextButton = default;
    [SerializeField]
    private PageChangedEvent onPageChanged = default;
    public PageChangedEvent OnPageChanged {
        get{ return onPageChanged; }
    }

    private float pageWidth;
    private int prevPageIndex = 0;
    private int prevPageIndexLate = 0;
    private Coroutine coroutine;

    protected override void Awake(){
        base.Awake();

        var grid = content.GetComponent<GridLayoutGroup>();
        pageWidth = grid.cellSize.x + grid.spacing.x;

        if(prevButton){
            prevButton.gameObject.SetActive(prevPageIndex > 0);
            prevButton.onClick.AddListener(() => ChangePage(prevPageIndexLate - 1));
        }
        if(nextButton){
            nextButton.onClick.AddListener(() => ChangePage(prevPageIndexLate + 1));
        }
    }

    public override void OnBeginDrag(PointerEventData eventData){
        base.OnBeginDrag(eventData);
    }

    public override void OnEndDrag(PointerEventData eventData){
        base.OnEndDrag(eventData);

        // 慣性を停止
        StopMovement();

        var pageIndex = -Mathf.RoundToInt(content.anchoredPosition.x / pageWidth);
        // ページが変わっていない且つ素早くドラッグした場合
        if(pageIndex == prevPageIndex && Mathf.Abs(eventData.delta.x) >= 5){
            pageIndex -= (int)Mathf.Sign(eventData.delta.x);
        }
        ChangePage(pageIndex);
    }
    private void ChangePage(int pageIndex){
        var maxPage = content.childCount - 1;
        pageIndex = Mathf.Clamp(pageIndex, 0, maxPage);
        var destX = -pageIndex * pageWidth;
        if(content.anchoredPosition.x != destX){
            this.SafeStopCoroutine(coroutine);
            var from = content.anchoredPosition.x;
            coroutine = this.CallLerpRealtime(duration * Mathf.Abs(from - destX) / pageWidth, t => {
                content.anchoredPosition = content.anchoredPosition.SetX(Mathf.Lerp(from, destX, t * t));
            }, () => {
                prevPageIndexLate = pageIndex;
                if(prevButton){
                    prevButton.gameObject.SetActive(pageIndex > 0);
                }
                if(nextButton){
                    nextButton.gameObject.SetActive(pageIndex < maxPage);
                }
                onPageChanged.Invoke(pageIndex);
            });
        }

        prevPageIndex = pageIndex;
    }
    public void ImmediateChangePage(int pageIndex){
        var maxPage = content.childCount - 1;
        pageIndex = Mathf.Clamp(pageIndex, 0, maxPage);
        var destX = -pageIndex * pageWidth;
        if(content.anchoredPosition.x != destX){
            this.SafeStopCoroutine(coroutine);
            content.anchoredPosition = content.anchoredPosition.SetX(destX);
        }

        if(prevButton){
            prevButton.gameObject.SetActive(pageIndex > 0);
        }
        if(nextButton){
            nextButton.gameObject.SetActive(pageIndex < maxPage);
        }

        prevPageIndex = pageIndex;
        prevPageIndexLate = pageIndex;
    }

    public void OnPointerDown(PointerEventData eventData){
        this.SafeStopCoroutine(coroutine);
    }
}