// Copied from https://github.com/spectreconsole/spectre.console/blob/d79e6adc5f8e637fb35c88f987023ffda6707243/src/Spectre.Console/Internal/Backends/Ansi/AnsiDetector.cs
// MIT License - Copyright(c) 2020 Patrik Svensson, Phil Scott, Nils Andresen
// which is partially based on https://github.com/keqingrong/supports-ansi/blob/master/index.js
// <auto-generated/>

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Windows.Win32;
using Windows.Win32.System.Console;

namespace Spectre.Console;

internal static class AnsiDetector
{
    private static readonly Regex[] _regexes =
    [
        new("^xterm"), // xterm, PuTTY, Mintty
        new("^rxvt"), // RXVT
        new("^eterm"), // Eterm
        new("^screen"), // GNU screen, tmux
        new("tmux"), // tmux
        new("^vt100"), // DEC VT series
        new("^vt102"), // DEC VT series
        new("^vt220"), // DEC VT series
        new("^vt320"), // DEC VT series
        new("ansi"), // ANSI
        new("scoansi"), // SCO ANSI
        new("cygwin"), // Cygwin, MinGW
        new("linux"), // Linux console
        new("konsole"), // Konsole
        new("bvterm"), // Bitvise SSH Client
        new("^st-256color"), // Suckless Simple Terminal, st
        new("alacritty"), // Alacritty
    ];

    public static (bool SupportsAnsi, bool LegacyConsole) Detect(bool stdError, bool upgrade)
    {
        // Running on Windows?
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            // Running under ConEmu?
            var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
            if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
            {
                return (true, false);
            }

            var supportsAnsi = WindowsSupportsAnsi(upgrade, stdError, out var legacyConsole);
            return (supportsAnsi, legacyConsole);
        }

        return DetectFromTerm();
    }

    private static (bool SupportsAnsi, bool LegacyConsole) DetectFromTerm()
    {
        // Check if the terminal is of type ANSI/VT100/xterm compatible.
        var term = Environment.GetEnvironmentVariable("TERM");
        if (!string.IsNullOrWhiteSpace(term))
        {
            if (_regexes.Any(regex => regex.IsMatch(term)))
            {
                return (true, false);
            }
        }

        return (false, true);
    }

    private static bool WindowsSupportsAnsi(bool upgrade, bool stdError, out bool isLegacy)
    {
        isLegacy = false;

        try
        {
            var @out = PInvoke.GetStdHandle_SafeHandle(stdError ? STD_HANDLE.STD_ERROR_HANDLE :STD_HANDLE.STD_OUTPUT_HANDLE);

            if (!PInvoke.GetConsoleMode(@out, out var mode))
            {
                // Could not get console mode, try TERM (set in cygwin, WSL-Shell).
                var (ansiFromTerm, legacyFromTerm) = DetectFromTerm();

                isLegacy = ansiFromTerm ? legacyFromTerm : isLegacy;
                return ansiFromTerm;
            }

            if ((mode & CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0||true)
            {
                isLegacy = true;

                if (!upgrade)
                {
                    return false;
                }

                // Try enable ANSI support.
                mode |= CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.DISABLE_NEWLINE_AUTO_RETURN;
                if (!PInvoke.SetConsoleMode(@out, mode))
                {
                    // Enabling failed.
                    return false;
                }

                isLegacy = false;
            }

            return true;
        }
        catch
        {
            // All we know here is that we don't support ANSI.
            return false;
        }
    }
}
