using PixiEditor.ChangeableDocument.Changeables.Animations;
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
using PixiEditor.ChangeableDocument.Changeables.Interfaces;
using PixiEditor.ChangeableDocument.ChangeInfos.Animation;
using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;

namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;

internal class DeleteNode_Change : Change
{
    public Guid NodeId { get; set; }

    private ConnectionsData originalConnections;

    private Node savedCopy;

    private GroupKeyFrame? savedKeyFrameGroup;

    [GenerateMakeChangeAction]
    public DeleteNode_Change(Guid nodeId)
    {
        NodeId = nodeId;
    }

    public override bool InitializeAndValidate(Document target)
    {
        Node node = target.FindNode<Node>(NodeId);

        if (node is null || target.NodeGraph.OutputNode == node)
            return false;

        originalConnections = NodeOperations.CreateConnectionsData(node);

        savedCopy = node.Clone();
        if (savedCopy is IPairNode pairNode)
        {
            pairNode.OtherNode = (node as IPairNode).OtherNode;
        }

        savedKeyFrameGroup = CloneGroupKeyFrame(target, NodeId);

        return true;
    }

    public static GroupKeyFrame? CloneGroupKeyFrame(Document target, Guid id)
    {
        GroupKeyFrame group = target.AnimationData.KeyFrames.FirstOrDefault(x => x.NodeId == id) as GroupKeyFrame;
        if (group is null)
            return null;
        return group.Clone() as GroupKeyFrame;
    }

    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
        out bool ignoreInUndo)
    {
        ignoreInUndo = false;
        var node = target.FindNode<Node>(NodeId);

        List<IChangeInfo> changes = NodeOperations.DetachNode(target.NodeGraph, node);

        target.NodeGraph.RemoveNode(node);

        changes.Add(new DeleteNode_ChangeInfo(NodeId));

        if (savedKeyFrameGroup != null)
        {
            target.AnimationData.RemoveKeyFrame(savedKeyFrameGroup.Id);
            changes.Add(new DeleteKeyFrame_ChangeInfo(savedKeyFrameGroup.Id));
        }

        return changes;
    }

    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document doc)
    {
        var copy = savedCopy!.Clone();
        copy.Id = NodeId;
        
        if (copy is IPairNode pairNode)
        {
            pairNode.OtherNode = (savedCopy as IPairNode).OtherNode;
        }

        doc.NodeGraph.AddNode(copy);

        List<IChangeInfo> changes = new();

        IChangeInfo createChange = CreateNode_ChangeInfo.CreateFromNode(copy);

        changes.Add(createChange);

        changes.AddRange(NodeOperations.ConnectStructureNodeProperties(originalConnections, copy, doc.NodeGraph));

        RevertKeyFrames(doc, savedKeyFrameGroup, changes);

        return changes;
    }

    public static void RevertKeyFrames(Document doc, GroupKeyFrame savedKeyFrameGroup, List<IChangeInfo> changes)
    {
        if (savedKeyFrameGroup != null)
        {
            var cloned = savedKeyFrameGroup.Clone();
            doc.AnimationData.AddKeyFrame(cloned);
            foreach (var keyFrame in savedKeyFrameGroup.Children)
            {
                changes.Add(new CreateRasterKeyFrame_ChangeInfo(keyFrame.NodeId, keyFrame.StartFrame, keyFrame.Id,
                    false));
                changes.Add(new KeyFrameLength_ChangeInfo(keyFrame.Id, keyFrame.StartFrame, keyFrame.Duration));
            }

            changes.Add(new KeyFrameVisibility_ChangeInfo(savedKeyFrameGroup.Id, savedKeyFrameGroup.IsVisible));
        }
    }

    public override void Dispose()
    {
        savedCopy?.Dispose();
    }
}
