using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace XenseAR {

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public enum PanelSliderType
{
    SlideLeft,
    SlideRight,
}

public class UIPanelSliderItem : MonoBehaviour, IDragHandler, IEndDragHandler
{
    public PanelSliderType type = PanelSliderType.SlideLeft;

    [Header("Button Control")]
    [SerializeField] Button nextPanelButton;
    [SerializeField] Button prevPanelButton;
    [SerializeField] Button lastPanelButton;
    [SerializeField] Button firstPanelButton;
    [SerializeField] Button exitPanelButton;

    [Header("Drag Action Control")]
    public bool enableDrag;
    public bool dragBack = false;
    public float moveSpeed = 500f;

    public UIPanelSliderItem prevPanel;
    public UIPanelSliderItem nextPanel;

    private bool initComplete = false;
    private bool smoothing = false;

    Vector2 panelPosition;

    RectTransform rectTransform;

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    // Start is called before the first frame update
    void Start()
    {
        int index = transform.GetSiblingIndex();
        int panelCount = transform.parent.childCount;

        if (index > 0) nextPanel = transform.parent.GetChild(index - 1).GetComponent<UIPanelSliderItem>();
        if (index < panelCount - 1) prevPanel = transform.parent.GetChild(index + 1).GetComponent<UIPanelSliderItem>();

        rectTransform.sizeDelta = transform.parent.GetComponent<RectTransform>().rect.size;

        panelPosition = rectTransform.anchoredPosition;

        if (nextPanelButton != null)
            nextPanelButton.onClick.AddListener(MoveToNextPanel);
        if (prevPanelButton != null)
            prevPanelButton.onClick.AddListener(MoveToPrevPanel);
        if (firstPanelButton != null)
            firstPanelButton.onClick.AddListener(MoveToFirstPanel);
        if (lastPanelButton != null)
            lastPanelButton.onClick.AddListener(MoveToLastPanel);
        if (exitPanelButton != null)
            exitPanelButton.onClick.AddListener(DestroyPanel);

        initComplete = true;
    }

    private void OnDestroy()
    {
        smoothing = false;
        if (nextPanelButton != null)
            nextPanelButton.onClick.RemoveListener(MoveToNextPanel);
        if (prevPanelButton != null)
            prevPanelButton.onClick.RemoveListener(MoveToPrevPanel);
        if (firstPanelButton != null)
            firstPanelButton.onClick.RemoveListener(MoveToFirstPanel);
        if (lastPanelButton != null)
            lastPanelButton.onClick.RemoveListener(MoveToLastPanel);
        if (exitPanelButton != null)
            exitPanelButton.onClick.AddListener(DestroyPanel);
    }

    public void OnDrag(PointerEventData data)
    {
        Vector2 difference = data.position - data.pressPosition ;
        float diffX = rectTransform.sizeDelta.x * difference.x / Screen.width;
        OnDrag(new Vector2(diffX, 0f));
    }

    public void OnDrag(Vector2 dragData)
    {
        smoothing = false;
        float diffX = dragData.x;

        switch (type)
        {
            case PanelSliderType.SlideLeft:
                if (diffX + panelPosition.x > 0f && prevPanel != null && dragBack)
                {
                    prevPanel.OnDrag(new Vector2(diffX + panelPosition.x, 0f));
                }
                diffX = Mathf.Clamp(diffX + panelPosition.x, -rectTransform.sizeDelta.x, 0f);
                break;
            case PanelSliderType.SlideRight:
                if (diffX + panelPosition.x < 0f && prevPanel != null && dragBack)
                {
                    prevPanel.OnDrag(new Vector2(diffX + panelPosition.x, 0f));
                }
                diffX = Mathf.Clamp(diffX + panelPosition.x, 0f, rectTransform.sizeDelta.x);
                break;
            default:
                break;
        }
        if (enableDrag)
            rectTransform.anchoredPosition = new Vector2(diffX, rectTransform.anchoredPosition.y);
    }

    public void OnEndDrag(PointerEventData data)
    {
        Vector2 difference = data.position - data.pressPosition;
        float diffX = rectTransform.sizeDelta.x * difference.x / Screen.width;
        OnEndDrag(new Vector2(diffX, 0f));
    }

    public void OnEndDrag(Vector2 endDragData)
    {
        panelPosition = rectTransform.anchoredPosition;
        smoothing = false;
        SmoothClamp();
        if (prevPanel != null && dragBack)
        {
            switch (type)
            {
                case PanelSliderType.SlideLeft:
                    if (endDragData.x + panelPosition.x > 0f)
                    {
                        prevPanel.OnEndDrag(new Vector2(endDragData.x + panelPosition.x, 0f));
                    }
                    break;
                case PanelSliderType.SlideRight:
                    if (endDragData.x + panelPosition.x < 0f)
                    {
                        prevPanel.OnEndDrag(new Vector2(endDragData.x + panelPosition.x, 0f));
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public void MoveToNextPanel()
    {
        MoveToNextPanel(false);
    }

    public async void MoveToNextPanel(bool skipAnimation = false)
    {
        while (!initComplete)
            await Task.Yield();
        if (nextPanel == null) return;
        Hide(skipAnimation);
    }

    public void MoveToPrevPanel()
    {
        MoveToPrevPanel(false);
    }

    public async void MoveToPrevPanel(bool skipAnimation = false)
    {
        while (!initComplete)
            await Task.Yield();
        if (prevPanel == null) return;
        prevPanel.Show(skipAnimation);
    }

    public void MoveToFirstPanel()
    {
        MoveToFirstPanel(false);
    }

    public async void MoveToFirstPanel(bool skipAnimation = false)
    {
        while (!initComplete)
            await Task.Yield();
        if (prevPanel != null)
        {
            Hide(skipAnimation);
            prevPanel.MoveToFirstPanel(skipAnimation);
        }
        else
        {
            Show(skipAnimation);
        }
    }

    public void MoveToLastPanel()
    {
        MoveToLastPanel(false);
    }

    public async void MoveToLastPanel(bool skipAnimation = false)
    {
        while (!initComplete)
            await Task.Yield();
        if (nextPanel != null)
        {
            Hide(skipAnimation);
            nextPanel.MoveToLastPanel(skipAnimation);
        }
        else
        {
            Show(skipAnimation);
        }
    }

    public void DestroyPanel()
    {
        Destroy(transform.parent.gameObject);
    }

    public void Show(bool skipAnimation = false)
    {
        smoothing = false;
        Vector2 targetPosition = new Vector2(0f, rectTransform.anchoredPosition.y);
        if (skipAnimation)
        {
            rectTransform.anchoredPosition = targetPosition;
            panelPosition = rectTransform.anchoredPosition;
        }
        else
        {
            SmoothClamp(targetPosition);
        }
    }

    public void Hide(bool skipAnimation = false)
    {
        smoothing = false;
        Vector2 targetPosition = new Vector2();
        switch (type)
        {
            case PanelSliderType.SlideLeft:
                targetPosition = new Vector2(-rectTransform.sizeDelta.x, rectTransform.anchoredPosition.y);
                break;
            case PanelSliderType.SlideRight:
                targetPosition = new Vector2(rectTransform.sizeDelta.x, rectTransform.anchoredPosition.y);
                break;
        }
        if (skipAnimation)
        {
            rectTransform.anchoredPosition = targetPosition;
            panelPosition = rectTransform.anchoredPosition;
        }
        else
        {
            SmoothClamp(targetPosition);
        }
    }

    async Task SmoothClamp(Vector2? targetPosition = null)
    {
        smoothing = true;
        Vector2 originPosition = rectTransform.anchoredPosition;

        if (!targetPosition.HasValue)
        {
            float slideMaxDistance = rectTransform.sizeDelta.x;
            if (Mathf.Abs(originPosition.x) / slideMaxDistance > 0.5f)
            {
                switch (type)
                {
                    case PanelSliderType.SlideLeft:
                        targetPosition = new Vector2(-rectTransform.sizeDelta.x, rectTransform.anchoredPosition.y);
                        break;
                    case PanelSliderType.SlideRight:
                        targetPosition = new Vector2(rectTransform.sizeDelta.x, rectTransform.anchoredPosition.y);
                        break;
                }
            }
            else targetPosition = new Vector2(0f, rectTransform.anchoredPosition.y);
        }

        Vector2 direction = (targetPosition.Value - originPosition).normalized;

        while (direction.x * (targetPosition.Value - rectTransform.anchoredPosition).normalized.x > 0f)
        {
            if (!smoothing) return;
            await Task.Yield();
            rectTransform.anchoredPosition += direction * Time.deltaTime * moveSpeed;
            panelPosition = rectTransform.anchoredPosition;
        }
        rectTransform.anchoredPosition = targetPosition.Value;
        panelPosition = rectTransform.anchoredPosition;
    }


    public async Task<UIPanelSliderItem> FirstPanel()
    {
        while (!initComplete)
            await Task.Yield();
        if (prevPanel == null) return this;
        else return await prevPanel.FirstPanel();
    }

    public async Task<UIPanelSliderItem> LastPanel()
    {
        while (!initComplete)
            await Task.Yield();
        if (nextPanel == null) return this;
        else return await nextPanel.LastPanel();
    }
}
}
