using System.Collections;
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dynamic.ScriptHandling;

/// <summary>
/// A holder class for a script. This is used so we can cache a list of these, and iterate over them, even when
/// the underlying script changes.
/// </summary>
public interface IReadOnlyScriptContainer : IEnumerable<ScriptContainer>, IDeepCloneable
{
    /// <summary>
    /// Whether this container is empty.
    /// </summary>
    public bool IsEmpty { get; }

    /// <summary>
    /// The script in this container.
    /// </summary>
    public Script? Script { get; }
}

/// <inheritdoc cref="IReadOnlyScriptContainer"/>
public class ScriptContainer : IReadOnlyScriptContainer
{
    /// <inheritdoc cref="ScriptContainer"/>
    public ScriptContainer()
    {
    }

    /// <inheritdoc cref="ScriptContainer"/>
    public ScriptContainer(Script script)
    {
        Script = script;
    }

    /// <inheritdoc />
    [MemberNotNullWhen(false, nameof(ScriptHandling.Script))]
    public bool IsEmpty => Script is null;

    /// <inheritdoc />
    public Script? Script { get; private set; }

    /// <inheritdoc />
    public IEnumerator<ScriptContainer> GetEnumerator()
    {
        yield return this;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    /// <summary>
    /// Assigns a new script to this container. If there was a script already, it is removed.
    /// </summary>
    /// <param name="script"></param>
    public void Set(Script script)
    {
        Script?.OnRemove();
        Script = script;
    }

    /// <summary>
    /// Removes the script from this container.
    /// </summary>
    public Script? Clear()
    {
        Script?.OnRemove();
        var script = Script;
        Script = null;
        return script;
    }

    /// <summary>
    /// Removes the script from this container, but does not call <see cref="Script.OnRemove"/>.
    /// Be very careful with this, as it can lead to unexpected behavior. An example of a valid use is Baton-Pass,
    /// where scripts are being removed to be added to another Pokemon, so we want them to remain active.
    /// </summary>
    public void ClearWithoutRemoving()
    {
        Script = null;
    }
}