// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;

// CR: We could move MSBuildApp.ExitType out of MSBuildApp
using Microsoft.Build.Execution;
using Microsoft.Build.Shared;

#nullable disable

namespace Microsoft.Build.CommandLine
{
    /// <summary>
    /// This is the Out-Of-Proc Task Host for supporting Cross-Targeting tasks.
    /// </summary>
    /// <remarks>
    /// It will be responsible for:
    /// - Task execution
    /// - Communicating with the MSBuildApp process, specifically the TaskHostFactory
    ///   (Logging messages, receiving Tasks from TaskHostFactory, sending results and other messages)
    /// </remarks>
    public static class OutOfProcTaskHost
    {
        /// <summary>
        /// Enumeration of the various ways in which the MSBuildTaskHost.exe application can exit.
        /// </summary>
        internal enum ExitType
        {
            /// <summary>
            /// The application executed successfully.
            /// </summary>
            Success,

            /// <summary>
            /// We received a request from MSBuild.exe to terminate
            /// </summary>
            TerminateRequest,

            /// <summary>
            /// A logger aborted the build.
            /// </summary>
            LoggerAbort,

            /// <summary>
            /// A logger failed unexpectedly.
            /// </summary>
            LoggerFailure,

            /// <summary>
            /// The Task Host Node did not terminate gracefully
            /// </summary>
            TaskHostNodeFailed,

            /// <summary>
            /// An unexpected failure
            /// </summary>
            Unexpected
        }

        /// <summary>
        /// Main Entry Point
        /// </summary>
        /// <remarks>
        /// We won't execute any tasks in the main thread, so we don't need to be in an STA
        /// </remarks>
        [MTAThread]
        public static int Main()
        {
            int exitCode = Execute() == ExitType.Success ? 0 : 1;
            return exitCode;
        }

        /// <summary>
        /// Orchestrates the execution of the application.
        /// Also responsible for top-level error handling.
        /// </summary>
        /// <returns>
        /// A value of Success if the bootstrapping succeeds
        /// </returns>
        internal static ExitType Execute()
        {
            switch (Environment.GetEnvironmentVariable("MSBUILDDEBUGONSTART"))
            {
#if FEATURE_DEBUG_LAUNCH
                case "1":
                    Debugger.Launch();
                    break;
#endif
                case "2":
                    // Sometimes easier to attach rather than deal with JIT prompt
                    Process currentProcess = Process.GetCurrentProcess();
                    Console.WriteLine($"Waiting for debugger to attach ({currentProcess.MainModule.FileName} PID {currentProcess.Id}).  Press enter to continue...");
                    Console.ReadLine();
                    break;
            }

            bool restart = false;
            do
            {
                OutOfProcTaskHostNode oopTaskHostNode = new OutOfProcTaskHostNode();
                Exception taskHostShutDownException = null;
                NodeEngineShutdownReason taskHostShutDownReason = oopTaskHostNode.Run(out taskHostShutDownException);

                if (taskHostShutDownException != null)
                {
                    return ExitType.TaskHostNodeFailed;
                }

                switch (taskHostShutDownReason)
                {
                    case NodeEngineShutdownReason.BuildComplete:
                        return ExitType.Success;

                    case NodeEngineShutdownReason.BuildCompleteReuse:
                        restart = true;
                        break;

                    default:
                        return ExitType.TaskHostNodeFailed;
                }
            }
            while (restart);

            // Should not happen
            ErrorUtilities.ThrowInternalErrorUnreachable();
            return ExitType.Unexpected;
        }
    }
}
