﻿// 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.

using System;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis.Wrapping
{
    /// <summary>
    /// Represents an edit between two tokens.  Specifically, provides the new trailing trivia for
    /// the <see cref="Left"/> token and the new leading trivia for the <see
    /// cref="Right"/> token.
    /// </summary>
    internal readonly struct Edit
    {
        public readonly SyntaxToken Left;
        public readonly SyntaxToken Right;
        public readonly SyntaxTriviaList NewLeftTrailingTrivia;
        public readonly SyntaxTriviaList NewRightLeadingTrivia;

        private Edit(
            SyntaxToken left, SyntaxTriviaList newLeftTrailingTrivia,
            SyntaxToken right, SyntaxTriviaList newRightLeadingTrivia)
        {
            if (left.Span.End > right.Span.Start)
                throw new InvalidEditException(left, right);

            Left = left;
            Right = right;
            NewLeftTrailingTrivia = newLeftTrailingTrivia;
            NewRightLeadingTrivia = newRightLeadingTrivia;
        }

        public string GetNewTrivia()
        {
            var result = PooledStringBuilder.GetInstance();
            AppendTrivia(result, NewLeftTrailingTrivia);
            AppendTrivia(result, NewRightLeadingTrivia);
            return result.ToStringAndFree();
        }

        private static void AppendTrivia(PooledStringBuilder result, SyntaxTriviaList triviaList)
        {
            foreach (var trivia in triviaList)
            {
                result.Builder.Append(trivia.ToFullString());
            }
        }

        /// <summary>
        /// Create the Edit representing the deletion of all trivia between left and right.
        /// </summary>
        public static Edit DeleteBetween(SyntaxNodeOrToken left, SyntaxNodeOrToken right)
            => UpdateBetween(left, default, default(SyntaxTriviaList), right);

        public static Edit UpdateBetween(
            SyntaxNodeOrToken left, SyntaxTriviaList leftTrailingTrivia,
            SyntaxTrivia rightLeadingTrivia, SyntaxNodeOrToken right)
        {
            return UpdateBetween(left, leftTrailingTrivia, new SyntaxTriviaList(rightLeadingTrivia), right);
        }

        public static Edit UpdateBetween(
            SyntaxNodeOrToken left, SyntaxTriviaList leftTrailingTrivia,
            SyntaxTriviaList rightLeadingTrivia, SyntaxNodeOrToken right)
        {
            var leftLastToken = left.IsToken ? left.AsToken() : left.AsNode()!.GetLastToken();
            var rightFirstToken = right.IsToken ? right.AsToken() : right.AsNode()!.GetFirstToken();
            return new Edit(leftLastToken, leftTrailingTrivia, rightFirstToken, rightLeadingTrivia);
        }

        private sealed class InvalidEditException(SyntaxToken left, SyntaxToken right) : Exception($"Left token had an end '{left.Span.End}' past the start of right token '{right.Span.Start}'")
        {
            // Used for analyzing dumps
#pragma warning disable IDE0052 // Remove unread private members
            private readonly SyntaxTree? _tree = left.SyntaxTree;
            private readonly SyntaxToken _left = left;
            private readonly SyntaxToken _right = right;
        }
    }
}
