using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Assets._game.scripts;
using Cinemachine.Utility;
using Rewired;
using Rewired.Data.Mapping;
using UnityEngine;
using UnityEngine.Experimental.PlayerLoop;
using Random = UnityEngine.Random;

public class PlayerController : GameEntity
{
    public int PlayerId { get; private set; }

    public Player Player { get; private set; }

    [SerializeField] private PlayerData _data;

    private float moveSpeed = 6f;

    private float turnSpeed = 10f;

    private Rewired.Player player;

    private Rigidbody rigidBody;

    private float leftStickHorizontal;

    private float rightStickHorizontal;

    private bool fire;

    private bool _hasInitialized;

    private float _nextFire;

    private float fireHeat;                 // Current heat
    private float fireHeatPerBullet = 1.0f; // How much heat it increases per show
    private float maxFireHeat = 8.0f;       // Max amount of heat
    private float fireHeatCooldown = 5.0f;  // How long cooldown when overheated
    private float fireHeatCooling = 5.0f;   // How fast it cool downs

    private Collider _collider;

    private bool _isDead;

    public Transform planeModel;

    private WeaponMount[] _weaponMounts;
    private int _lastWeaponMountIndex;
    private AudioSource _audioSource;
    private GameObject _currentDamageVisual;
    private int _lastHitPoints;
    private AudioSource _engineSound;

    public void Init(Player player, Collider collider)
    {
        _data = player.Data;
        this.PlayerId = player.PlayerId;
        Player = player;
        player.PlayerHit += OnPlayerHit;
        _lastHitPoints = player.HitPoints;
        this.moveSpeed = _data.MoveSpeed;
        this.turnSpeed = _data.TurnSpeed;

        Player.PlayerKilled +=
            (player1, killer) => Kill();
        _collider = collider;
        Setup();
    }

    private void OnPlayerHit(Player player, int amount, GameEntity source)
    {
        if (player.HitPoints <= 25 && _lastHitPoints > 25)
        {
            if (_currentDamageVisual)
            {
                Destroy(_currentDamageVisual);
            }
            _currentDamageVisual = Instantiate(Player.Data.HighDamageEffect,planeModel);
            ControllerVibrateStrong();
        }
        else if (player.HitPoints <= 50 && _lastHitPoints > 50)
        {
            if (_currentDamageVisual)
            {
                Destroy(_currentDamageVisual);
            }
            _currentDamageVisual = Instantiate(Player.Data.MediumDamageEffect, planeModel);
            ControllerVibrateMedium();
        }
        else if (player.HitPoints <= 75 && _lastHitPoints > 75)
        {
            if (_currentDamageVisual)
            {
                Destroy(_currentDamageVisual);
            }
            _currentDamageVisual = Instantiate(Player.Data.LowDamageEffect, planeModel);
            ControllerVibrateLight();
        }
        _lastHitPoints = player.HitPoints;
    }

    public void ControllerVibrateStrong()
    {
        if (player != null)
        {
            foreach (var joystick in player.controllers.Joysticks)
            {
                if (joystick.supportsVibration)
                {
                    joystick.SetVibration(1f, 1f, 2f, 2f);
                }
            }
        }
    }

    public void ControllerVibrateMedium()
    {
        if (player != null)
        {
            foreach (var joystick in player.controllers.Joysticks)
            {
                if (joystick.supportsVibration)
                {
                    joystick.SetVibration(0.75f, 0.75f, 1f, 1f);
                }
            }
        }
    }

    public void ControllerVibrateLight()
    {
        if (player != null)
        {
            foreach (var joystick in player.controllers.Joysticks)
            {
                if (joystick.supportsVibration)
                {
                    joystick.SetVibration(0.5f, 0.5f, 0.5f, 0.5f);
                }
            }
        }
    }


    void Setup()
    {
        Debug.Log(PlayerId);
        player = ReInput.players.GetPlayer(PlayerId);
        Debug.Log(player);

        _audioSource = gameObject.AddComponent<AudioSource>();
        rigidBody = GetComponent<Rigidbody>();
        planeModel = GetComponentsInChildren<Transform>().FirstOrDefault(x => x.CompareTag("Plane"));
        _weaponMounts = planeModel.GetComponentsInChildren<WeaponMount>();
        _engineSound = planeModel.GetComponent<AudioSource>();
        //gameObject.AddComponent<PlayerDamageVisualizer>().Init(Player);

        // Set controller light color if supported
        foreach (var joystick in player.controllers.Joysticks)
        {
            var ds4 = joystick.GetExtension<Rewired.Platforms.PS4.PS4GamepadExtension>();
            if (ds4 != null)
            {
                switch (PlayerId)
                {
                    case 0:
                    {
                        ds4.SetLightColor(0f, 0.89f, 1f); // Blue
                        break;
                    }
                    case 1:
                    {
                        ds4.SetLightColor(1f, 0.77f, 0f); // Yellow
                        break;
                    }
                    case 2:
                    {
                        ds4.SetLightColor(1f, 0f, 0.33f); // Red
                        break;
                    }
                    case 3:
                    {
                        ds4.SetLightColor(0.75f, 1f, 0); // Green
                        break;
                    }
                }
            }
        }
    }

    void Update()
    {
        if (_isDead)
        {
            return;
        }

        if(fireHeat > 0.0f) {
            fireHeat -= Time.deltaTime * fireHeatCooling;
            if(fireHeat < 0.0f)
                fireHeat = 0.0f;
        }

        GetInput();
    }

    private void FixedUpdate()
    {
        ProcessInput();
    }

    private void OnEnable()
    {
        if (rigidBody)
        {
            rigidBody.isKinematic = false;
        }

        leftStickHorizontal = 0;
        rightStickHorizontal = 0;
    }

    private void OnDisable()
    {
        rigidBody.isKinematic = true;
    }

    private void ProcessInput()
    {
        var roll = rightStickHorizontal * (Time.deltaTime * _data.TurnSpeed);
        var yaw = leftStickHorizontal * (Time.deltaTime * _data.TurnSpeed);

        // Change direction
        var y = yaw > 0 || yaw < 0 ? yaw + roll : yaw;

        var horizontalRotation = Quaternion.identity;
        horizontalRotation.eulerAngles = new Vector3(0, y, 0);

        rigidBody.rotation *= horizontalRotation;

        // Rotate model
        var _roll = Mathf.Clamp(y * 33, -70, 70);
        planeModel.localRotation = Quaternion.Slerp(planeModel.localRotation, Quaternion.Euler(-90 + _roll, 90, -90f), 2 * Time.deltaTime);


        if (_engineSound)
        {
            _engineSound.pitch = 1 +  Mathf.InverseLerp(0, 70, Mathf.Abs(_roll)) * 0.7f;
        }
//        Debug.Log("Model Rotation: " + planeModel.localRotation);
//        Debug.Log("Body Rotation" + horizontalRotation.eulerAngles);

        // Move forward
        var glideFactor = Mathf.Clamp(yaw > 0 || yaw < 0 ? y / 6 : y / 2, -2f, 2f);
        var addPosition = new Vector3(glideFactor, 0, 1f);
        addPosition = rigidBody.rotation * addPosition;
        rigidBody.velocity = addPosition * (Time.deltaTime * _data.MoveSpeed);

        // Process fire
        if (fire)
        {
            if (_nextFire <= Time.timeSinceLevelLoad && fireHeat < maxFireHeat)
            {
                Fire();
            }
        }
    }

    private void GetInput()
    {
        leftStickHorizontal = player.GetAxis("Horizontal0");
        rightStickHorizontal = player.GetAxis("Horizontal1");
        fire = player.GetButton("Fire1");
    }

    private void Fire()
    {
        var spawnLocation = transform.forward + Vector3.forward;
        if (_weaponMounts.Length > 0)
        {
            _lastWeaponMountIndex += 1;
            if (_lastWeaponMountIndex >= _weaponMounts.Length)
            {
                _lastWeaponMountIndex = 0; //TODO: Random?
            }
            spawnLocation = _weaponMounts[_lastWeaponMountIndex].transform.position;
            _weaponMounts[_lastWeaponMountIndex].PlayEffect();
        }
        var go = Instantiate(Player.Data.Projectile.Model, spawnLocation, transform.rotation);
        if (Player.Data.Projectile.FireAudio)
        {
            _audioSource.PlayOneShot(Player.Data.Projectile.FireAudio, 1f);
        }
        //TODO: Object poooling
        var projectile = go.AddComponent<Projectile>();
        projectile.Owner = this;
        var projectileCollider = projectile.GetComponent<Collider>();
        if (projectileCollider != null)
        {
            Physics.IgnoreCollision(projectileCollider, _collider);
        }
        projectile.Data = Player.Data.Projectile;
        _nextFire = Time.timeSinceLevelLoad + Player.Data.FireRate;

        fireHeat+= fireHeatPerBullet;
        
        // Overheated, cooldown period
        if(fireHeat >= maxFireHeat)
            fireHeat += fireHeatCooldown;

        Player.Fire();
        ControllerVibrateLight();
    }

    public void Kill()
    {
        var boomBoom = gameObject.AddComponent<TimedExplosion>();
        boomBoom.Explosion = Player.Data.DeathBoom;
        boomBoom.Init(0.5f, transform);
        _isDead = true;
        rigidBody.useGravity = true;
        rigidBody.constraints = RigidbodyConstraints.None;
        rigidBody.AddExplosionForce(0.5f, transform.position + Vector3.one * Random.value, 1);
        rigidBody.angularVelocity = new Vector3(Random.Range(0, 360), Random.Range(0, 360), Random.Range(0, 360));
        if (_engineSound)
        {
            _engineSound.pitch = Random.Range(1.5f, 3f);
        }
    }
}
