using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.ILCore;
using FishNet.CodeGenerating.Processing;
using FishNet.CodeGenerating.Processing.Rpc;
using MonoFN.Cecil;
using System.Collections.Generic;
using System.Linq;
using Unity.CompilationPipeline.Common.Diagnostics;
#if !UNITY_2020_1_OR_NEWER
using UnityEngine;
#endif
using SR = System.Reflection;


namespace FishNet.CodeGenerating
{

    internal class CodegenSession
    {
        /// <summary>
        /// Current module for this session.
        /// </summary>
        internal ModuleDefinition Module;
        /// <summary>
        /// Outputs errors when codegen fails.
        /// </summary>
        internal List<DiagnosticMessage> Diagnostics;
        /// <summary>
        /// SyncVars that are being accessed from an assembly other than the currently being processed one.
        /// </summary>
        internal List<FieldDefinition> DifferentAssemblySyncVars = new List<FieldDefinition>();


        /// <summary>
        /// CodegenBase classes for processing a module.
        /// </summary>
        private List<CodegenBase> _bases;
        /// <summary>
        /// Quick lookup of base classes.
        /// </summary>
        private Dictionary<string, CodegenBase> _basesCache = new Dictionary<string, CodegenBase>();

        /// <summary>
        /// Returns class of type if found within CodegenBase classes.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        internal T GetClass<T>() where T : CodegenBase
        {
            string tName = typeof(T).Name;
            return (T)_basesCache[tName];
        }
        /// <summary>
        /// Resets all helpers while importing any information needed by them.
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        internal bool Initialize(ModuleDefinition module)
        {
            Module = module;
            Diagnostics = new List<DiagnosticMessage>();

            _bases = new List<CodegenBase>()
                {
                    new ReaderImports(), new ReaderProcessor()
                    ,new WriterImports(), new WriterProcessor()
                    , new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper()
                    , new ObjectHelper(), new NetworkBehaviourHelper()
                    , new CreatedSyncVarGenerator(), new TransportHelper()
                    , new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper()
                    , new CustomSerializerProcessor()
                    , new NetworkBehaviourProcessor()
                    , new QolAttributeProcessor()
                    , new RpcProcessor()
                    , new NetworkBehaviourSyncProcessor()
                    , new PredictionProcessor()
                };

            //Add all to dictionary first, then import.
            foreach (CodegenBase item in _bases)
            {
                string tName = item.GetType().Name;
                _basesCache.Add(tName, item);
            }

            //Initialize.
            foreach (CodegenBase item in _bases)
            {
                item.Initialize(this);
                if (!item.ImportReferences())
                    return false;
            }

            return true;
        }




        #region Logging.
        /// <summary>
        /// Logs a warning.
        /// </summary>
        /// <param name="msg"></param>
        internal void LogWarning(string msg)
        {
#if UNITY_2020_1_OR_NEWER
            Diagnostics.AddWarning(msg);
#else
            Debug.LogWarning(msg);
#endif
        }
        /// <summary>
        /// Logs an error.
        /// </summary>
        /// <param name="msg"></param>
        internal void LogError(string msg)
        {
#if UNITY_2020_1_OR_NEWER
            Diagnostics.AddError(msg);
#else
            Debug.LogError(msg);
#endif
        }
        #endregion

        #region ImportReference.

        public MethodReference ImportReference(SR.MethodBase method)
        {
            return Module.ImportReference(method);
        }

        public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context)
        {
            return Module.ImportReference(method, context);
        }

        public TypeReference ImportReference(TypeReference type)
        {
            return Module.ImportReference(type);
        }

        public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context)
        {
            return Module.ImportReference(type, context);
        }

        public FieldReference ImportReference(FieldReference field)
        {
            return Module.ImportReference(field);
        }

        public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context)
        {
            return Module.ImportReference(field, context);
        }
        public MethodReference ImportReference(MethodReference method)
        {
            return Module.ImportReference(method);
        }

        public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context)
        {
            return Module.ImportReference(method, context);
        }
        public TypeReference ImportReference(System.Type type)
        {
            return ImportReference(type, null);
        }


        public TypeReference ImportReference(System.Type type, IGenericParameterProvider context)
        {
            return Module.ImportReference(type, context);
        }


        public FieldReference ImportReference(SR.FieldInfo field)
        {
            return Module.ImportReference(field);
        }

        public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context)
        {
            return Module.ImportReference(field, context);
        }

        #endregion

    }


}