using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Assets.Source.Patterns.ObjectPool;
using UnityEngine;

namespace Assets.Source.Misc.Parallax
{
    [Serializable]
    public class ParallaxArgs
    {
        public float MovementFactor = 1;
        public float IntervalRadius = 5;
    }

    public class ParallaxController : MonoBehaviour
    {
        [SerializeField] private Camera _relativeCamera;
        public List<ParallaxObject> SpawnPrefabs;
        public float ReleaseDist = 100;

        private Dictionary<IPoolObject, ParallaxArgs> _lookup = new Dictionary<IPoolObject, ParallaxArgs>();
        private List<ParallaxSpawnLayer> _layers;

        private Vector2 _prevCamPos;

        public Camera RelativeCamera => _relativeCamera;

        void Awake()
        {
            _layers = GetComponentsInChildren<ParallaxSpawnLayer>().ToList();
            foreach (var layer in _layers)
            {
                layer.Controller = this;
            }
            var initParallaxObjects = GetComponentsInChildren<ParallaxObject>();
            foreach (var obj in initParallaxObjects)
                AddParallaxObject(obj, new ParallaxArgs {MovementFactor = 1});
        }

        void Start()
        {
            if (_relativeCamera != null)
                _prevCamPos = _relativeCamera.transform.position;

            //foreach (var layer in _layers)
            //{
            //    for (int i = 0; i < 3; i++)
            //    {
            //        layer.Spawn(1, false);
            //        //layer.Spawn(-1);
            //    }
            //}
        }
        
        void FixedUpdate()
        {
            UpdateParallax();
        }

        private void UpdateParallax()
        {
            var camPos = _relativeCamera.transform.position;
            var delta = (Vector2)camPos - _prevCamPos;

            var dirty = _lookup.ToList();
            while (dirty.Count > 0)
            {
                UpdateParallaxObject(dirty[0].Key, dirty[0].Value, delta);
                dirty.RemoveAt(0);
            }

            _prevCamPos = camPos;
        }

        private void UpdateParallaxObject(IPoolObject obj, ParallaxArgs args, Vector2 delta)
        {
            obj.transform.position += (Vector3)delta * args.MovementFactor;

            var d = _relativeCamera.transform.position - obj.transform.position;
            if (d.magnitude > ReleaseDist + args.IntervalRadius)
            {
                obj.Release();

                //if (args.TriggersRespawn)
                RemoveParallaxObject(obj);
            }
        }
        
        public void SetCamera(Camera cam)
        {
            _relativeCamera = cam;
            _prevCamPos = cam.transform.position;
        }

        public void AddParallaxObject(IPoolObject obj, ParallaxArgs args)
        {
            _lookup.Add(obj, args);
        }
        public void RemoveParallaxObject(IPoolObject obj)
        {
            _lookup.Remove(obj);
        }
    }
}
