//go:build windows
// +build windows

package termenv

import (
	"fmt"
	"os"
	"strconv"

	"golang.org/x/sys/windows"
)

func (o *Output) ColorProfile() Profile {
	if !o.isTTY() {
		return Ascii
	}

	if o.environ.Getenv("ConEmuANSI") == "ON" {
		return TrueColor
	}

	winVersion, _, buildNumber := windows.RtlGetNtVersionNumbers()
	if buildNumber < 10586 || winVersion < 10 {
		// No ANSI support before Windows 10 build 10586.
		if o.environ.Getenv("ANSICON") != "" {
			conVersion := o.environ.Getenv("ANSICON_VER")
			cv, err := strconv.ParseInt(conVersion, 10, 64)
			if err != nil || cv < 181 {
				// No 8 bit color support before v1.81 release.
				return ANSI
			}

			return ANSI256
		}

		return Ascii
	}
	if buildNumber < 14931 {
		// No true color support before build 14931.
		return ANSI256
	}

	return TrueColor
}

func (o Output) foregroundColor() Color {
	// default gray
	return ANSIColor(7)
}

func (o Output) backgroundColor() Color {
	// default black
	return ANSIColor(0)
}

// EnableWindowsANSIConsole enables virtual terminal processing on Windows
// platforms. This allows the use of ANSI escape sequences in Windows console
// applications. Ensure this gets called before anything gets rendered with
// termenv.
//
// Returns the original console mode and an error if one occurred.
func EnableWindowsANSIConsole() (uint32, error) {
	handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
	if err != nil {
		return 0, err
	}

	var mode uint32
	err = windows.GetConsoleMode(handle, &mode)
	if err != nil {
		return 0, err
	}

	// See https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
	if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
		vtpmode := mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
		if err := windows.SetConsoleMode(handle, vtpmode); err != nil {
			return 0, err
		}
	}

	return mode, nil
}

// RestoreWindowsConsole restores the console mode to a previous state.
func RestoreWindowsConsole(mode uint32) error {
	handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
	if err != nil {
		return err
	}

	return windows.SetConsoleMode(handle, mode)
}

// EnableVirtualTerminalProcessing enables virtual terminal processing on
// Windows for o and returns a function that restores o to its previous state.
// On non-Windows platforms, or if o does not refer to a terminal, then it
// returns a non-nil no-op function and no error.
func EnableVirtualTerminalProcessing(o *Output) (restoreFunc func() error, err error) {
	// There is nothing to restore until we set the console mode.
	restoreFunc = func() error {
		return nil
	}

	// If o is not a tty, then there is nothing to do.
	tty, ok := o.Writer().(*os.File)
	if tty == nil || !ok {
		return
	}

	// Get the current console mode. If there is an error, assume that o is not
	// a terminal, discard the error, and return.
	var mode uint32
	if err2 := windows.GetConsoleMode(windows.Handle(tty.Fd()), &mode); err2 != nil {
		return
	}

	// If virtual terminal processing is already set, then there is nothing to
	// do and nothing to restore.
	if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING {
		return
	}

	// Enable virtual terminal processing. See
	// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
	if err2 := windows.SetConsoleMode(windows.Handle(tty.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err2 != nil {
		err = fmt.Errorf("windows.SetConsoleMode: %w", err2)
		return
	}

	// Set the restore function. We maintain a reference to the tty in the
	// closure (rather than just its handle) to ensure that the tty is not
	// closed by a finalizer.
	restoreFunc = func() error {
		return windows.SetConsoleMode(windows.Handle(tty.Fd()), mode)
	}

	return
}
