#nullable enable


namespace TagFighter.Effects.Steps
{
    using System.Collections.Generic;
    using System.Reflection;
    using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;

    [StepType(StepTypeAttribute.SetterStep)]
    public class ContextResourceSet : OutputStep<bool>
    {
        [UnityEngine.SerializeField]
        SinglePort<double> _in = new();

        [CareBoo.Serially.TypeFilter(derivedFrom: typeof(Resources.Resource<>))]
        public CareBoo.Serially.SerializableType? Resource;

        [CareBoo.Serially.TypeFilter(derivedFrom: typeof(ContextRegister<>))]
        public CareBoo.Serially.SerializableType? Register;

        public override IEnumerable<IPort> Inputs {
            get {
                yield return _in;
            }
        }

        public override string ToString() {
            var resourceName = Resource == null || Resource.Type == null ? "*" : Resource.Type.Name;
            var registerName = Register == null || Register.Type == null ? "*" : Register.Type.Name;

            return $"Context.Set.{resourceName}.{registerName}";
        }

        public override bool IsValid {
            get {
                return Register != null && Register.Type != null && Resource != null && Resource.Type != null;
            }
        }

        public override bool Run(EffectInput Data) {
            if (!IsValid || Resource == null || Register == null) {
                UnityEngine.Debug.LogError($"{this} Inner types are null");
                return false;
            }

            var getSpecificMethod = typeof(ContextResourceSet).GetMethod(nameof(SetSpecific), BindingFlags.NonPublic | BindingFlags.Instance).
                                            MakeGenericMethod(Resource.Type, Resource.Type.BaseType.GetGenericArguments()[0], Register.Type);
            var value = getSpecificMethod.Invoke(this, new object[] { Data });

            return (bool)value;

        }

        bool SetSpecific<TResource, TUnitType, TContextRegister>(EffectInput blackBoard)
            where TResource : Resources.Resource<TUnitType>
            where TUnitType : Resources.IUnitType
            where TContextRegister : IContextRegister {

            var result = (_in.Node != null) ? _in.Node.Run(blackBoard) : default;

            var success = false;
            if (blackBoard != null) {
                blackBoard.Context.SetResource<TResource, TUnitType, TContextRegister>((Resources.Unit<TUnitType>)result);
                success = true;
            }

            return success;
        }

        public override EffectStepNodeData ToData() {
            var effectStepNodeData = base.ToData();
            effectStepNodeData.Resource = Resource;
            effectStepNodeData.Register = Register;
            return effectStepNodeData;
        }

        public override bool FromData(EffectStepNodeData effectStepNodeData, Dictionary<string, EffectStepNode> guidToNode) {
            base.FromData(effectStepNodeData, guidToNode);
            Resource = effectStepNodeData.Resource;
            Register = effectStepNodeData.Register;
            return true;
        }
    }
}