using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using UnityEditor;
using UnityEngine;

public class LinkXmlGenerator
{
    Dictionary<Type, Type> m_TypeConversion = new Dictionary<Type, Type>();
    HashSet<Type> m_Types = new HashSet<Type>();

    ///<Summary>link.xmlに登録する型を追加する</Summary>
    public void AddType(Type type)
    {
        if( type == null)
            return;
        AddTypeInternal(type);
    }
    
    ///<Summary>link.xmlに登録する型を追加する</Summary>
    public void AddTypes(params Type[] types)
    {
        if (types == null)
            return;
        foreach (var t in types)
            AddTypeInternal(t);
    }

    ///<Summary>link.xmlに登録する型を追加する</Summary>
    public void AddTypes(IEnumerable<Type> types)
    {
        if (types == null)
            return;
        foreach (var t in types)
            AddTypeInternal(t);
    }

    private void AddTypeInternal(Type t)
    {
        if (t == null)
            return;
        
        Type convertedType;
        if (m_TypeConversion.TryGetValue(t, out convertedType))
            m_Types.Add(convertedType);
        else
            m_Types.Add(t);
    }

    ///<Summary>代替する型を指定する。エディターとランタイムで利用する型が違う場合に使用</Summary>
    public void SetTypeConversion(Type a, Type b)
    {
        m_TypeConversion[a] = b; 
    }

    
    ///<Summary>link.xmlに登録するアセットを追加する</Summary>
    public void AddAsset(string assetpath)
    {
        var assets = AssetDatabase.GetDependencies(assetpath);

        List<Type> types = new List<Type>();
        foreach( var asset in assets)
        {
            var type = AssetDatabase.GetMainAssetTypeAtPath(asset);
            if( type == typeof(GameObject) )
            {
                var obj = (GameObject)AssetDatabase.LoadAssetAtPath(asset, typeof(GameObject));
                types.AddRange(obj.GetComponentsInChildren<Component>(true).Select(c => c.GetType()));

            }else{
                types.Add(type);
            }
        }
        AddTypes(types);
    }

    ///<Summary>link.xmlに登録するアセットを追加する</Summary>
    public void AddAssets(string[] assetPaths)
    {
        foreach( var assetPath in assetPaths)
            AddAsset(assetPath);
    }

    ///<Summary>link.xmlファイルを保存する</Summary>
    public void Save(string path)
    {
        var assemblyMap = new Dictionary<Assembly, List<Type>> ();
        foreach (var t in m_Types)
        {
            var a = t.Assembly;
            List<Type> types;
            if (!assemblyMap.TryGetValue(a, out types))
                assemblyMap.Add(a, types = new List<Type>());
            types.Add(t);
        }
        XmlDocument doc = new XmlDocument();
        var linker = doc.AppendChild(doc.CreateElement("linker"));
        foreach (var k in assemblyMap)
        {
            if( k.Key.FullName.Contains("UnityEditor"))
                continue;

            var assembly = linker.AppendChild(doc.CreateElement("assembly"));
            var attr = doc.CreateAttribute("fullname");
            attr.Value = k.Key.FullName;
            if (assembly.Attributes != null)
            {
                assembly.Attributes.Append(attr);
                
                foreach (var t in k.Value)
                {
                    var typeEl = assembly.AppendChild(doc.CreateElement("type"));
                    var tattr = doc.CreateAttribute("fullname");
                    tattr.Value = t.FullName;
                    if (typeEl.Attributes != null)
                    {
                        typeEl.Attributes.Append(tattr);
                        var pattr = doc.CreateAttribute("preserve");
                        pattr.Value = "all";
                        typeEl.Attributes.Append(pattr);
                    }
                }
            }
        }
        doc.Save(path);
    }
}