using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using InABox.Core;
using Syncfusion.Windows.Shared;

namespace InABox.DynamicGrid
{
    public static class DynamicEditorControlFactory
    {

        private static Dictionary<Type, List<(Type valueType, Type editor)>>? _editors;

        private static Dictionary<Type, List<(Type valueType, Type editor)>> GetEditors()
        {
            if(_editors is null)
            {
                _editors = [];
                foreach(var type in CoreUtils.TypeList(AppDomain.CurrentDomain.GetAssemblies(),
                    x => x.IsClass
                        && !x.IsAbstract
                        && !x.IsGenericType))
                {
                    Type? valueType = null;
                    Type? editor = null;
                    if(type.GetSuperclassDefinition(typeof(DynamicEditorControl<,>)) is Type editorClass)
                    {
                        valueType = editorClass.GenericTypeArguments[0];
                        editor = editorClass.GenericTypeArguments[1];
                    }
                    else if(type.GetSuperclassDefinition(typeof(DynamicEnclosedEditorControl<,>)) is Type enclosedEditorClass)
                    {
                        valueType = enclosedEditorClass.GenericTypeArguments[0];
                        editor = enclosedEditorClass.GenericTypeArguments[1];
                    }
                    if (editor is not null && valueType is not null && (editor == typeof(IBaseEditor) || editor.HasInterface<IBaseEditor>()))
                    {
                        _editors.GetValueOrAdd(editor).Add((valueType, type));
                    }
                }
            }
            return _editors;
        }

        private static bool GetControlType(Type editorType, Type valueType, [NotNullWhen(true)] out Type? TControl)
        {
            var controls = GetEditors().Where(x => editorType == x.Key || editorType.IsAssignableTo(x.Key));

            Type? editType = null;
            List<(Type valueType, Type editor)>? tControls = null;

            TControl = null;
            foreach(var control in controls)
            {
                if(control.Key == editorType)
                {
                    tControls = control.Value;
                    break;
                }
                if(TControl is null || editType!.IsAssignableTo(control.Key))
                {
                    (editType, tControls) = control;
                }
            }

            if (tControls is null) return false;

            Type? _valueType = null;
            foreach(var control in tControls)
            {
                if(control.valueType == valueType)
                {
                    TControl = control.editor;
                    return true;
                }
                if(valueType.IsAssignableTo(control.valueType)
                    && (_valueType is null || _valueType.IsAssignableTo(control.valueType)))
                {
                    (_valueType, TControl) = control;
                }
            }

            return TControl is not null;
        }

        public static BaseDynamicEditorControl? CreateControl(BaseEditor editor, Type valueType, IDynamicEditorHost host) 
        {
            if (GetControlType(editor.GetType(), valueType, out var TControl))
            {
                var result = Activator.CreateInstance(TControl) as BaseDynamicEditorControl;
                if (result != null)
                {
                    result.ValueType = valueType;
                    result.Host = host;
                    result.EditorDefinition = editor;
                }

                return result;
            }

            return null;
        }
        
    }
}