﻿// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.CodeAnalysis.CSharp.Test.Utilities
{
    /// <summary>
    /// Represents the source code used for a C# test. Allows us to have single helpers that enable all the different ways
    /// we typically provide source in testing.
    /// </summary>
    public readonly struct CSharpTestSource
    {
        public static CSharpTestSource None => new CSharpTestSource(null);

        public object Value { get; }

        private CSharpTestSource(object value)
        {
            Value = value;
        }

        public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourceFileName = "")
        {
            switch (Value)
            {
                case string source:
                    return new[] { CSharpTestBase.Parse(source, filename: sourceFileName, parseOptions) };
                case string[] sources:
                    Debug.Assert(string.IsNullOrEmpty(sourceFileName));
                    return CSharpTestBase.Parse(parseOptions, sources);
                case (string source, string fileName):
                    Debug.Assert(string.IsNullOrEmpty(sourceFileName));
                    return new[] { CSharpTestBase.Parse(source, fileName, parseOptions) };
                case (string Source, string FileName)[] sources:
                    Debug.Assert(string.IsNullOrEmpty(sourceFileName));
                    return sources.Select(source => CSharpTestBase.Parse(source.Source, source.FileName, parseOptions)).ToArray();
                case SyntaxTree tree:
                    Debug.Assert(parseOptions == null);
                    Debug.Assert(string.IsNullOrEmpty(sourceFileName));
                    return new[] { tree };
                case SyntaxTree[] trees:
                    Debug.Assert(parseOptions == null);
                    Debug.Assert(string.IsNullOrEmpty(sourceFileName));
                    return trees;
                case CSharpTestSource[] testSources:
                    return testSources.SelectMany(s => s.GetSyntaxTrees(parseOptions, sourceFileName)).ToArray();
                case null:
                    return Array.Empty<SyntaxTree>();
                default:
                    throw new Exception($"Unexpected value: {Value}");
            }
        }

        public static implicit operator CSharpTestSource(string source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource(string[] source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource((string Source, string FileName) source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource((string Source, string FileName)[] source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource(SyntaxTree source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource(SyntaxTree[] source) => new CSharpTestSource(source);
        public static implicit operator CSharpTestSource(List<SyntaxTree> source) => new CSharpTestSource(source.ToArray());
        public static implicit operator CSharpTestSource(ImmutableArray<SyntaxTree> source) => new CSharpTestSource(source.ToArray());
        public static implicit operator CSharpTestSource(CSharpTestSource[] source) => new CSharpTestSource(source);
    }
}
