/*
<copyright file="BGEditorEMExecutor.cs" company="BansheeGz">
    Copyright (c) 2019-2024 All Rights Reserved
</copyright>
*/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace BansheeGz.BGDatabase.Editor
{
    public class BGEditorEMExecutor : IDisposable
    {
        private readonly ConcurrentQueue<Action> TasksQueue = new ConcurrentQueue<Action>();

        public BGEditorEMExecutor() => EditorApplication.update += ThisIsExecutedOnMainThread;
        public void Dispose() => EditorApplication.update -= ThisIsExecutedOnMainThread;

        public void RunOnMainThread(Action action) => TasksQueue.Enqueue(action);

        private void ThisIsExecutedOnMainThread()
        {
            if (TasksQueue.Count > 100)
            {
                Debug.LogError("Unexpected behavior: task queue has more than 10 tasks! Emergency exit: all tasks are canceled !");
                TasksQueue.Clear();
                return;
            }

            Action task;
            while ((task = TasksQueue.Dequeue()) != null)
            {
                try
                {
                    task();
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }
        
        //original from https://stackoverflow.com/questions/4555307/enabling-queuet-with-concurrency
        private class ConcurrentQueue<T> : ICollection, IEnumerable<T>
        {
            private readonly Queue<T> _queue = new Queue<T>();

            public IEnumerator<T> GetEnumerator()
            {
                lock (SyncRoot)
                {
                    foreach (var item in _queue)
                    {
                        yield return item;
                    }
                }
            }

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

            public void CopyTo(Array array, int index)
            {
                lock (SyncRoot)
                {
                    ((ICollection) _queue).CopyTo(array, index);
                }
            }

            // Assumed to be atomic, so locking is unnecessary
            public int Count => _queue.Count;

            public object SyncRoot => ((ICollection) _queue).SyncRoot;

            public bool IsSynchronized => true;

            public void Enqueue(T item)
            {
                lock (SyncRoot)
                {
                    _queue.Enqueue(item);
                }
            }

            public T Dequeue()
            {
                lock (SyncRoot)
                {
                    return _queue.Count == 0 ? default(T) : _queue.Dequeue();
                }
            }

            public T Peek()
            {
                lock (SyncRoot)
                {
                    return _queue.Count == 0 ? default(T) : _queue.Peek();
                }
            }

            public void Clear()
            {
                lock (SyncRoot)
                {
                    _queue.Clear();
                }
            }
        }


    }
}