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

namespace Assets.Source.Misc.Parallax
{
    public class ParallaxSpawnLayer : MonoBehaviour
    {
        public ParallaxController Controller;
        public List<ParallaxObject> SpawnPrefabs;
        public float SpawnRadius = 10;
        public float SpawnOffset;
        public float MovementFactor = 1;
        
        private List<ParallaxObject> _rightObjects = new List<ParallaxObject>();
        private List<ParallaxObject> _leftObjects = new List<ParallaxObject>();
        private float _lastReleaseRightX;
        private float _lastReleaseLeftX;

        private Vector2 _prevCamPos;

        private Transform CamTransform => Controller.RelativeCamera.transform;

        public float LastSpawnRightX
        {
            get
            {
                var obj = _rightObjects.LastOrDefault();
                return obj?.transform.position.x + obj?.IntervalRadius ?? _lastReleaseRightX;
            }
        }

        public float LastSpawnLeftX
        {
            get
            {
                var obj = _leftObjects.LastOrDefault();
                return obj?.transform.position.x - obj?.IntervalRadius ?? _lastReleaseLeftX;
            }
        }

        void FixedUpdate()
        {
            var camPos = CamTransform.position;
            var delta = (Vector2)camPos - _prevCamPos;
            _lastReleaseLeftX -= delta.x * MovementFactor;
            _lastReleaseRightX += delta.x * MovementFactor;
            _prevCamPos = camPos;

            var bothEmpty = _rightObjects.Count == 0 && _leftObjects.Count == 0;
            var N = 0;
            while (CamTransform.transform.position.x + SpawnRadius > LastSpawnRightX)
            {
                Spawn(1);
                N++;
                if (N > 100)
                {
                    Debug.Log("!!!!!!!!!!");
                    break;
                }
            }
            N = 0;
            if (bothEmpty)
                _lastReleaseLeftX = 0;
            while (CamTransform.transform.position.x - SpawnRadius < LastSpawnLeftX)
            {
                Spawn(-1);
                N++;
                if (N > 100)
                {
                    Debug.Log("!!!!!!!!!!");
                    break;
                }
            }
        }

        //void OnDrawGizmos()
        //{
        //    Gizmos.color = Color.black;
        //    Gizmos.DrawWireCube(Vector3.right * LastSpawnRightX, new Vector3(0.2f, 100));
        //    Gizmos.DrawWireSphere(CamTransform.position, SpawnRadius);
        //}

        private int _spawnCount;
        public void Spawn(float dir)
        {
            _spawnCount++;
            Debug.Log(_spawnCount);
            var suitablePrefabs = SpawnPrefabs;
            //SpawnPrefabs.Where(
            //    o => camPos.y <= o.ParallaxArgs.SpawnHeightLimit).ToList();
            if (suitablePrefabs.Count == 0)
                return;
            
            var prefab = suitablePrefabs.GetRandom();
            var r = prefab.IntervalRadius;
            var lastObj = dir > 0 ? _rightObjects.LastOrDefault() : _leftObjects.LastOrDefault();
            var lastR = lastObj?.IntervalRadius;
            var startX = dir > 0 ? LastSpawnRightX : LastSpawnLeftX;
            var doubleRadius = (lastR ?? r) + r;
            var spawnPos = Vector3.right * (startX + dir * doubleRadius / 2);
            var spawnPosY = 0f;
            if (dir > 0)
            {
                if (_rightObjects.Count > 0)
                    spawnPosY = _rightObjects.Last().transform.position.y;
                else if (_leftObjects.Count > 0)
                    spawnPosY = _leftObjects.Last().transform.position.y;
            }
            else
            {
                if (_leftObjects.Count > 0)
                    spawnPosY = _leftObjects.Last().transform.position.y;
                else if (_rightObjects.Count > 0)
                    spawnPosY = _rightObjects.Last().transform.position.y;
            }
            spawnPos.y = spawnPosY;
            spawnPos.z = prefab.transform.position.z;
            spawnPos.x += SpawnOffset;

            var obj = prefab.Spawn(spawnPos);
            Controller.AddParallaxObject(obj,
                new ParallaxArgs {IntervalRadius = obj.IntervalRadius, MovementFactor = MovementFactor});
            obj.AddOnReleaseHandler(o => HandleDespawnedObject(dir, obj));

            if (dir > 0)
            {
                if (_leftObjects.Count == 0)
                    _lastReleaseLeftX += doubleRadius;
                _rightObjects.Add(obj);
            }
            else
            {
                if (_rightObjects.Count == 0)
                    _lastReleaseRightX -= doubleRadius;
                _leftObjects.Add(obj);
            }
        }

        private void HandleDespawnedObject(float dir, IPoolObject obj)
        {
            if (dir > 0)
            {
                _lastReleaseRightX = LastSpawnRightX;
                _rightObjects.Remove((ParallaxObject) obj);
            }
            else
            {
                _lastReleaseLeftX = LastSpawnLeftX;
                _leftObjects.Remove((ParallaxObject) obj);
            }
        }
    }
}
