using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 구상
/// 1. 리소르 로드 작업은 비용이 큰 작업이기에 한 번 로드한 리소스는 딕셔너리에 저장해 필요시 재활용 : 캐싱
/// 2. 경로를 받아 리소스를 로드해 Instantiate를 실행해 생성된 게임 오브젝트를 반환
/// 3. 생성된 게임 오브젝트를 파괴
/// </summary>
public class ResourceManager: IManager
{
    private Dictionary<string, Object> _resources = new Dictionary<string, Object>();
    
    public T Load<T>(string path) where T : Object
    {
        if (string.IsNullOrEmpty(path))
        {
            Debug.LogError("ResourceManager Load Failed! path is null or empty");
            return null;
        }
        
        // 캐싱된게 있으면 그걸 반환
        if (_resources.TryGetValue(path, out Object cachedResource)) return cachedResource as T;
        
        // 새로 로드
        T resource = Resources.Load<T>(path);
        
        if (resource == null)
        {
            Debug.LogError($"ResourceManager Load Failed! resource not found at path: {path}");
            return null;
        }
        
        _resources.Add(path, resource);
        return resource;
    }
    
    public GameObject Instantiate(string path, Transform parent = null)
    {
        // 1. 경로를 조합하여 원본 프리팹을 로드
        GameObject original = Load<GameObject>(path);
        
        if (original == null)
        {
            Debug.LogError($"ResourceManager Instantiate Failed! Prefab not found at path: {path}");
            return null;
        }

        // 2. 프리팹에 Poolable 컴포넌트가 있는지 확인
        if (original.GetComponent<Poolable>() != null)
        {
            // Poolable이 있으면 PoolManager를 통해 오브젝트를 가져옴
            return Manager.Pool.Pop(path, parent);
        }
        
        // 3. Poolable이 없으면 직접 생성
        GameObject go = Object.Instantiate(original, parent);
        go.name = original.name;

        return go;
    }
    
    public GameObject Instantiate(GameObject original, Transform parent = null)
    {
        // 3. Poolable이 없으면 직접 생성
        GameObject go = Object.Instantiate(original, parent);
        go.name = original.name;

        return go;
    }
    
    public void Destroy(GameObject go, float delay = 0f)
    {
        if (go == null) return;
        
        Object.Destroy(go,delay);
    }
    
    public void Clear()
    {
        _resources.Clear();
    }
}

/// 리소스 매니저에서 캐싱 작업을 왜 하는가?
/// - 단순하게 호출하는 모든 생성작업이 어떠한 오브젝트의 풀링을 위한 것이라면 어차피 초기 한 번만 불려서 그 오브젝트를 재활용하면 되는데
/// 뭐하러 굳이 리소스를 딕셔너리로 남겨서 캐싱을 하나? 싶지만
/// 리소스 매니저의 역할은 풀링만을 위해 사용하는것이 아닌 프로젝트 전체의 리소스 관리를 위한 것이기에 풀링을 사용하지 않는 상황에서도 해당 리소스를 재활용할 수 있도록 하기 위함이다.
/// 예1. 가끔 씬에서 특정 오브젝트를 생성해야 하는 상황이 발생할 수 있다. 
/// 예2. UI 매니저에서 특정 UI를 띄워야 하는 상황이 발생할 수 있다.
/// 예3. 서로 다른 스크립트가 같은 리소스를 필요로 할 수 있다.
/// - 이러한 상황에서 매번 Resources.Load를 통해 리소스를 불러오게 된다면 비용이 큰 작업이기에 성능 저하가 발생할 수 있다.
/// 즉 리소스 매니저에서 캐싱 작업을 하는 이유는 프로젝트 전체의 리소스 관리를 효율적으로 하기 위함이다.