/*           INFINITY CODE          */
/*     https://infinity-code.com    */

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using HarmonyLib;
using InfinityCode.UltimateEditorEnhancer.UnityTypes;
using UnityEditor;
using UnityEngine;

namespace InfinityCode.UltimateEditorEnhancer.Interceptors
{
    public abstract class Interceptor
    {
        protected static Harmony harmony;
        protected MethodInfo patch;

        protected virtual InitType initType
        {
            get => InitType.immediately;
        }

        protected abstract MethodInfo originalMethod { get; }

        protected virtual string prefixMethodName
        {
            get => null;
        }

        protected virtual string postfixMethodName
        {
            get => null;
        }

        protected virtual void Init()
        {

        }

        [InitializeOnLoadMethod]
        private static async void InitInterceptors()
        {
            TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom<Interceptor>();
            List<Interceptor> phaseOne = new List<Interceptor>();
            List<Interceptor> phaseTwo = new List<Interceptor>();

            harmony = new Harmony("InfinityCode.UltimateEditorEnhancer");

            foreach (Type type in types)
            {
                if (type.IsAbstract) continue;
                Interceptor interceptor = Activator.CreateInstance(type) as Interceptor;
                interceptor.Init();
                if (interceptor.initType == InitType.immediately) phaseOne.Add(interceptor);
                else phaseTwo.Add(interceptor);
            }

            foreach (Interceptor interceptor in phaseOne)
            {
                try
                {
                    interceptor.Patch();
                }
                catch
                {
                    
                }
            }

            while (GUISkinRef.GetCurrent() == null) await Task.Delay(1);

            Debug.unityLogger.logEnabled = false;
            foreach (Interceptor interceptor in phaseTwo)
            {
                try
                {
                    interceptor.Patch();
                }
                catch
                {

                }
            }
            Debug.unityLogger.logEnabled = true;
        }

        protected virtual void Patch()
        {
            if (patch != null || originalMethod == null) return; 

            try
            {
                HarmonyMethod prefix = null;
                HarmonyMethod postfix = null;

                if (!string.IsNullOrEmpty(prefixMethodName))
                {
                    MethodInfo prefixMethod = AccessTools.Method(GetType(), prefixMethodName);
                    if (prefixMethod != null) prefix = new HarmonyMethod(prefixMethod);
                }

                if (!string.IsNullOrEmpty(postfixMethodName))
                {
                    MethodInfo postfixMethod = AccessTools.Method(GetType(), postfixMethodName);
                    if (postfixMethod != null) postfix = new HarmonyMethod(postfixMethod);
                }

                patch = harmony.Patch(originalMethod, prefix, postfix);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }
        }

        protected virtual void Unpatch()
        {
            if (harmony == null || originalMethod == null || patch == null) return;

            if (!string.IsNullOrEmpty(prefixMethodName)) harmony.Unpatch(originalMethod, AccessTools.Method(GetType(), prefixMethodName));
            if (!string.IsNullOrEmpty(postfixMethodName)) harmony.Unpatch(originalMethod, AccessTools.Method(GetType(), postfixMethodName));
            patch = null;
        }

        protected enum InitType
        {
            immediately,
            gui
        }
    }
}