using System;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
/// <summary>
/// 扫描出全部资源中,非GameObject,也没有被任何GameObject或者Scene引用的资源;
/// </summary>
public class UnusedResourceToList
{   
    [MenuItem("ResourceTool/Scan All Assets")]
    public static void Get()
    {
        var instance = new UnusedResourceToList();
        instance.Start();
    }
    
    private List<ResourceUsageData> _assetList;
    private List<string> _failAsset;
    private string[] _testList;
    private int _index;
    protected void Start()
    {
        _failAsset = new List<string>();
        var assetPaths = AssetDatabase.GetAllAssetPaths();
        _assetList = new List<ResourceUsageData>(assetPaths.Length);
        for (var i = 0; i < assetPaths.Length; i++)
        {
            const string assetHeader = "Assets";
            if (assetPaths[i].StartsWith(assetHeader) && !assetPaths[i].StartsWith("Assets/StreamingAssets"))
            {
                var filePath = Application.dataPath + assetPaths[i].Substring(assetHeader.Length);
                // 排除文件夹
                if (File.Exists(filePath))
                {
                    var extension = Path.GetExtension(assetPaths[i]);
                    if (string.IsNullOrEmpty(extension) ||
                        !extension.Equals(".prefab", StringComparison.OrdinalIgnoreCase) &&
                        !extension.Equals(".unity", StringComparison.OrdinalIgnoreCase) &&
                        !extension.Equals(".cs", StringComparison.OrdinalIgnoreCase) &&
                        !extension.Equals(".js", StringComparison.OrdinalIgnoreCase))
                    {
                        var item = new ResourceUsageData(assetPaths[i], extension);
                        if (assetPaths[i].Contains("/Resources/"))
                            item.inResource = true;
                        _assetList.Add(item);
                    }
                }
            }
        }
        _testList = (from assetPath in AssetDatabase.GetAllAssetPaths()
            let extension = Path.GetExtension(assetPath)
            where !string.IsNullOrEmpty(extension) && (
                  extension.Equals(".prefab", StringComparison.OrdinalIgnoreCase) ||
                  extension.Equals(".unity", StringComparison.OrdinalIgnoreCase) ||
                  assetPath.Contains("/Resources/"))
            select assetPath).ToArray();
        _index = 0;
        EditorApplication.update += OnEditorUpdate;
    }
    /// <summary>
    /// 从Scene和GameObject建立连接库
    /// </summary>
    private void OnEditorUpdate()
    {
        var finish = false;
        try
        {
            if (_index < _testList.Length)
            {
                var path = _testList[_index];
                var dependencies = AssetDatabase.GetDependencies(path);
                for (var i = 0; i < dependencies.Length; i++)
                {
                    if (!string.IsNullOrEmpty(dependencies[i]) && !path.Equals(dependencies[i]))
                    {
                        var data = GetListItem(dependencies[i]);
                        if (data != null)
                        {
                            data.refCount++;
                            if (path.Contains("/Resources/"))
                                data.refByResource = true;
                        }
                    }
                }
                Debug.Log(string.Format("扫描资源{0} / {1}", _index, _testList.Length));
                _index++;
            }
            else
            {
                Debug.LogWarning("完成全部扫描");
                finish = true;
            }
        }
        catch (Exception e)
        {
            Debug.LogError(e.ToString());
            Debug.LogError(string.Format("扫描失败于{0}", _index));
            _failAsset.Add(_testList[_index]);
            _index++;
        }

        if (finish)
            WriteToFile();
    }

    private void WriteToFile()
    {
        EditorApplication.update = null;
        _assetList.Sort(CompareMethod);
        var builder = new StringBuilder();
        builder.Append("Asset");
        builder.Append('\t');
        builder.Append("Extension");
        builder.Append('\t');
        builder.Append("IsResource");
        builder.Append('\t');
        builder.Append("RefByResource");
        builder.Append('\t');
        builder.Append("RefCount");
        var filePath = Application.dataPath + "/_Test/AllAssetSituation.txt";
        for (var i = 0; i < _assetList.Count; i++)
            _assetList[i].WriteToBuilder(builder);
        File.WriteAllText(filePath, builder.ToString());
        Debug.LogWarning("结果输出到文件 " + filePath);
        if (_failAsset.Count > 0)
        {
            Debug.LogError("扫描资源失败数目 " + _failAsset.Count);
            for (var i = 0; i < _failAsset.Count; i++)
                Debug.LogError("扫描失败于 " + _failAsset[i]);
        }
    }
    
    private static int CompareMethod(ResourceUsageData source, ResourceUsageData target)
    {
        var result = source.inResource.CompareTo(target.inResource);
        if (result == 0)
            result = source.refCount.CompareTo(target.refCount);
        if (result == 0)
            result = string.Compare(source.assetPath, target.assetPath, StringComparison.Ordinal);
        return result;
    }
    
    private ResourceUsageData GetListItem(string assetPath)
    {
        ResourceUsageData data = null;
        for (var i = 0; i < _assetList.Count; i++)
            if (_assetList[i].assetPath.Equals(assetPath))
            {
                data = _assetList[i];
                break;
            }

        return data;
    }
}

public class ResourceUsageData
{
    public string assetPath;
    public string extension;
    public Type assetType;
    public int refCount;
    public bool inResource;
    public bool refByResource;

    public ResourceUsageData(string path, string extension)
    {
        assetPath = path;
        this.extension = extension;
        assetType = AssetDatabase.GetMainAssetTypeAtPath(path);
    }

    public void WriteToBuilder(StringBuilder builder)
    {
        if (builder.Length > 0)
            builder.Append('\n');
        builder.Append(assetPath);
        builder.Append('\t');
        builder.Append(extension);
        builder.Append('\t');
        builder.Append(inResource);
        builder.Append('\t');
        builder.Append(refByResource);
        builder.Append('\t');
        builder.Append(refCount);
    }
}