using System;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Creatureliblibrary;
using Creatureliblibrary.Generated;
using PkmnLibSharp.Utilities;

namespace PkmnLibSharp.Library
{
    public class Forme : PointerWrapper
    {
        private string _name;
        private ImmutableArray<byte> _types;
        private ImmutableArray<string> _abilities;
        private ImmutableArray<string> _hiddenAbilities;
        private LearnableMoves _moves;

        public string Name => _name ??= SpeciesVariant.GetName(Ptr).PtrString();
        public float Height => SpeciesVariant.GetHeight(Ptr);
        public float Weight => SpeciesVariant.GetWeight(Ptr);
        public float BaseExperience => SpeciesVariant.GetBaseExperience(Ptr);

        public ImmutableArray<byte> Types
        {
            get
            {
                if (_types != null)
                    return _types;

                var typesCount = SpeciesVariant.GetTypeCount(Ptr);
                var types = new byte[typesCount];
                for (ulong i = 0; i < typesCount; i++)
                {
                    types[i] = SpeciesVariant.GetType(Ptr, i);
                }

                _types = types.ToImmutableArray();
                return _types;
            }
        }

        public uint BaseHealth => SpeciesVariant.GetStatistic(Ptr, (byte) Statistic.Health);
        public uint BaseAttack => SpeciesVariant.GetStatistic(Ptr, (Creatureliblibrary.Statistic) Statistic.Attack);
        public uint BaseDefense => SpeciesVariant.GetStatistic(Ptr, (Creatureliblibrary.Statistic) Statistic.Defense);

        public uint BaseSpecialAttack =>
            SpeciesVariant.GetStatistic(Ptr, (Creatureliblibrary.Statistic) Statistic.SpecialAttack);

        public uint BaseSpecialDefense =>
            SpeciesVariant.GetStatistic(Ptr, (Creatureliblibrary.Statistic) Statistic.SpecialDefense);

        public uint BaseSpeed => SpeciesVariant.GetStatistic(Ptr, (Creatureliblibrary.Statistic) Statistic.Speed);

        public ImmutableArray<string> Abilities
        {
            get
            {
                if (_abilities != null)
                    return _abilities;

                var abilityCount = SpeciesVariant.GetTalentCount(Ptr);
                var abilities = new string[abilityCount];
                for (byte i = 0; i < abilityCount; i++)
                {
                    IntPtr s = IntPtr.Zero;
                    SpeciesVariant.GetTalent(Ptr, MarshalHelper.False, i, ref s).Assert();
                    abilities[i] = s.PtrString();
                }

                _abilities = abilities.ToImmutableArray();
                return _abilities;
            }
        }

        public ImmutableArray<string> HiddenAbilities
        {
            get
            {
                if (_hiddenAbilities != null)
                    return _hiddenAbilities;

                var abilityCount = SpeciesVariant.GetSecretTalentCount(Ptr);
                var abilities = new string[abilityCount];
                for (byte i = 0; i < abilityCount; i++)
                {
                    IntPtr s = IntPtr.Zero;
                    SpeciesVariant.GetTalent(Ptr, MarshalHelper.True, i, ref s).Assert();
                    abilities[i] = s.PtrString();
                }

                _hiddenAbilities = abilities.ToImmutableArray();
                return _hiddenAbilities;
            }
        }

        public LearnableMoves Moves
        {
            get
            {
                if (_moves != null) return _moves;
                var movesPtr = SpeciesVariant.GetLearnableAttacks(Ptr);
                if (!TryResolvePointer(movesPtr, out _moves))
                {
                    _moves = new LearnableMoves(movesPtr);
                }

                return _moves;
            }
        }

        public int GetPkmnType(int index)
        {
            return Types[index];
        }

        public static unsafe Forme Create(string name, float height, float weight, uint baseExperience, byte[] types,
            ushort baseHealth, ushort baseAttack, ushort baseDefense, ushort baseSpecialAttack,
            ushort baseSpecialDefense, ushort baseSpeed, string[] abilities, string[] hiddenAbilities,
            LearnableMoves moves)
        {
            var abilitiesConverted = abilities.Select(x => x.ToPtr()).ToArray();
            var hiddenAbilitiesConverted = hiddenAbilities.Select(x => x.ToPtr()).ToArray();
            fixed (IntPtr* ab = abilitiesConverted)
            {
                fixed (IntPtr* hab = hiddenAbilitiesConverted)
                {
                    var ptr = SpeciesVariant.Construct(name.ToPtr(), height, weight, baseExperience, types.ArrayPtr(),
                        (ulong) types.Length, baseHealth, baseAttack, baseDefense, baseSpecialAttack,
                        baseSpecialDefense, baseSpeed, (IntPtr) ab, (ulong) abilities.Length, (IntPtr) hab,
                        (ulong) hiddenAbilities.Length, moves.Ptr);
                    var f = new Forme(ptr);
                    foreach (var intPtr in abilitiesConverted)
                        Marshal.FreeHGlobal(intPtr);
                    foreach (var intPtr in hiddenAbilitiesConverted)
                        Marshal.FreeHGlobal(intPtr);
                    return f;
                }
            }
        }

        internal Forme(IntPtr parent) : base(parent)
        {
        }

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