using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Camera))]

public class MouseOrbit : MonoBehaviour {
    public Transform target;
    public ThirdPersonController controller;
    public float distance = 1.0f;
    public Vector3 offset = Vector3.zero;
    public Vector3 fightingOffset = new Vector3(0.4f, 0.0f, -0.1f);
    [HideInInspector]
    public Quaternion rotation;
    [Header("Parameters")]
    public bool isControllable = true;
    private bool wasControllable;
    public bool firstView;
    [HideInInspector]
    public float returningFactor = 1.0f;
    private Vector3 lastPosition;
    private Quaternion lastRotation;
    [HideInInspector]
    public float xSpeed;
    [HideInInspector]
    public float ySpeed;
    private bool bloomEnabled = true;

    private Vector2 input;
    private float x = 0.0f;
    private float y = 0.0f;
    private Camera cam;
    private Vector3 localPos;
    private float cameraCollisionTime = 1.0f;
    private Transform startTarget;
    private float swimmingHeight = 0.0f;
    private bool wasUsingHDev = false;
    private static float quakeAmount = 0.0f;
    private float oscill = 0.0f;

    // Start is called before the first frame update
    void Awake() {
        transform.parent = null;
        xSpeed = PlayerPrefs.GetFloat("MouseSpeedX", 250);
        ySpeed = PlayerPrefs.GetFloat("MouseSpeedY", 120);
        RecalculateXY(transform.eulerAngles);
        cam = GetComponent<Camera>();
        localPos = transform.position - target.position;
        wasControllable = isControllable;
        if (target != null) {
            startTarget = target;
        }
        quakeAmount = 0.0f;
    }

    public void ReceiveInput(Vector2 i) {
        if (isControllable && !PauseMenu.paused) {
            input = i;
        }
        else {
            input = Vector2.zero;
        }
    }

    // Update is called once per frame
    void LateUpdate() {
        //InputSystem.Update();
#if UNITY_EDITOR
        if (Input.GetKeyDown(KeyCode.Backslash)) {
            isControllable = !isControllable;
        }
#endif
        if (target == null || PauseMenu.paused) {
            return;
        }
        x += ySpeed * -1f * input.y * Time.unscaledDeltaTime;
        if (x < -30.0f) {
            x = -30.0f;
        }
        else if (x > 60.0f) {
            x = 60.0f;
        }
        y += xSpeed * input.x * Time.unscaledDeltaTime;
        rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(x, y, 0.0f), Time.unscaledDeltaTime * 24.0f);
        if (returningFactor < 1.0) {
            //transform.rotation = Quaternion.Slerp(lastRotation, Quaternion.Euler(x, y, 0.0f), returningFactor);
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(x, y, 0.0f), Time.unscaledDeltaTime * 32.0f * returningFactor * returningFactor * returningFactor);
        }
        else {
            transform.rotation = rotation;
        }

        cameraCollisionTime += Time.unscaledDeltaTime * 3.0f;
        Vector3 position;
        float jumpHeight = 0.0f;
        bool dofEnabled = false;
        float speed = 0.0f;
        if (controller != null) {
            if (controller.groundedY < controller.transform.position.y && controller.entity != null && !controller.entity.swimming) {
                jumpHeight = (controller.groundedY - controller.transform.position.y)/2.0f;
            }
            speed = controller.moveSpeed;
            if (controller.entity != null && controller.entity.swimming) {
                swimmingHeight = Mathf.Lerp(swimmingHeight, 0.2f, Time.deltaTime * 3.0f);
                speed = speed * -1;
            }
            else {
                swimmingHeight = Mathf.Lerp(swimmingHeight, 0.0f, Time.deltaTime * 3.0f);
            }
        }
        if (wasUsingHDev) {
            returningFactor = 0.6f;
        }
        if (speed >= 2.0f) {
            oscill += Time.deltaTime * 8.0f;
        }
        else {
            oscill += Time.deltaTime * 4.0f * speed;
        }
        position = target.position + transform.TransformDirection(offset) + new Vector3(0.0f, jumpHeight + swimmingHeight, 0.0f) + (rotation * new Vector3(Mathf.Cos(oscill) * speed * 0.015f, Mathf.Cos(oscill * 2.0f) * speed * 0.015f, distance * -1));
        float smoothSpeed = 1.0f + cameraCollisionTime;
        if (controller != null) {
            smoothSpeed += speed;
            if (controller.entity != null && controller.entity.fighting) {
                smoothSpeed = 1.0f;
                position = target.position + transform.TransformDirection(fightingOffset) + (rotation * new Vector3(0.0f, 0.0f, distance * -1));
            }
        }
        Vector3 margin = Quaternion.LookRotation(position - target.position) * new Vector3(0.0f, 0.0f, -0.14f);
        Vector3 margin2 = new Vector3(margin.z, margin.y, margin.x);
        RaycastHit hit;
        int mask = ~(641 << 9);
        if (Physics.Linecast(target.position, position + margin, out hit, mask) || Physics.Linecast(target.position, position + margin2, out hit, mask) || Physics.Linecast(target.position, position - margin2, out hit, mask)) {
            Vector3 point = hit.point + margin;
            position = point;
            cameraCollisionTime = 0.0f;
        }
        localPos = Vector3.Lerp(transform.position - target.position, position - target.position, Time.unscaledDeltaTime * 6.0f * smoothSpeed);
        if (returningFactor < 1.0) {
            //transform.position = Vector3.Slerp(lastPosition, position, returningFactor);
            transform.position = Vector3.Slerp(transform.position, position, Time.unscaledDeltaTime * 32.0f * returningFactor * returningFactor * returningFactor);
            returningFactor = returningFactor + ((((1.0f - returningFactor) * 3.0f) + 0.1f) / 3.0f) * Time.unscaledDeltaTime;
        }
        else {
            transform.position = localPos + target.position;
        }
        if (quakeAmount > 0) {
            quakeAmount -= Time.deltaTime;
        }
        else if (quakeAmount < 0) {
            quakeAmount = 0;
        }
        transform.position += QuakeVector();

        if (controller != null) {
            float targetFov = 60.0f;
            if (controller.moveSpeed > 2.0f) {
                targetFov = targetFov + ((controller.moveSpeed - 2.0f) * 5.0f);
            }
            if (returningFactor > 0.9f) {
                targetFov += 12.0f;
                dofEnabled = true;
            }
            cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFov, Time.unscaledDeltaTime * 5.0f);
        }
        if ((isControllable || Application.isFocused) && !PauseMenu.paused) {
            Cursor.lockState = CursorLockMode.Locked;
        }
        else {
            Cursor.lockState = CursorLockMode.None;
        }
        wasControllable = isControllable;
    }

    public void ForceRotation() {
        rotation = Quaternion.Euler(x, y, 0.0f);
        transform.rotation = rotation;
    }

    public void ForceRotation(Quaternion nr) {
        rotation = Quaternion.Euler(nr.eulerAngles.x, nr.eulerAngles.y, 0.0f);
        transform.rotation = rotation;
    }

    public void ForceRotation(float nx, float ny) {
        x = nx;
        y = ny;
        ForceRotation();
    }

    public Vector2 GetRotation() {
        return new Vector2(x, y);
    }

    public void OnDisable() {
        //Cursor.lockState = CursorLockMode.None;
        UpdateLastPosition();
    }

    public void OnEnable() {
        UpdateLastPosition();
    }

    public void UpdateLastPosition() {
        lastPosition = transform.position;
        lastRotation = transform.rotation;
    }

    public void RecalculateXY(Vector3 eulerAngles) {
        //Debug.Log("Prima: X = " + x + ", Y = " + y);
        x = eulerAngles.x;
        while (x > 180.0f) {
            x -= 360.0f;
        }
        while (x < -180.0f) {
            x += 360.0f;
        }
        y = eulerAngles.y;
        while (y > 180.0f) {
            y -= 360.0f;
        }
        while (y < -180.0f) {
            y += 360.0f;
        }
        //Debug.Log("Dopo: X = " + x + ", Y = " + y);
    }

    public void RotateTowards(Vector3 pos) {
        if (target != null) {
            Quaternion rot = Quaternion.LookRotation(pos - (target.position + transform.TransformDirection(fightingOffset)));
            rotation = Quaternion.Lerp(rotation, rot, Time.unscaledDeltaTime * 9.0f);
            transform.rotation = rotation;
            RecalculateXY(rotation.eulerAngles);
        }
        else {
            Quaternion rot = Quaternion.LookRotation(pos - transform.position);
            rotation = Quaternion.Lerp(rotation, rot, Time.unscaledDeltaTime * 9.0f);
            transform.rotation = rotation;
            RecalculateXY(rotation.eulerAngles);
        }
    }

    public void UpdateCharacter() {

    }

    public void UpdateCameraSensitivity(float sx, float sy) {
        xSpeed = sx;
        ySpeed = sy;
    }

    public static void Quake(float amount) {
        if (amount > quakeAmount) {
            quakeAmount = amount;
        }
    }

    public static void StopQuake() {
        quakeAmount = 0.0f;
    }

    public static Vector3 QuakeVector() {
        return Random.insideUnitSphere * quakeAmount;
    }
}