namespace Engine.Resource;

public class ResourceManager : IResourceManager
{
  internal IResourceStreamProvider StreamProvider => _streamProvider;

  private readonly IResourceStreamProvider _streamProvider;
  private readonly Dictionary<Type, IResourceLoader> _loaders = new();
  private readonly Dictionary<Type, ResourceStorage> _storages = new();

  public ResourceManager(IResourceStreamProvider parStreamProvider)
  {
    _streamProvider = parStreamProvider;
  }

  internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
  {
    _loaders.Add(typeof(T), parLoader);
  }

  public T Load<T>(string parPath) where T : class
  {
    if (!_storages.TryGetValue(typeof(T), out var storage))
    {
      storage = new ResourceStorage();
      _storages.Add(typeof(T), storage);
    }

    if (storage.Get<T>(parPath) is { } cachedResource)
    {
      return cachedResource;
    }

    var loader = _loaders.GetValueOrDefault(typeof(T));
    if (loader == null)
    {
      throw new InvalidOperationException($"No loader found for type {typeof(T)}");
    }

    var resource = loader.Load(parPath, _streamProvider);

    storage.Add(parPath, resource);

    return (T)resource;
  }

  internal void Reset()
  {
    _storages.Clear();
  }

  private class ResourceStorage
  {
    private readonly Dictionary<string, object> _resources = new();

    public void Add<T>(string parPath, T parResource) where T : class
    {
      _resources.Add(parPath, parResource);
    }

    public T? Get<T>(string parPath) where T : class
    {
      return _resources.TryGetValue(parPath, out var resource) ? (T)resource : null;
    }

    public void Remove(string parPath)
    {
      _resources.Remove(parPath);
    }
  }
}