namespace UniversalNetFrame451;

/// <summary>
/// The Smaller T_Level value is, The higher Priority it has
/// </summary>
/// <typeparam name="T_type"></typeparam>
/// <typeparam name="T_level"></typeparam>
public class PriorityQueue<T_Level, T_type> : IEnumerable<T_type>
    where T_Level : Enum
    //where T_Level : IOrderedEnumerable<T_Level>
{
    public PriorityQueue()
    {

        Type type = Enum.GetUnderlyingType(typeof(T_Level));

        if (type == typeof(int))
        {
            Dictionary<int, T_Level> t_int = new();

            foreach (var item in Enum.GetValues(typeof(T_Level)))
                t_int[(int)item] = (T_Level)item;

            foreach (var item in t_int.OrderBy(t => t.Key))
                _queues.Add(item.Value, new());
            return;
        }

        if (type == typeof(byte))
        {
            Dictionary<byte, T_Level> t_byte = new();

            foreach (var item in Enum.GetValues(typeof(T_Level)))
                t_byte[(byte)item] = (T_Level)item;

            foreach (var item in t_byte.OrderBy(t => t.Key))
                _queues.Add(item.Value, new());
            return;
        }

        if (type == typeof(short))
        {
            Dictionary<short, T_Level> t_short = new();

            foreach (var item in Enum.GetValues(typeof(T_Level)))
                t_short[(short)item] = (T_Level)item;

            foreach (var item in t_short.OrderBy(t => t.Key))
                _queues.Add(item.Value, new());
            return;
        }

        if (type == typeof(long))
        {
            Dictionary<long, T_Level> t_long = new();

            foreach (var item in Enum.GetValues(typeof(T_Level)))
                t_long[(long)item] = (T_Level)item;

            foreach (var item in t_long.OrderBy(t => t.Key))
                _queues.Add(item.Value, new());
            return;
        }

        throw new Exception();

    }


    private readonly Dictionary<T_Level, Queue<T_type>> _queues = [];

    public bool TryEnqueue(T_Level level, T_type content)
    {
        lock (this)
        {
            if (!this._queues.TryGetValue(level, out Queue<T_type> queue) || queue is null)
                return false;

            queue.Enqueue(content);

            return true;
        }
    }


    public bool TryPeek(out T_type output)
    {
        lock (this)
        {
            foreach (T_Level Key in _queues.Keys)
            {
                if (!this._queues[Key].TryPeek(out output))
                    continue;

                return true;
            }

            output = default;
            return false;
        }
    }

    public bool TryPeek(T_Level level, out T_type output)
    {
        lock (this)
        {
            output = default;

            if (!this._queues.TryGetValue(level, out Queue<T_type> queue) || queue is null)
                return false;

            return queue.TryPeek(out output);
        }
    }


    public bool TryDequeue(out T_type output)
    {
        lock (this)
        {
            foreach (T_Level Key in _queues.Keys)
            {
                if (!this._queues[Key].TryDequeue(out output))
                    continue;

                return true;
            }

            output = default;
            return false;
        }
    }

    public bool TryDequeue(T_Level level, out T_type output)
    {
        lock (this)
        {
            output = default;

            if (!this._queues.TryGetValue(level, out Queue<T_type> queue) || queue is null)
                return false;

            return queue.TryDequeue(out output);
        }
    }

    public Dictionary<T_Level, int> Count
    {
        get
        {
            lock (this)
            {
                Dictionary<T_Level, int> counts = [];

                foreach (var item in _queues)
                    counts[item.Key] = item.Value.Count;

                return counts;
            }
        }
    }

    public int Total
    {
        get
        {
            int count = 0; lock (this)
            {
                foreach (var item in _queues)
                    count += item.Value.Count;
                return count;
            }
        }
    }


    public int LevelCount(T_Level t_Level)
    {
        lock (this)
        {
            if (!_queues.TryGetValue(t_Level, out var Value) || Value is null)
                return 0;

            return Value.Count;
        }
    }



    public IEnumerator<T_type> GetEnumerator()
    {
        lock (this)
        {
            List<T_type> t = [];
            while (TryDequeue(out T_type output))
                t.Add(output);

            return t.GetEnumerator();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

}
