using SadnessMonday.DiscIndustries.Fluids;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Jobs;
using Unity.Jobs;
using System.Collections.Generic;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace SadnessMonday.DiscIndustries.Components{
    public class FluidJobTester : MonoBehaviour {
        public NativeList<FluidBuffer> buffers;

        [SerializeField]
        int bufferCount = 10;

        [SerializeField]
        bool DrainEnabled = true;
        [SerializeField]
        bool FillEnabled = true;

        private void OnEnable() {
            //buffers = new NativeArray<FluidBuffer>(bufferCount, Allocator.Persistent);
            buffers = new NativeList<FluidBuffer>(Allocator.Persistent);
            buffers.ResizeUninitialized(bufferCount);

            PopulateTestBuffers();
#if UNITY_EDITOR
            SetupGizmos();
#endif
        }

        int[] sinks = new int[] { 9, 5 };
        int[] sources = new int[] { 0 };

        private void PopulateTestBuffers() {
            buffers[0] = new FluidBuffer(0, 200) { Right = 1 };
            buffers[1] = new FluidBuffer(0, 200) { Left = 0, Right = 2 };
            buffers[2] = new FluidBuffer(0, 200) { Left = 1, Right = 3, Bottom = 4 };
            buffers[3] = new FluidBuffer(0, 200) { Left = 2, Right = 5 };
            buffers[5] = new FluidBuffer(0, 200) { Left = 3 };


            buffers[4] = new FluidBuffer(0, 200) { Top = 2, Bottom = 6 };
            buffers[6] = new FluidBuffer(0, 200) { Top = 4, Bottom = 7 };
            buffers[7] = new FluidBuffer(0, 200) { Top = 6, Bottom = 8 };
            buffers[8] = new FluidBuffer(0, 200) { Top = 7, Bottom = 9 };
            buffers[9] = new FluidBuffer(0, 200) { Top = 8 };

            //for (int i = 0; i < bufferCount; i++) {
            //    FluidBuffer buffer = new FluidBuffer(i, 200);

            //    if (i > 0) {
            //        buffer.Left = i - 1;
            //    }
            //    if (i < bufferCount - 1) {
            //        buffer.Right = i + 1;
            //    }

            //    buffers[i] = buffer;
            //}
        }

        private void OnDisable() {
            buffers.Dispose();
        }

        float timer = 0;
        float timestep = 1f / TimeManager.TicksPerSecond;

        private void Update() {
            timer += Time.deltaTime;
            while (timer > timestep) {
                timer -= timestep;

                Simulate();
            }
        }

        private void Simulate() {
            if (DrainEnabled) {
                foreach (var sink in sinks) {
                    // Remove some fluid from the last buffer
                    var s = buffers[sink];
                    s.Content = s.Content / 2;
                    buffers[sink] = s;
                }
            }

            if (FillEnabled) {
                foreach (var source in sources) {
                    // Remove some fluid from the last buffer
                    var s = buffers[source];
                    s.Content = s.Content + 20;
                    s.Content = Mathf.Clamp(s.Content, 0, s.Capacity);
                    buffers[source] = s;
                }
            }

            // Schedule Jobs
            UpdateFlow flowJob = new UpdateFlow(buffers.AsArray());
            var flowHandle = flowJob.Schedule(buffers.Length, 1);

            UpdateContent contentJob = new UpdateContent(buffers.AsArray());
            var contentJobHandle = contentJob.Schedule(buffers.Length, 1, flowHandle);

            contentJobHandle.Complete();

            int total = 0;
            for (int i = 0; i < buffers.Length; i++) {
                total += buffers[i].Content;
            }

            print($"Total system fluid content = {total}");
        }


#if UNITY_EDITOR
        Dictionary<int, Vector3> positions = new Dictionary<int, Vector3>();
        Bounds gizmoBounds;

        void SetupGizmos() {
            gizmoBounds = new Bounds(Vector3.zero, Vector3.one);
            HashSet<int> visited = new HashSet<int>();
            Stack<(int, Vector3)> stack = new Stack<(int, Vector3)>();
            stack.Push((0, Vector3.zero));

            while (stack.Count > 0) {
                var current = stack.Pop();
                var currentIdx = current.Item1;

                if (!visited.Add(currentIdx)) continue;

                var currentPos = current.Item2;
                var currentBuffer = buffers[currentIdx];
                positions[currentIdx] = currentPos;
                gizmoBounds.Encapsulate(currentPos);

                if (currentBuffer.Left >= 0) stack.Push((currentBuffer.Left, currentPos + Vector3.left));
                if (currentBuffer.Right >= 0) stack.Push((currentBuffer.Right, currentPos + Vector3.right));
                if (currentBuffer.Top >= 0) stack.Push((currentBuffer.Top, currentPos + Vector3.up));
                if (currentBuffer.Bottom >= 0) stack.Push((currentBuffer.Bottom, currentPos + Vector3.down));
            }
        }


        void OnDrawGizmos() {
            if (!Application.isPlaying) return;
            Gizmos.color = Color.red;
            for (int i = 0; i < buffers.Length; i++) {
                var buffer = buffers[i];
                Gizmos.DrawSphere(positions[i] * 3, buffer.NormalizedContent + .1f);
            }
        }
#endif
    }
}