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

// Require a character controller to be attached to the same game object
[RequireComponent(typeof(Animator))]

public class ThirdPersonController : MovementController
{
    public static ThirdPersonController main;
    [HideInInspector]
    public bool firstPerson;
    [HideInInspector]
    public Vector3 moveDirection;
    [HideInInspector]
    private bool wasControllable = true;
    public AudioClip[] soundFootstep;
    public AudioClip waterSwimmingSound;
    public AudioClip jumpSound;
    [HideInInspector]
    public float moveSpeed = 0.0f;

    private float jumpTime;
    private float groundedTime = -1.0f;
    private float landingTime;
    [HideInInspector]
    public float groundedY = 0.0f;
    private float verticalSpeed = 0.0f;
    private bool crouching = false;
    private float speedMultiplier;
    private Animator anim;
    private CharacterController characterController;
    private new Rigidbody rigidbody;
    private float time2 = 1.0f;
    private int lastFootstep = -1;
    private Vector2 smoothInput = Vector2.zero;
    [HideInInspector]
    public WaterTrigger[] waterSources;
    private float swimHeight;
    private Vector3 collisionDirection;
    private Vector3 collisionNormal;
    private Vector3 slideDirection = Vector3.zero;
    public float speedBoost = 1.0f;
    private GameInputManager gameInputManager;
    private float currentTime = 0.0f;

    // The last collision flags returned from controller.Move
    private CollisionFlags collisionFlags;

    //Constants
    private float gravity = 9.8f;

    public void Awake() {
        if (enabled) {
            main = this;
        }
        verticalSpeed = 0.0f;
        groundedY = 0.0f;
        ScriptedScene.CheckForCanvas();
        ScriptedScene.CheckForGlobalVariables();
    }

    // Start is called before the first frame update
    void Start() {
        anim = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
        rigidbody = GetComponent<Rigidbody>();
        entity = GetComponent<Entity>();
        waterSources = GameObject.FindObjectsOfType<WaterTrigger>();
        if (GameInputManager.main != null) {
            gameInputManager = GameInputManager.main;
            wasControllable = gameInputManager.isControllable;
        }
    }

    // Update is called once per frame
    void Update() {
        time2 += Time.deltaTime;
        currentTime += Time.deltaTime;
        if (!wasControllable && gameInputManager.isControllable) {
            moveDirection = transform.forward;
            if (GameInputManager.skipping) {
                GameInputManager.skipping = false;
            }
            if (rigidbody == null && !characterController.enabled) {
                characterController.enabled = true;
            }
        }
        if (gameInputManager.isControllable && gameInputManager == null) {
            if (GameInputManager.main != null) {
                gameInputManager = GameInputManager.main;
            }
            else {
                gameInputManager.isControllable = false;
            }
        }
        UpdateSmoothedMovementDirection();

        //Speed calculation
        speedMultiplier = 2.0f;
        if (gameInputManager.running && !crouching && (entity == null || !entity.swimming)) {
            speedMultiplier = 3.5f;
        }
        else if (gameInputManager.walking && !crouching) {
            speedMultiplier = 1.0f;
        }
        //Speed is smooth
        if (gameInputManager.isControllable && (entity == null || !entity.forceShowSword)) {
            float smoothFactor = 1.5f;
            if (moveSpeed < 2.0f) {
                smoothFactor = 3.0f;
            }
            float slopeMultiplier = 1.0f;
            if (IsGrounded() && collisionDirection.y < 0.0f) {
                if (moveSpeed < 2.0f) {
                    slopeMultiplier = Mathf.Lerp(1.0f, 0.0f, collisionDirection.y / -1.4f);
                }
                else {
                    slopeMultiplier = Mathf.Lerp(1.0f, 0.0f, collisionDirection.y / -0.9f);
                }
            }
            if (currentTime - groundedTime < 0.15f) {
                moveSpeed = Mathf.Lerp(moveSpeed, gameInputManager.input.magnitude * speedMultiplier * slopeMultiplier, Time.deltaTime * 2.0f * smoothFactor);
            }
        }
        else {
            moveSpeed = Mathf.Lerp(moveSpeed, 0.0f, Time.deltaTime * 6.0f);
        }
        if (entity==null || !entity.fighting) {
            anim.SetFloat("Speed", moveSpeed);
            if (gameInputManager.isControllable && !PauseMenu.paused && (entity == null || !entity.forceShowSword) && entity.parameters.currentHP > 0) {
                Time.timeScale = Mathf.Lerp(Time.timeScale, 1.0f, Time.unscaledDeltaTime * 9.0f);
            }
        }
        anim.SetBool("Falling", currentTime - groundedTime > 0.15f);

        //Swimming
        if (entity != null){
            entity.swimming = false;
            for (int i = 0; i < waterSources.Length; i++) {
                if (waterSources[i].Inside(transform.position + new Vector3(0.0f, entity.swimHeight, 0.0f))) {
                    entity.swimming = true;
                    swimHeight = waterSources[i].height - entity.swimHeight - 0.075f;
                    break;
                }
            }
            anim.SetBool("Swimming", entity.swimming);
        }

        //Crouch
        AutomaticCrouch(anim);

        Vector3 movement = Vector3.zero;
        //Additional movement (sum to the animation's movement)
        if (IsGrounded() && Time.time-jumpTime>0.15f) {
            if (gameInputManager.jumping) {
                landingTime = Time.time;
                PlayFootstep(true);
                PlayFootstep(true);
                //Vibration.SetVibration((verticalSpeed * -0.1f)+0.1f);
            }
            verticalSpeed = 0.0f;
            gameInputManager.jumping = false;
        }
        else if (entity == null || !entity.swimming) {
            verticalSpeed -= gravity * Time.deltaTime;
        }
        if (verticalSpeed < -30.0f) {
            verticalSpeed = -30.0f;
        }
        if (entity != null && entity.swimming) {
            verticalSpeed = Mathf.Lerp(verticalSpeed, 0.0f, Time.deltaTime);
            slideDirection = Vector3.zero;
        }
        movement += HorizontalMovement() * speedBoost;
        movement += VerticalMovement();
        if (characterController.enabled) {
            collisionFlags = characterController.Move(movement * Time.deltaTime);
            if (IsGrounded()) {
                groundedTime = currentTime;
                groundedY = transform.position.y;
            }
        }
        if (entity != null && entity.swimming && transform.position.y < swimHeight) {
            transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x, swimHeight, transform.position.z), Time.deltaTime * 12.0f);
        }
        wasControllable = gameInputManager.isControllable;

        //Debug
#if UNITY_EDITOR
        if (Input.GetKeyDown(KeyCode.P)) {
            speedMultiplier = 15.0f;
        }
#endif
    }

    public void LateUpdate() {
    }

    void UpdateSmoothedMovementDirection() {
        Transform cameraTransform = Camera.main.transform;
        bool grounded = IsGrounded();

        // Forward vector relative to the camera along the x-z plane	
        Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
        forward = new Vector3(forward.x, 0.0f, forward.z);
        forward = forward.normalized;

        // Right vector relative to the camera
        // Always orthogonal to the forward vector
        Vector3 right = new Vector3(forward.z, 0, -forward.x);
        Vector3 actualMoveDirection = Vector3.zero;

        // Target direction relative to the camera
        if (gameInputManager.input.magnitude > 0.1f && gameInputManager.isControllable) {
            float xFactor = 1.0f;
            actualMoveDirection = (gameInputManager.input.x * xFactor * right) + (gameInputManager.input.y * forward);

            if (currentTime - groundedTime < 0.15f) {
                moveDirection = actualMoveDirection;
            }
            else {
                moveDirection = Vector3.Lerp(moveDirection, actualMoveDirection, Time.deltaTime * 3.0f);
            }
        }


        // Grounded controls
        if (actualMoveDirection != Vector3.zero && gameInputManager.isControllable && (entity==null || !entity.fighting) && (entity == null || !entity.forceShowSword)) {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(actualMoveDirection), 10.0f * Time.deltaTime);
        }
    }

    Vector3 HorizontalMovement() {
        Vector3 res;
        res = moveDirection * moveSpeed;
        if (Mathf.Abs(collisionNormal.y) < 0.80) {
            //res = res + (collisionNormal + Vector3.down) * 3.0f;
            Vector3 tsd = Vector3.Cross(collisionNormal, Vector3.Cross(collisionNormal, Vector3.up));
            slideDirection = Vector3.Lerp(slideDirection, tsd, Time.deltaTime * 8.0f);
        }
        else {
            slideDirection = Vector3.Lerp(slideDirection, Vector3.zero, Time.deltaTime * 8.0f);
        }
        res = res + (slideDirection * 7.0f);
        return res;
    }

    Vector3 VerticalMovement() {
        Vector3 res;
        res = Vector3.up * verticalSpeed;
        return res;
    }

    public void DoJump() {
        if (currentTime - groundedTime < 0.15f && Time.time - landingTime > 0.4f && !gameInputManager.jumping && gameInputManager.isControllable) {
            if (verticalSpeed < 6.0f) {
                verticalSpeed = 6.0f;
            }
            gameInputManager.jumping = true;
            jumpTime = Time.time;
            anim.SetTrigger("Jump");
        }
    }

    public void OnDestroy() {
        main = null;
    }

    public void ResetComboTrigger() {

    }

    public void ResetComboTriggerTrigger() {

    }

    public bool IsGrounded() {
        return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
    }

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

    public void PlayFootstep(bool forcePlay) {
        if (entity != null && entity.swimming) {
            if (time2 > 0.15f && waterSwimmingSound != null) {
                if (soundFootstep.Length > 0) {
                    AudioSource fsas = ScriptedScene.PlayClipAtPoint(waterSwimmingSound, transform.position - new Vector3(0, -1, 0));
                    fsas.volume = 0.2f + moveSpeed * 0.1f * Random.Range(0.7f, 1.0f);
                    fsas.pitch = Random.Range(0.9f, 1.1f);
                }
                time2 = 0;
            }
        }
        else {
            if (forcePlay || (time2 > 0.15f && (moveSpeed > 0.1f || entity == null || entity.fighting) && soundFootstep.Length > 0)) {
                int randIndex = lastFootstep;
                if (soundFootstep.Length > 1) {
                    for (int i = 0; i < 20; i++) {
                        randIndex = Random.Range(0, soundFootstep.Length);
                        if (randIndex != lastFootstep) {
                            break;
                        }
                    }
                }
                else {
                    randIndex = 0;
                }
                if (soundFootstep.Length > 0) {
                    AudioSource fsas = ScriptedScene.PlayClipAtPoint(soundFootstep[randIndex], transform.position - new Vector3(0, -1, 0));
                    fsas.volume = 0.4f * Random.Range(0.7f, 1.0f);
                    fsas.minDistance = 0.3f;
                    fsas.maxDistance = 10.0f;
                }
                lastFootstep = randIndex;
                time2 = 0;
            }
        }
    }

    public void OnControllerColliderHit(ControllerColliderHit hit) {
        collisionDirection = Vector3.Cross(hit.normal, transform.right);
        if (IsGrounded()) {
            if (hit.collider is TerrainCollider || hit.gameObject.CompareTag("Slide")){
                collisionNormal = hit.normal;
            }
            else {
                collisionNormal = Vector3.up;
            }
        }
        //Debug.Log(collisionDirection);
        //Debug.DrawRay(transform.position, collisionDirection);
        /*if (hit.moveDirection.y > 0.01)
            return;*/
    }

    public void SmashStuff() {
        // Boh non so se tenerla zi
        /*if (entity != null && !entity.fighting && !BattleManager.main.IsFighting() && entity.parameters.canUseSwords) {
            anim.SetTrigger("Attack");
        }*/
    }

    public void Stop() {
        moveSpeed = 0.0f;
    }
}
