using System;
using System.Collections.Concurrent;

namespace PkmnLibSharp.Utilities
{
    public abstract class PointerWrapper : IDisposable
    {
        private readonly IntPtr _ptr;
        internal IntPtr Ptr
        {
            get
            {
                if (_isDeleted)
                {
                    throw new Exception("Pointer access after dispose detected. This is not legal, and will cause native exceptions.");
                }
                return _ptr;
            }
        }

        private bool _isDeleted = false;

        private static readonly ConcurrentDictionary<IntPtr, WeakReference<object>> Cached =
            new ConcurrentDictionary<IntPtr, WeakReference<object>>();

        protected PointerWrapper(IntPtr ptr)
        {
            _ptr = ptr;
            var weakRef = new WeakReference<object>(this);
            Cached.TryAdd(ptr, weakRef);
        }

        ~PointerWrapper()
        {
            if (!_isDeleted)
                Cached.TryRemove(Ptr, out _);
        }

        public static bool TryResolvePointer<T>(IntPtr p, out T result) where T : PointerWrapper
        {
            if (Cached.TryGetValue(p, out var val))
            {
                if (val.TryGetTarget(out var target))
                {
                    result = (T) target;
                    return true;
                }
            }
            result = null;
            return false;
        }

        protected abstract void DeletePtr();
        public virtual void Dispose()
        {
            if (_isDeleted)
                return;
            Cached.TryRemove(Ptr, out _);
            DeletePtr();
            _isDeleted = true;
        }
    }
}