using System;
using System.Collections.Generic;
using Creatureliblibrary.Generated;
using Pkmnlib.Generated;
using PkmnLibSharp.Utilities;

namespace PkmnLibSharp.Library
{
    public class Species : PointerWrapper
    {
        private string _name;
        private string _growthRate;

        private readonly Dictionary<string, Forme> _formes =
            new Dictionary<string, Forme>(StringComparer.InvariantCultureIgnoreCase);

        public ushort Id => CreatureSpecies.GetId(Ptr);
        public float GenderRate => CreatureSpecies.GetGenderRate(Ptr);
        public byte CaptureRate => CreatureSpecies.GetCaptureRate(Ptr);
        public string Name => _name ??= CreatureSpecies.GetName(Ptr).PtrString();
        public string GrowthRate => _growthRate ??= CreatureSpecies.GetGrowthRate(Ptr).PtrString();

        public bool HasForme(string s)
        {
            return CreatureSpecies.HasVariant(Ptr, s.ToPtr()) == MarshalHelper.True;
        }

        public bool TryGetForme(string s, out Forme forme)
        {
            if (_formes.TryGetValue(s, out forme))
                return true;
            var ptr = IntPtr.Zero;
            if (CreatureSpecies.TryGetVariant(Ptr, s.ToPtr(), ref ptr) == MarshalHelper.True)
            {
                if (TryResolvePointer(ptr, out forme))
                {
                    _formes.Add(s, forme);
                    return true;
                }

                forme = new Forme(ptr);
                _formes.Add(s, forme);
                return true;
            }

            return false;
        }

        public Forme GetForme(string s)
        {
            if (_formes.TryGetValue(s, out var forme))
                return forme;
            var ptr = IntPtr.Zero;
            CreatureSpecies.GetVariant(ref ptr, Ptr, s.ToPtr()).Assert();
            if (TryResolvePointer(ptr, out forme))
            {
                _formes.Add(s, forme);
                return forme;
            }
            forme = new Forme(ptr);
            _formes.Add(s, forme);
            return forme;
        }
        
        public void SetForme(string s, Forme forme)
        {
            _formes.Add(s, forme);
            CreatureSpecies.SetVariant(Ptr, s.ToPtr(), forme.Ptr).Assert();
        }


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

        public static Species Create(ushort id, string name, Forme defaultForme, float genderRatio, string growthRate,
            byte captureRate, byte baseHappiness)
        {
            var ptr = IntPtr.Zero;
            PokemonSpecies.Construct(ref ptr, id, name.ToPtr(), defaultForme.Ptr, genderRatio,
                growthRate.ToPtr(), captureRate, baseHappiness).Assert();
            var s = new Species(ptr);
            s._formes.Add("default", defaultForme);
            return s;
        }

        protected override void DeletePtr()
        {
            PokemonSpecies.Destruct(Ptr);
        }
    }
}