#nullable enable


namespace TagFighter.Effects.Steps
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;

    public enum PortCapacity
    {
        None = 0,
        Single = 1,
        Multi = 2,
    }
    public class EffectStepValidatedEventArgs : EventArgs
    {
        public string DisplayName;
        public bool IsValid;
        public EffectStepValidatedEventArgs(string displayName, bool isValid) {
            DisplayName = displayName;
            IsValid = isValid;
        }
    }

    /// <summary>
    /// <see cref="FoldStep"/>              Input: Single Sequence<Double>  Output: Double
    /// <see cref="ZipStep"/>               Input: Multi  Sequence<Double>  Output: Sequence<Double>
    /// <see cref="RepeatStep"/>            Input: Single Double            Output: Sequence<Double>
    /// <see cref="OperatorStep"/>          Input: Multi  Double            Output: Double
    /// 
    /// <see cref="ConstValue"/>        Input: None Output: Double
    /// <see cref="ContextResourceGet"/>     Input: None Output: Double
    /// <see cref="PawnResourceGet"/>        Input: None Output: Sequence<Double>
    /// 
    /// <see cref="ContextResourceSet"/>     Input: Single Double
    /// <see cref="PawnResourceSet"/>        Input: Single Sequence<Double>
    /// <see cref="ConeAreaGet"/>           Input: 2xDouble
    /// </summary>
    ///

    [System.Serializable]
    public class EffectStepNodeData
    {
        public CareBoo.Serially.SerializableType SerializedType;
        public string Guid;
        public Vector2 Position;
        public List<PortData> PortsData;

        public List<double>? Const;

        [UnityEngine.SerializeReference]
        public IResourceOperator? Operator;
        public CareBoo.Serially.SerializableType? Resource;

        public CareBoo.Serially.SerializableType? Register;
        public string? DisplayName;

        public EffectStepNodeData(System.Type type, string guid, Vector2 position, List<PortData> portsData) {
            SerializedType = new(type);
            Guid = guid;
            Position = position;
            PortsData = portsData;
        }

    }
    public interface IEffectStepNodeData
    {
        public EffectStepNodeData ToData();
        public bool FromData(EffectStepNodeData effectStepNodeData, Dictionary<string, EffectStepNode> guidToNode);

    }

    public abstract class EffectStepNode : ScriptableObject, IEffectStepNodeData
    {
        public Vector2 Position;
        public string Guid = System.Guid.NewGuid().ToString();
        public event EventHandler<EffectStepValidatedEventArgs>? EffectStepValidated;

        public abstract IEnumerable<IPort> Inputs { get; }

        public abstract bool IsValid { get; }
        protected virtual void OnValidate() {
            OnValidated(this, new(ToString(), IsValid));
        }
        protected virtual void OnValidated(object sender, EffectStepValidatedEventArgs e) {
            EffectStepValidated?.Invoke(sender, e);
        }
        public virtual EffectStepNodeData ToData() {
            return new(GetType(), Guid, Position, Inputs.Select(input => input.ToData()).ToList());
        }

        public virtual bool FromData(EffectStepNodeData effectStepNodeData, Dictionary<string, EffectStepNode> guidToNode) {
            name = effectStepNodeData.SerializedType.Type.Name;
            Guid = effectStepNodeData.Guid;
            Position = effectStepNodeData.Position;
            foreach (var (input, data) in Inputs.Zip(effectStepNodeData.PortsData, (input, data) => (input, data))) {
                input.FromData(data, guidToNode);
            }

            return true;
        }
    }

    [System.AttributeUsage(System.AttributeTargets.Class, Inherited = false)
    ]
    public class StepTypeAttribute : System.Attribute
    {
        public string Name;

        public StepTypeAttribute(string name) {
            Name = name;
        }

        public override string ToString() => Name;

        public const string OperatorStep = "Operator";
        public const string GetterStep = "Get";
        public const string SetterStep = "Set";
        public const string TesterStep = "Test";
        public const string ReleaseStep = "Release";
    }

    public abstract class OutputStep<T> : EffectStepNode
    {
        public abstract T Run(EffectInput blackBoard);
    }

    public abstract class ReleaseStep : EffectStepNode
    {
        public abstract void Run(EffectInput blackBoard);
    }

}