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

public class ButtonCollider : MonoBehaviour {
    private const float SCREEN_RAY_LENGTH = 50f;

    [SerializeField]
    private Camera controlCamera = null;
    [SerializeField]
    private UnityEvent onClickEvent = null;
    [SerializeField]
    private Collider buttonCollider;

    private int touchId = -1;
    private bool mouseClick = false;
    private Vector2 clickPosition = Vector2.zero;
    private const float pixelsError = 10f;

    public void Init(UnityEvent clickEvent, Collider collider = null, Camera camera = null) {
        onClickEvent = clickEvent;
        buttonCollider = collider ?? GetCollider();
        controlCamera = camera ?? GetDefaultCamera();
    }

    private void Start () {
        if (buttonCollider == null) {
            buttonCollider = GetCollider();
        }
        if (controlCamera == null) {
            controlCamera = GetDefaultCamera();
        }
    }

    private void OnColliderClick() {
        if (onClickEvent != null) {
            onClickEvent.Invoke();
        }
    }

    private Collider GetCollider() {
        var collider = GetComponentInChildren<Collider>();
        if (collider == null) {
            collider = CreateDefaultCollider();
        }
        return collider;
    }

    private Collider CreateDefaultCollider() {
        var collider = gameObject.AddComponent<BoxCollider>();
        return collider;
    }

    private Camera GetDefaultCamera() {
        return FindObjectOfType<Camera>();
    }

    private void Update() {
        bool isHoverOnInterface = false;
        if (EventSystem.current != null) {
            isHoverOnInterface = EventSystem.current.IsPointerOverGameObject();
        }
        if (buttonCollider == null) {
            return;
        }
        if (controlCamera == null) {
            return;
        }
        // Touch input
        for (int i = 0; i < Input.touches.Length; i++) {
            if (Input.touches[i].phase == TouchPhase.Began && !isHoverOnInterface) {
                RaycastHit info;
                if (buttonCollider.Raycast(controlCamera.ScreenPointToRay(Input.touches[i].position), out info, SCREEN_RAY_LENGTH)) {
                    touchId = Input.touches[i].fingerId;
                    clickPosition = Input.touches[i].position;
                    break;
                }
            }
            if (Input.touches[i].phase == TouchPhase.Ended || Input.touches[i].phase == TouchPhase.Canceled) {
                RaycastHit info;
                if (buttonCollider.Raycast(controlCamera.ScreenPointToRay(Input.touches[i].position), out info, SCREEN_RAY_LENGTH)) {
                    if (touchId == Input.touches[i].fingerId && ((clickPosition - Input.touches[i].position).magnitude < pixelsError)) {
                        OnColliderClick();
                    }
                    touchId = -1;
                    break;
                }
            }
        }
        // Mouse input
        if (Input.GetMouseButtonDown(0) && !isHoverOnInterface) {
            RaycastHit info;
            if (buttonCollider.Raycast(controlCamera.ScreenPointToRay(Input.mousePosition), out info, SCREEN_RAY_LENGTH)) {
                mouseClick = true;
                clickPosition = Input.mousePosition;
            }
        }
        if (Input.GetMouseButtonUp(0) && mouseClick && ((clickPosition - (Vector2)Input.mousePosition).magnitude < pixelsError)) {
            RaycastHit info;
            if (buttonCollider.Raycast(controlCamera.ScreenPointToRay(Input.mousePosition), out info, SCREEN_RAY_LENGTH)) {
                OnColliderClick();
            }
        }
    }

#if UNITY_EDITOR
    private void Reset()
    {
        buttonCollider = GetComponent<Collider>();
    }
#endif
}
