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

[RequireComponent(typeof(CanvasGroup))]
public class UIElementDragger : MonoBehaviour, IDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler
{
    public bool LockSize;
    [ShowIf("LockSize", false)]
    public Vector2 
        SizeMin = new Vector2(100, 100),
        SizeMax = new Vector2(1920, 1080);

    //Prevent the element for being attached or detached from a toolbar
    public bool LockProxyState;
    public string ProxyName;
    public UIDraggableProxy Proxy;
    public UIToolbar.ElementType ElementType;
    public GameObject HandlePosition, HandleSize;

    private Vector2 mouseOffset;
    private float currentScale = 1;
    private float sizeWhenClicked;
    private Vector2 savedMousePos;
    private bool _awake;
    private CanvasGroup _canvasGroup;

    public DragAction LastDragAction;
    
    public enum DragAction
    {
        None,
        Position,
        Scale,
        Size
    }

    private void Awake()
    {
        if (_awake) return;

        _awake = true;
        _canvasGroup = GetComponent<CanvasGroup>();

        if (LockSize)
        {
            HandleSize.SetActive(false);
        }
    }

    private void Start()
    {
        if (Proxy == null)
        {
            transform.position = RecalculatePosition(transform.position);
        }
    }

    public void SetProxyMode(UIDraggableProxy proxy)
    {
        Awake();
        OnEndDrag(null);
        Proxy = proxy;
        if (proxy != null)
        {
            transform.position = proxy.GetComponent<RectTransform>().position;
            HandlePosition.gameObject.SetActive(false);
        }
        else
        {
            gameObject.SetActive(true);
            HandlePosition.gameObject.SetActive(true);
        }
    }

    public void ProxySetVisible(bool visible)
    {
        if (!visible)
        {
            gameObject.SetActive(false);
        }
        else
        {
            var canvas = ModelViewerInterface.GetInstance().MainCanvas;
            var proxyRect = Proxy.transform as RectTransform;
            var handleRect = HandlePosition.transform as RectTransform;
            var handleOffset = handleRect.sizeDelta.y * canvas.scaleFactor * Vector3.up;
            if (Proxy.GetComponentInParent<UIToolbar>().Layout == UIToolbar.LayoutMode.Vertical)
            {
                //anchor to top-right of button
                transform.position = proxyRect.position + proxyRect.sizeDelta.x * canvas.scaleFactor * Vector3.right + handleOffset;
            }
            else
            {
                //anchor to bottom-left of button
                transform.position = proxyRect.position + proxyRect.sizeDelta.y * canvas.scaleFactor * Vector3.up - handleOffset;
            }
            gameObject.SetActive(true);
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        var rectTsf = transform as RectTransform;
        var canvas = ModelViewerInterface.GetInstance().MainCanvas;

        switch (LastDragAction)
        {
            case DragAction.Scale:
                {
                    currentScale = Mathf.Clamp((Input.mousePosition.x - savedMousePos.x) / Screen.width + sizeWhenClicked, 0.5f, 1);
                    transform.localScale = Vector3.one * currentScale;
                    break;
                }
            case DragAction.Size:
                {
                    var mousePos = (Vector2)Input.mousePosition;
                    mousePos.y = -(Screen.height - mousePos.y); //convert anchor from bottom-left to top-left
                    var sizeDelta = mousePos / canvas.scaleFactor - (Vector2)rectTsf.anchoredPosition;
                    sizeDelta.x = Mathf.Clamp(sizeDelta.x, SizeMin.x, Mathf.Min(Screen.width / canvas.scaleFactor, SizeMax.x));
                    sizeDelta.y = Mathf.Clamp(-sizeDelta.y, SizeMin.y, Mathf.Min(Screen.height / canvas.scaleFactor, SizeMax.y));
                    if (currentScale > 0)
                    {
                        sizeDelta *= currentScale;
                    }
                    rectTsf.sizeDelta = sizeDelta;
                    break;
                }
            case DragAction.Position:
                {
                    transform.position = RecalculatePosition((Vector2)Input.mousePosition + mouseOffset);
                    break;
                }
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        _canvasGroup.blocksRaycasts = true;
    }

    private void OnDestroy()
    {
        if(Proxy != null)
        {
            Destroy(Proxy.gameObject);
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        eventData.pointerDrag = this.gameObject;
        _canvasGroup.blocksRaycasts = false;
        if (eventData.button == PointerEventData.InputButton.Right)
        {
            LastDragAction = DragAction.Scale;
            ModelViewerMain.GetInstance().GetCamera().LightLock = true;
            savedMousePos = Input.mousePosition;
            sizeWhenClicked = currentScale;
            transform.SetAsLastSibling();
        }
        else if (eventData.hovered.Contains(HandlePosition))
        {
            LastDragAction = DragAction.Position;
            mouseOffset = transform.position - Input.mousePosition;
            transform.SetAsLastSibling();
        }
        else if (eventData.hovered.Contains(HandleSize))
        {
            LastDragAction = DragAction.Size;
            mouseOffset = transform.position - Input.mousePosition;
        }
        else
        {
            LastDragAction = DragAction.None;
            _canvasGroup.blocksRaycasts = true;
            eventData.pointerDrag = null;
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        OnEndDrag(null);
    }

    public Vector2 RecalculatePosition(Vector2 pos)
    {
        var handle = HandlePosition.transform as RectTransform;
        var tsf = transform as RectTransform;
        var canvas = ModelViewerInterface.GetInstance().MainCanvas;

        var xPos = Mathf.Clamp(pos.x, 0, Screen.width - tsf.sizeDelta.x * canvas.scaleFactor);
        var yPos = Mathf.Clamp(pos.y, handle.sizeDelta.y * canvas.scaleFactor, Screen.height);

        return new Vector2 (xPos, yPos);
    }
}
