using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PkmnLibSharp.Utilities;

namespace PkmnLibSharp.Battling
{
    public class PokemonParty : PointerWrapper, IEnumerable<Pokemon?>
    {
        private Pokemon?[]? _cache;
        private ReadOnlyNativePtrArray<Pokemon>? _party;
        
        internal PokemonParty(IntPtr ptr) : base(ptr){}
        
        public PokemonParty(byte size = 6) : base(Creaturelib.Generated.CreatureParty.ConstructWithSize(size))
        {}

        public PokemonParty(Pokemon?[] pokemon) : base(
            Creaturelib.Generated.CreatureParty.ConstructFromArray(pokemon.ArrayPtr(), (ulong) pokemon.Length))
        {
            _cache = pokemon;
        }

        protected internal override void Initialize(IntPtr ptr)
        {
            base.Initialize(ptr);
            _cache = Party.ToArray();
        }

        public virtual Pokemon? this[int i] => GetAtIndex((ulong) i);

        public Pokemon? GetAtIndex(ulong index)
        {
            var ptr = IntPtr.Zero;
            Creaturelib.Generated.CreatureParty.GetAtIndex(ref ptr, Ptr, index).Assert();
            return TryResolvePointer(ptr, out Pokemon? pkmn) ? pkmn! : Constructor.Active.ConstructPokemon(ptr)!;
        }

        public void Switch(ulong indexA, ulong indexB)
        {
            Creaturelib.Generated.CreatureParty.Switch(Ptr, indexA, indexB);
            var temp = _cache![indexA];
            _cache[indexA] = _cache[indexB];
            _cache[indexB] = temp;
        }

        public Pokemon? SwapInto(ulong indexA, Pokemon? pokemon)
        {
            var ptr = IntPtr.Zero;
            var pkmnPtr = IntPtr.Zero;
            if (pokemon != null) pkmnPtr = pokemon.Ptr;
            Creaturelib.Generated.CreatureParty.SwapInto(ref ptr, Ptr, indexA, pkmnPtr).Assert();
            _cache![indexA] = pokemon;
            if (TryResolvePointer(ptr, out Pokemon? newPokemon))
                return newPokemon!;
            return Constructor.Active.ConstructPokemon(ptr)!;
        }

        public bool HasAvailablePokemon()
        {
            return Creaturelib.Generated.CreatureParty.HasAvailableCreatures(Ptr) == 1;
        }

        public ReadOnlyNativePtrArray<Pokemon> Party
        {
            get
            {
                if (_party != null) return _party;
                var ptr = Creaturelib.Generated.CreatureParty.GetParty(Ptr);
                _party = new ReadOnlyNativePtrArray<Pokemon>(ptr, (int) Length, Constructor.GenericType.Pokemon);
                return _party;
            }
        }

        public ulong Length => Creaturelib.Generated.CreatureParty.GetLength(Ptr);

        public void PackParty()
        {
            Creaturelib.Generated.CreatureParty.PackParty(Ptr);
            _cache = Party.ToArray();
        }
        
        public IEnumerator<Pokemon?> GetEnumerator()
        {
            return Party.GetEnumerator();
        }

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

        protected override void DeletePtr()
        {
            Creaturelib.Generated.CreatureParty.Destruct(Ptr);
        }

        protected internal override void MarkAsDeleted()
        {
            base.MarkAsDeleted();
            foreach (var pokemon in Party)
            {
                pokemon?.MarkAsDeleted();
            }
            _cache = null;
        }

        
        
    }
}