using System.Linq;
using NaughtyAttributes;
using UnityEngine;

namespace Station46.Environments.Intro_Sphere.Scripts
{
    public class FollowSphere : MonoBehaviour
    {
        private const int AverageLength = 3;
        private static readonly int FresnelPower = Shader.PropertyToID("_FresnelPower");

        public float
            minDistance = 0.001f,
            speed = 200,
            sizeFactor = 1,
            brightnessFactor = 1;
        [SerializeField] private Transform target;
        [BoxGroup("Internal")] [SerializeField] private AudioSource audioSource;
        [BoxGroup("Internal")] [SerializeField] private Transform centerTransform;
        [BoxGroup("Internal")] [SerializeField] private MeshRenderer sphereRenderer;
        
        private float[] _audioSamples;
        private readonly float[] _samplesAverage = new float[AverageLength];
        private int averageIndex;
        private float _initialBrightness;
        private Material _sphereMaterial;
        private Rigidbody _rigidbody;

        private void Awake()
        {
            var clip = audioSource.clip;
            _audioSamples = new float[clip.samples*clip.channels];
            clip.GetData(_audioSamples, 0);

            _sphereMaterial = sphereRenderer.material;
            _initialBrightness = _sphereMaterial.GetFloat(FresnelPower);
            _rigidbody = GetComponent<Rigidbody>();
        }

        private void FixedUpdate()
        {
            var position = transform.position;
            var followPosition = target.position;
            if (Vector3.Distance(position, followPosition) >= minDistance)
            {
                _rigidbody.AddForce(speed * (followPosition - position));
            }
        }

        private void Update()
        {
            if (audioSource.isPlaying)
            {
                _samplesAverage[averageIndex] = Mathf.Abs(_audioSamples[audioSource.timeSamples]);
                averageIndex = (averageIndex + 1) % AverageLength;
                var average = _samplesAverage.Average();
                SetCenterSize(1 + sizeFactor * average);
                SetSphereMaterialBrightness(_initialBrightness - brightnessFactor * average);
            }
        }

        private void SetCenterSize(float size) => centerTransform.localScale = new Vector3(size, size, size);

        private void SetSphereMaterialBrightness(float brightness) => _sphereMaterial.SetFloat(FresnelPower, brightness);
    }
}