// 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 System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Formatting
{
    internal partial class TriviaDataFactory
    {
        /// <summary>   
        /// represents a general trivia between two tokens. slightly more expensive than others since it
        /// needs to calculate stuff unlike other cases
        /// </summary>
        private class ComplexTrivia : AbstractComplexTrivia
        {
            public ComplexTrivia(SyntaxFormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2)
                : base(options, treeInfo, token1, token2)
            {
            }

            protected override void ExtractLineAndSpace(string text, out int lines, out int spaces)
                => text.ProcessTextBetweenTokens(this.TreeInfo, this.Token1, this.Options.TabSize, out lines, out spaces);

            protected override TriviaData CreateComplexTrivia(int line, int space)
                => CreateModifiedComplexTrivia(line, space);

            protected override TriviaData CreateComplexTrivia(int line, int space, int indentation)
                => CreateModifiedComplexTrivia(line, space);

            private TriviaData CreateModifiedComplexTrivia(int line, int space)
                => new ModifiedComplexTrivia(this.Options, this, line, space);

            protected override TriviaDataWithList Format(
                FormattingContext context, ChainedFormattingRules formattingRules, int lines, int spaces, CancellationToken cancellationToken)
            {
                return new FormattedComplexTrivia(context, formattingRules, this.Token1, this.Token2, lines, spaces, this.OriginalString, cancellationToken);
            }

            protected override bool ContainsSkippedTokensOrText(TriviaList list)
                => CodeShapeAnalyzer.ContainsSkippedTokensOrText(list);

            private bool ShouldFormat(FormattingContext context)
            {
                var commonToken1 = this.Token1;
                var commonToken2 = this.Token2;

                var formatSpanEnd = commonToken2.Kind() == SyntaxKind.None ? commonToken1.Span.End : commonToken2.Span.Start;
                var span = TextSpan.FromBounds(commonToken1.Span.End, formatSpanEnd);
                if (context.IsSpacingSuppressed(span, TreatAsElastic))
                {
                    return false;
                }

                var triviaList = new TriviaList(commonToken1.TrailingTrivia, commonToken2.LeadingTrivia);
                Contract.ThrowIfFalse(triviaList.Count > 0);

                // okay, now, check whether we need or are able to format noisy tokens
                if (ContainsSkippedTokensOrText(triviaList))
                {
                    return false;
                }

                if (!this.SecondTokenIsFirstTokenOnLine)
                {
                    return CodeShapeAnalyzer.ShouldFormatSingleLine(triviaList);
                }

                Debug.Assert(this.SecondTokenIsFirstTokenOnLine);

                if (Options.UseTabs)
                {
                    return true;
                }

                var firstTriviaInTree = this.Token1.Kind() == SyntaxKind.None;

                return CodeShapeAnalyzer.ShouldFormatMultiLine(context, firstTriviaInTree, triviaList);
            }

            public override void Format(
                FormattingContext context,
                ChainedFormattingRules formattingRules,
                Action<int, TokenStream, TriviaData> formattingResultApplier,
                CancellationToken cancellationToken,
                int tokenPairIndex = TokenPairIndexNotNeeded)
            {
                if (!ShouldFormat(context))
                {
                    return;
                }

                formattingResultApplier(tokenPairIndex, context.TokenStream, Format(context, formattingRules, this.LineBreaks, this.Spaces, cancellationToken));
            }

            public override SyntaxTriviaList GetTriviaList(CancellationToken cancellationToken)
                => throw new NotImplementedException();

            public override IEnumerable<TextChange> GetTextChanges(TextSpan span)
                => throw new NotImplementedException();
        }
    }
}
