using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Structures;

namespace Intrepid.Utilities.Structures.Traits
{
    public sealed class Traits<T> : IReadOnlyTraits<T>, IMutableTraits<T> where T : ITrait
    {
        private Dictionary<string, T> Holder { get; } = new();
        private static string KeyFor<P>() => typeof(P).AssemblyQualifiedName;

        public IEnumerable<SKey> TraitKeys => Holder.Values.Select(v => v.Key);
        public IEnumerable<Type> TraitTypes => AllTraits.Select(t => t.GetType());
        public IEnumerable<T> AllTraits => Holder.Values;
        public TraitsQuery<T> Querying() => new(this);

        public void Declare<TT>(TT trait) where TT : T
        {
            Holder[KeyFor<TT>()] = trait;
        }
        
        public bool Discard<TT>() where TT : T => Holder.Remove(KeyFor<TT>());

        public bool Has<TT>() where TT : T => Holder.ContainsKey(KeyFor<TT>());
        
        public bool HasNot<TT>() where TT : T => !Has<TT>();
        
        public int Count<TT>() where TT : T => AllTraits.Count(t => t is TT);
        
        public int Count() => AllTraits.Count();

        public bool TryGet<TT>(out TT trait) where TT : T
        {
            if (Holder.TryGetValue(KeyFor<TT>(), out var t))
            {
                trait = (TT)t;
                return true;
            }

            trait = default;
            return false;
        }

        public bool TryFindFirst<TT>(out TT tt) where TT : T
        {
            if (AllTraits.Any(t => t is TT))
            {
                tt = (TT)AllTraits.First(t => t is TT);
                return true;
            }
            
            tt = default;
            return false;
        }
    }
}