﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Collections.Generic;
using Microsoft.CodeAnalysis;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel
{
    internal class CodeModelEventQueue
    {
        private readonly Queue<CodeModelEvent> _eventQueue;

        public CodeModelEventQueue(Queue<CodeModelEvent> eventQueue)
            => _eventQueue = eventQueue;

        private void EnqueueEvent(CodeModelEvent @event)
        {
            // Don't bother with events that are already queued.
            foreach (var queuedEvent in _eventQueue)
            {
                if (queuedEvent.Equals(@event))
                {
                    return;
                }
            }

            // Events are added to the end of the queue, so we only check the
            // last event to see if we can combine it with the new event. The
            // other events will be for prior edits, and should remain distinct.
            // In order to combine the events, they must both be for the same node, 
            // and they must both be change events.

            if (_eventQueue.Count > 0)
            {
                var priorEvent = _eventQueue.Peek();
                if (priorEvent.Node == @event.Node &&
                    priorEvent.ParentNode == @event.ParentNode &&
                    priorEvent.Type.IsChange() &&
                    @event.Type.IsChange())
                {
                    priorEvent.Type |= @event.Type;
                    return;
                }
            }

            _eventQueue.Enqueue(@event);
        }

        public void EnqueueAddEvent(SyntaxNode node, SyntaxNode parent)
            => EnqueueEvent(new CodeModelEvent(node, parent, CodeModelEventType.Add));

        public void EnqueueRemoveEvent(SyntaxNode node, SyntaxNode parent)
            => EnqueueEvent(new CodeModelEvent(node, parent, CodeModelEventType.Remove));

        public void EnqueueChangeEvent(SyntaxNode node, SyntaxNode parent, CodeModelEventType eventType)
            => EnqueueEvent(new CodeModelEvent(node, parent, eventType));

        public void Discard()
            => _eventQueue.Dequeue();

        public int Count
        {
            get { return _eventQueue.Count; }
        }
    }
}
