using System;
using System.Collections.Generic;
using PkmnLibSharp.Utilities;
using Random = PkmnLibSharp.Utilities.Random;

namespace PkmnLibSharp.Library
{
    public class NatureLibrary : PointerWrapper
    {
        private readonly Dictionary<string, Nature> _cache =
            new Dictionary<string, Nature>(StringComparer.InvariantCultureIgnoreCase);

        private readonly Dictionary<IntPtr, string> _natureNames = new Dictionary<IntPtr, string>();

        internal NatureLibrary(IntPtr ptr) : base(ptr)
        {
        }

        public NatureLibrary(ulong initialCapacity) : base(Pkmnlib.Generated.NatureLibrary.Construct(initialCapacity))
        {
        }

        public void LoadNature(string name, Nature nature)
        {
            Pkmnlib.Generated.NatureLibrary.LoadNature(Ptr, name.ToPtr(), nature.Ptr).Assert();
            _cache.Add(name, nature);
            _natureNames.Add(nature.Ptr, name);
        }

        public Nature GetNature(string name)
        {
            if (_cache.TryGetValue(name, out var nature))
                return nature;
            var ptr = IntPtr.Zero;
            Pkmnlib.Generated.NatureLibrary.GetNatureByName(Ptr, name.ToPtr(), ref ptr).Assert();
            if (TryResolvePointer(ptr, out Nature? n))
            {
                _cache.Add(name, n!);
                _natureNames.Add(ptr, name);
                return n!;
            }

            nature = new Nature(ptr);
            _cache.Add(name, nature);
            _natureNames.Add(ptr, name);
            return nature;
        }

        public string GetNatureName(Nature nature)
        {
            if (_natureNames.TryGetValue(nature.Ptr, out var s))
                return s;
            var ptr = IntPtr.Zero;
            Pkmnlib.Generated.NatureLibrary.GetNatureName(Ptr, nature.Ptr, ref ptr).Assert();
            return ptr.PtrString()!;
        }

        public string GetRandomNatureName(Random random)
        {
            IntPtr val = IntPtr.Zero;
            Pkmnlib.Generated.NatureLibrary.GetRandomNatureName(Ptr, random.Ptr, ref val).Assert();
            return val.PtrString()!;
        }

        public IEnumerable<string> GetNatures()
        {
            var count = Pkmnlib.Generated.NatureLibrary.GetNatureCount(Ptr);
            var ptr = IntPtr.Zero;
            for (ulong i = 0; i < count; i++)
            {
                Pkmnlib.Generated.NatureLibrary.GetNatureByIndex(Ptr, i, ref ptr).Assert();
                yield return ptr.PtrString()!;
            }
        }

        protected override void DeletePtr()
        {
            Pkmnlib.Generated.NatureLibrary.Destruct(Ptr);
        }

        protected internal override void MarkAsDeleted()
        {
            base.MarkAsDeleted();
            foreach (var nature in _cache)
            {
                nature.Value.MarkAsDeleted();
            }
        }
    }
}