using System;
using System.Collections.Generic;
using System.Linq;

#if !UNITY_EDITOR && UNITY_WSA
// For System.Reflection.TypeExtensions
using System.Reflection;
#endif

namespace Unity.VisualScripting.FullSerializer.Internal
{
    public static class fsTypeExtensions
    {
        /// <summary>
        /// Returns a pretty name for the type in the style of one that you'd see
        /// in C# without the namespace.
        /// </summary>
        public static string CSharpName(this Type type)
        {
            return CSharpName(type, /*includeNamespace:*/ false);
        }

        public static string CSharpName(this Type type, bool includeNamespace, bool ensureSafeDeclarationName)
        {
            var name = CSharpName(type, includeNamespace);
            if (ensureSafeDeclarationName)
            {
                name = name.Replace('>', '_').Replace('<', '_').Replace('.', '_');
            }
            return name;
        }

        /// <summary>
        /// Returns a pretty name for the type in the style of one that you'd see
        /// in C#.
        /// </summary>
        /// <parparam name="includeNamespace">
        /// Should the name include namespaces?
        /// </parparam>
        public static string CSharpName(this Type type, bool includeNamespace)
        {
            // we special case some of the common type names
            if (type == typeof(void))
            {
                return "void";
            }
            if (type == typeof(int))
            {
                return "int";
            }
            if (type == typeof(float))
            {
                return "float";
            }
            if (type == typeof(bool))
            {
                return "bool";
            }
            if (type == typeof(double))
            {
                return "double";
            }
            if (type == typeof(string))
            {
                return "string";
            }

            // Generic parameter, ie, T in Okay<T> We special-case this logic
            // otherwise we will recurse on the T
            if (type.IsGenericParameter)
            {
                return type.ToString();
            }

            var name = "";

            var genericArguments = (IEnumerable<Type>)type.GetGenericArguments();
            if (type.IsNested)
            {
                name += type.DeclaringType.CSharpName() + ".";

                // The declaring type generic parameters are considered part of
                // the nested types generic parameters so we need to remove them,
                // otherwise it will get included again.
                //
                // Say we have type `class Parent<T> { class Child {} }` If we
                // did not do the removal, then we would output
                // Parent<T>.Child<T>, but we really want to output
                // Parent<T>.Child
                if (type.DeclaringType.GetGenericArguments().Length > 0)
                {
                    genericArguments = genericArguments.Skip(type.DeclaringType.GetGenericArguments().Length);
                }
            }

            if (genericArguments.Any() == false)
            {
                name += type.Name;
            }
            else
            {
                var genericsTic = type.Name.IndexOf('`');
                if (genericsTic > 0)
                {
                    name += type.Name.Substring(0, genericsTic);
                }
                name += "<" + String.Join(",", genericArguments.Select(t => CSharpName(t, includeNamespace)).ToArray()) + ">";
            }

            if (includeNamespace && type.Namespace != null)
            {
                name = type.Namespace + "." + name;
            }

            return name;
        }
    }
}
