using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// 
///     [RequireComponent(typeof(Camera))]
public class AroundCamera : MonoBehaviour
{
    #region Field and Property
    /// 
    /// Around center.
    /// 
    public Transform target;
    /// 
    /// Settings of mouse button, pointer and scrollwheel.
    /// 
    public MouseSettings mouseSettings = new MouseSettings(1, 10, 10);
    /// 
    /// Range limit of angle.
    /// 
    public Range angleRange = new Range(-90, 90);
    /// 
    /// Range limit of distance.
    /// 
    public Range distanceRange = new Range(1, 10);
    /// 
    /// Damper for move and rotate.
    /// 
    [Range(0, 10)]
    public float damper = 5;
    /// 
    /// Camera current angls.
    /// 
    public Vector2 CurrentAngles { set; get; }
    /// 
    /// Current distance from camera to target.
    /// 
    public float CurrentDistance { set; get; }
    /// 
    /// Camera target angls.
    /// 
    public Vector2 targetAngles;
    /// 
    /// Target distance from camera to target.
    /// 
    public float targetDistance;
    public int mLayer;
    public bool mIsMovableDetection = true;
    public bool mRotateEnable = true;
    public static bool mIsTouchedUI = false;
    #endregion
    #region Protected Method
    protected virtual void Start()
    {
        Initialize();
    }
    private void Update()
    {
        if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1))
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                mIsTouchedUI = true;
            }
        }
        if(Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1))
        {
            mIsTouchedUI = false;
        }
    }
    public static bool IsTouchedUI()
    {
        return mIsTouchedUI;
    }
    public float mCameraDistance = 0.0f;
    protected virtual void LateUpdate()
    {
        Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, float.MaxValue))
        {
            float cameraDistance = Vector3.Distance(Camera.main.transform.position, hit.point);
            mouseSettings.wheelSensitivity = cameraDistance / 4;
            mCameraDistance = cameraDistance;
        }
        AroundByMouse();
    }
    /// 
    /// Initialize component.
    /// 
    public virtual void Initialize()
    {
        CurrentAngles = targetAngles = transform.eulerAngles;
        CurrentDistance = targetDistance = Vector3.Distance(transform.position, target.position);
    }
    /// 
    /// Camera around target by mouse.
    /// 
    public void AroundByMouse()
    {
        if (IsTouchedUI()) return;
        float preDistance = targetDistance;
        Vector2 preAngle = targetAngles;
        bool mouseOperate = false;
#if !UNITY_ANDROID
        if (Input.GetMouseButton(mouseSettings.mouseButtonID) && !IsTouchedUI())
        {
            //Mouse pointer.
            targetAngles.y += Input.GetAxis("Mouse X") * mouseSettings.pointerSensitivity;
            targetAngles.x -= Input.GetAxis("Mouse Y") * mouseSettings.pointerSensitivity;
            //Range.
            targetAngles.x = Mathf.Clamp(targetAngles.x, angleRange.min, angleRange.max);
            mouseOperate = true;
        }
        if (Input.GetAxis("Mouse ScrollWheel") != 0f)
        {
            //Mouse scrollwheel.
            targetDistance -= Input.GetAxis("Mouse ScrollWheel") * mouseSettings.wheelSensitivity;
            targetDistance = Mathf.Clamp(targetDistance, distanceRange.min, distanceRange.max);
            mouseOperate = true;
        }
#else
        if (Input.touchCount == 2)
        {
            Touch touch0 = Input.GetTouch(0);
            Touch touch1 = Input.GetTouch(1);
            if (touch0.phase == TouchPhase.Moved && touch1.phase == TouchPhase.Moved)
            {
                Vector2 lastDelta = (touch0.position - touch0.deltaPosition) - (touch1.position - touch1.deltaPosition);
                Vector2 currDelta = touch0.position - touch1.position;
                float deltaMagnitude = currDelta.magnitude - lastDelta.magnitude;
                targetDistance -= deltaMagnitude * mouseSettings.wheelSensitivity * 0.02f;
                targetDistance = Mathf.Clamp(targetDistance, distanceRange.min, distanceRange.max);
                CurrentDistance = targetDistance;
                mouseOperate = true;
            }
        }
        if (Input.touchCount == 1 && mRotateEnable)
        {
            //Mouse pointer.
            targetAngles.y += Input.GetAxis("Mouse X") * mouseSettings.pointerSensitivity * 0.2f; ;
            targetAngles.x -= Input.GetAxis("Mouse Y") * mouseSettings.pointerSensitivity * 0.2f; ;
            //Range.
            targetAngles.x = Mathf.Clamp(targetAngles.x, angleRange.min, angleRange.max);
            CurrentAngles = targetAngles;
            mouseOperate = true;
        }
#endif
        if (mouseOperate && !MovableDetection())
        {
            targetDistance = preDistance;
            targetAngles = preAngle;
            return;
        }
#if !UNITY_ANDROID
        //Lerp.
        CurrentAngles = Vector2.Lerp(CurrentAngles, targetAngles, damper * Time.deltaTime);
        CurrentDistance = Mathf.Lerp(CurrentDistance, targetDistance, damper * Time.deltaTime);
        //Not Lerp
        //CurrentAngles = targetAngles;
        //CurrentDistance = targetDistance;
#endif
        //Update transform position and rotation.
        transform.rotation = Quaternion.Euler(CurrentAngles);
        transform.position = target.position - transform.forward * CurrentDistance;
#region
#if !UNITY_ANDROID
        if (Vector2.Distance(CurrentAngles, targetAngles) > 1.0f)
            PlayerPrefs.SetInt("CameraRotating", 1);
        else
            PlayerPrefs.SetInt("CameraRotating", 0);
        if (Mathf.Abs(CurrentDistance - targetDistance) > 1.0f)
            PlayerPrefs.SetInt("CameraZooming", 1);
        else
            PlayerPrefs.SetInt("CameraZooming", 0);
#endif
#endregion
    }
    public bool MovableDetection()
    {
        if (!mIsMovableDetection) return true;
        Quaternion preRot = transform.rotation;
        Vector3 prePos = transform.position;
        transform.rotation = Quaternion.Euler(targetAngles);
        transform.position = target.position - transform.forward * targetDistance;
        if (Physics.Linecast(prePos, transform.position))
        {
            transform.rotation = preRot;
            transform.position = prePos;
            return false;
        }
        return true;
    }
#endregion
}