package utils

// Description: Usefull IO functions.

import (
	"bufio"
	"crypto/md5"
	"encoding/hex"
	"io"
	"strings"
	"syscall"
)

// Banner creates a formatted, banner with a heading and a message.
// It can be printed to raise attention to a message
func Banner(heading, msg string) string {
	nsep := 80 - len(heading) - 2
	return "\n" + sep(nsep/2) + " " + heading + " " + sep(nsep/2) +
		sep(nsep%2) + "\n" + msg + "\n" + sep(80)
}

func sep(nlen int) string { return strings.Repeat("-", nlen) }

// StripCommentsAndTokenize reads from the provided io.Reader, strips comments
// (lines starting with '#'), and tokenizes the remaining lines into words
// which are returned as a slice of strings.
func StripCommentsAndTokenize(r io.Reader) (tokens []string) {
	for scanner := bufio.NewScanner(r); scanner.Scan(); {
		line := scanner.Text()
		commentIndex := strings.Index(line, "#")
		if commentIndex != -1 {
			line = line[:commentIndex]
		}
		tokens = append(tokens, strings.Fields(line)...)
	}
	return
}

// GetKernelVersion returns the kernel version as a string.
// It is only tested on Linux and panics if the syscall fails.
func GetKernelVersion() string {
	var (
		uname syscall.Utsname
		i     int
		s     [65]byte
	)
	handleOSError(syscall.Uname(&uname))
	for ; uname.Release[i] != 0; i++ {
		s[i] = byte(uname.Release[i])
	}
	return string(s[:i])
}

// IsMD5SumMatch returns true if the MD5sum of the file with the given filename
// matches the given MD5sum. It panics if the file cannot be opened.
func IsMD5SumMatch(MD5sum, filename string) bool {
	f := Open(filename)
	defer f.Close()
	return MD5sum == hex.EncodeToString(MD5Sum(f))
}

// MD5Sum returns the MD5 checksum of the data read from the provided io.Reader
// as a byte slice. It panics if the read operation fails.
func MD5Sum(r io.Reader) []byte {
	hasher := md5.New()
	Copy(r, hasher)
	return hasher.Sum(nil)
}

// PromptUser writes a message to `w` and waits for a 'y' or 'n' response given
// by the user from `r`. It returns true if the user responds with 'y' or 'Y',
// and false otherwise.
func PromptUser(w *bufio.Writer, r io.Reader, prompt string) bool {
	w.WriteString(prompt + " [y/N]: ")
	w.Flush()
	b := ReadChar(r)
	for c := b; c != '\n'; { // consume until newline
		c = ReadChar(r)
	}
	return b == 'y' || b == 'Y'
}

// ReadWordsFromFile returns all words from the file with the given filename
// after stripping comments (lines starting with '#') as a slice of strings.
// It panics if the file cannot be opened.
func ReadWordsFromFile(filename string) []string {
	f := Open(filename)
	defer f.Close()
	return StripCommentsAndTokenize(f)
}

// Count returns the number of true values in the provided boolean slice.
func Count(booleans ...bool) (count int) {
	for _, b := range booleans {
		if b {
			count++
		}
	}
	return
}

func Join(s []string) string { return strings.Join(s, " ") }
