// Package terminal provides VT100 terminal codes and a windows
// implementation of that.
package terminal

import (
	"io"
	"os"
	"runtime"
	"sync"

	colorable "github.com/mattn/go-colorable"
	"golang.org/x/crypto/ssh/terminal"
)

// VT100 codes
const (
	EraseLine         = "\x1b[2K"
	MoveToStartOfLine = "\x1b[1G"
	MoveUp            = "\x1b[1A"

	Reset      = "\x1b[0m"
	Bright     = "\x1b[1m"
	Dim        = "\x1b[2m"
	Underscore = "\x1b[4m"
	Blink      = "\x1b[5m"
	Reverse    = "\x1b[7m"
	Hidden     = "\x1b[8m"

	BlackFg   = "\x1b[30m"
	RedFg     = "\x1b[31m"
	GreenFg   = "\x1b[32m"
	YellowFg  = "\x1b[33m"
	BlueFg    = "\x1b[34m"
	MagentaFg = "\x1b[35m"
	CyanFg    = "\x1b[36m"
	WhiteFg   = "\x1b[37m"

	BlackBg   = "\x1b[40m"
	RedBg     = "\x1b[41m"
	GreenBg   = "\x1b[42m"
	YellowBg  = "\x1b[43m"
	BlueBg    = "\x1b[44m"
	MagentaBg = "\x1b[45m"
	CyanBg    = "\x1b[46m"
	WhiteBg   = "\x1b[47m"

	HiBlackFg   = "\x1b[90m"
	HiRedFg     = "\x1b[91m"
	HiGreenFg   = "\x1b[92m"
	HiYellowFg  = "\x1b[93m"
	HiBlueFg    = "\x1b[94m"
	HiMagentaFg = "\x1b[95m"
	HiCyanFg    = "\x1b[96m"
	HiWhiteFg   = "\x1b[97m"

	HiBlackBg   = "\x1b[100m"
	HiRedBg     = "\x1b[101m"
	HiGreenBg   = "\x1b[102m"
	HiYellowBg  = "\x1b[103m"
	HiBlueBg    = "\x1b[104m"
	HiMagentaBg = "\x1b[105m"
	HiCyanBg    = "\x1b[106m"
	HiWhiteBg   = "\x1b[107m"
)

var (
	// make sure that start is only called once
	once sync.Once
)

// Start the terminal - must be called before use
func Start() {
	once.Do(func() {
		f := os.Stdout
		if !terminal.IsTerminal(int(f.Fd())) {
			// If stdout not a tty then remove escape codes
			Out = colorable.NewNonColorable(f)
		} else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" {
			// If TERM is set just use stdout
			Out = f
		} else {
			Out = colorable.NewColorable(f)
		}
	})
}

// WriteString writes the string passed in to the terminal
func WriteString(s string) {
	Write([]byte(s))
}

// GetSize reads the dimensions of the current terminal or returns a
// sensible default
func GetSize() (w, h int) {
	w, h, err := terminal.GetSize(int(os.Stdout.Fd()))
	if err != nil {
		w, h = 80, 25
	}
	return w, h
}

// Out is an io.Writer which can be used to write to the terminal
// eg for use with fmt.Fprintf(terminal.Out, "terminal fun: %d\n", n)
var Out io.Writer

// Write sends out to the VT100 terminal.
// It will initialise the terminal if this is the first call.
func Write(out []byte) {
	Start()
	_, _ = Out.Write(out)
}
