using System;
using System.Runtime.Serialization;
using PkmnLibSharp.Battling;
using PkmnLibSharp.Library;
using PkmnLibSharp.Library.Items;
using PkmnLibSharp.Library.Moves;

namespace PkmnLibSharp.Utilities
{
    public abstract class Constructor
    {
        public static Constructor Active { get; set; } = 
            new Constructor<Species, Forme, MoveData, Item, LearnableMoves, Pokemon, Battle>();
        
        internal abstract Species? ConstructSpecies(IntPtr ptr);
        internal abstract Forme? ConstructForme(IntPtr ptr);
        internal abstract MoveData? ConstructMove(IntPtr ptr);
        internal abstract Item? ConstructItem(IntPtr ptr);
        internal abstract LearnableMoves? ConstructLearnableMoves(IntPtr ptr);
        internal abstract Pokemon? ConstructPokemon(IntPtr ptr);
        internal abstract Battle? ConstructBattle(IntPtr ptr);
    }

    public class Constructor<
            TSpecies, TForme, TMoveData, TItem, TLearnableMoves, 
            TPokemon, TBattle> 
        : Constructor
        where TSpecies : Species 
        where TForme : Forme 
        where TMoveData : MoveData 
        where TItem: Item 
        where TLearnableMoves : LearnableMoves
        
        where TPokemon : Pokemon
        where TBattle : Battle
    {
        private readonly Type _speciesType = typeof(TSpecies);
        private readonly Type _formeType = typeof(TForme);
        private readonly Type _moveDataType = typeof(TMoveData);
        private readonly Type _itemType = typeof(TItem);
        private readonly Type _learnableMovesType = typeof(TLearnableMoves);
        
        private readonly Type _pokemonType = typeof(TPokemon);
        private readonly Type _battleType = typeof(TBattle);

        private static T? Create<T>(IntPtr ptr, Type t) where T : PointerWrapper
        {
            if (ptr == IntPtr.Zero) return null;
            var o = (T) FormatterServices.GetUninitializedObject(t);
            o.Initialize(ptr);
            return o;
        }

        internal override Species? ConstructSpecies(IntPtr ptr)
        {
            return Create<TSpecies>(ptr, _speciesType);
        }

        internal override Forme? ConstructForme(IntPtr ptr)
        {
            return Create<TForme>(ptr, _formeType);
        }

        internal override MoveData? ConstructMove(IntPtr ptr)
        {
            return Create<TMoveData>(ptr, _moveDataType);
        }

        internal override Item? ConstructItem(IntPtr ptr)
        {
            return Create<TItem>(ptr, _itemType);
        }

        internal override LearnableMoves? ConstructLearnableMoves(IntPtr ptr)
        {
            return Create<TLearnableMoves>(ptr, _learnableMovesType);
        }

        internal override Pokemon? ConstructPokemon(IntPtr ptr)
        {
            return Create<TPokemon>(ptr, _pokemonType);
        }

        internal override Battle? ConstructBattle(IntPtr ptr)
        {
            return Create<TBattle>(ptr, _battleType);
        }
    }
}