package common

//
// gopsutil is a port of psutil(http://pythonhosted.org/psutil/).
// This covers these architectures.
//  - linux (amd64, arm)
//  - freebsd (amd64)
//  - windows (amd64)
import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
)

var (
	Timeout    = 3 * time.Second
	ErrTimeout = errors.New("command timed out")
)

type Invoker interface {
	Command(string, ...string) ([]byte, error)
	CommandWithContext(context.Context, string, ...string) ([]byte, error)
}

type Invoke struct{}

func ( Invoke) ( string,  ...string) ([]byte, error) {
	,  := context.WithTimeout(context.Background(), Timeout)
	defer ()
	return .CommandWithContext(, , ...)
}

func ( Invoke) ( context.Context,  string,  ...string) ([]byte, error) {
	 := exec.CommandContext(, , ...)

	var  bytes.Buffer
	.Stdout = &
	.Stderr = &

	if  := .Start();  != nil {
		return .Bytes(), 
	}

	if  := .Wait();  != nil {
		return .Bytes(), 
	}

	return .Bytes(), nil
}

type FakeInvoke struct {
	Suffix string // Suffix species expected file name suffix such as "fail"
	Error  error  // If Error specfied, return the error.
}

// Command in FakeInvoke returns from expected file if exists.
func ( FakeInvoke) ( string,  ...string) ([]byte, error) {
	if .Error != nil {
		return []byte{}, .Error
	}

	 := runtime.GOOS

	 := filepath.Base()

	 := strings.Join(append([]string{}, ...), "")
	 = url.QueryEscape()
	 := path.Join("testdata", , )
	if .Suffix != "" {
		 += "_" + .Suffix
	}
	if PathExists() {
		return os.ReadFile()
	}
	return []byte{}, fmt.Errorf("could not find testdata: %s", )
}

func ( FakeInvoke) ( context.Context,  string,  ...string) ([]byte, error) {
	return .Command(, ...)
}

var ErrNotImplementedError = errors.New("not implemented yet")

// ReadFile reads contents from a file
func ( string) (string, error) {
	,  := os.ReadFile()
	if  != nil {
		return "", 
	}

	return string(), nil
}

// ReadLines reads contents from a file and splits them by new lines.
// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
func ( string) ([]string, error) {
	return ReadLinesOffsetN(, 0, -1)
}

// ReadLines reads contents from file and splits them by new line.
// The offset tells at which line number to start.
// The count determines the number of lines to read (starting from offset):
//
//	n >= 0: at most n lines
//	n < 0: whole file
func ( string,  uint,  int) ([]string, error) {
	,  := os.Open()
	if  != nil {
		return []string{""}, 
	}
	defer func( *os.File) {
		 := .Close()
		if  != nil {
			log.Fatalln()
		}
	}()

	var  []string

	 := bufio.NewReader()
	for  := 0;  < +int() ||  < 0; ++ {
		,  := .ReadString('\n')
		if  != nil {
			break
		}
		if  < int() {
			continue
		}
		 = append(, strings.Trim(, "\n"))
	}

	return , nil
}

func ( []int8) string {
	 := make([]byte, len())
	 := -1
	for ,  := range  {
		if  == 0 {
			 = 
			break
		}
		[] = byte()
	}
	if  == -1 {
		 = len()
	}

	return string([0:])
}

func ( []uint8) string {
	 := make([]byte, len())
	 := -1
	for ,  := range  {
		if  == 0 {
			 = 
			break
		}
		[] = byte()
	}
	if  == -1 {
		 = len()
	}

	return string([0:])
}

func ( []byte) string {
	 := -1
	 := -1
	for ,  := range  {
		// skip left side null
		if  == -1 &&  == 0 {
			continue
		}
		if  == -1 {
			 = 
		}

		if  == 0 {
			break
		}
		 =  + 1
	}
	if  == -1 {
		return string()
	}
	return string([:])
}

// ReadInts reads contents from single line file and returns them as []int32.
func ( string) ([]int64, error) {
	,  := os.Open()
	if  != nil {
		return []int64{}, 
	}
	defer func( *os.File) {
		 := .Close()
		if  != nil {
			log.Fatalln()
		}
	}()

	var  []int64

	 := bufio.NewReader()

	// The int files that this is concerned with should only be one liners.
	,  := .ReadString('\n')
	if  != nil {
		return []int64{}, 
	}

	,  := strconv.ParseInt(strings.Trim(, "\n"), 10, 32)
	if  != nil {
		return []int64{}, 
	}
	 = append(, )

	return , nil
}

// Parse Hex to uint32 without error
func ( string) uint32 {
	,  := strconv.ParseUint(, 16, 32)
	return uint32()
}

// Parse to int32 without error
func mustParseInt32( string) int32 {
	,  := strconv.ParseInt(, 10, 32)
	return int32()
}

// Parse to uint64 without error
func mustParseUint64( string) uint64 {
	,  := strconv.ParseInt(, 10, 64)
	return uint64()
}

// Parse to Float64 without error
func mustParseFloat64( string) float64 {
	,  := strconv.ParseFloat(, 64)
	return 
}

// StringsHas checks the target string slice contains src or not
func ( []string,  string) bool {
	for ,  := range  {
		if strings.TrimSpace() ==  {
			return true
		}
	}
	return false
}

// StringsContains checks the src in any string of the target string slice
func ( []string,  string) bool {
	for ,  := range  {
		if strings.Contains(, ) {
			return true
		}
	}
	return false
}

// IntContains checks the src in any int of the target int slice.
func ( []int,  int) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}

// get struct attributes.
// This method is used only for debugging platform dependent code.
func attributes( interface{}) map[string]reflect.Type {
	 := reflect.TypeOf()
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}

	 := make(map[string]reflect.Type)
	if .Kind() != reflect.Struct {
		return nil
	}

	for  := 0;  < .NumField(); ++ {
		 := .Field()
		if !.Anonymous {
			[.Name] = .Type
		}
	}

	return 
}

func ( string) bool {
	if ,  := os.Stat();  == nil {
		return true
	}
	return false
}

// GetEnv retrieves the environment variable key. If it does not exist it returns the default.
func (,  string,  ...string) string {
	 := os.Getenv()
	if  == "" {
		 = 
	}

	switch len() {
	case 0:
		return 
	case 1:
		return filepath.Join(, [0])
	default:
		 := make([]string, len()+1)
		[0] = 
		copy([1:], )
		return filepath.Join(...)
	}
}

func ( ...string) string {
	return GetEnv("HOST_PROC", "/proc", ...)
}

func ( ...string) string {
	return GetEnv("HOST_SYS", "/sys", ...)
}

func ( ...string) string {
	return GetEnv("HOST_ETC", "/etc", ...)
}

func ( ...string) string {
	return GetEnv("HOST_VAR", "/var", ...)
}

func ( ...string) string {
	return GetEnv("HOST_RUN", "/run", ...)
}

func ( ...string) string {
	return GetEnv("HOST_DEV", "/dev", ...)
}

// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
// sysctl commands (see DoSysctrl).
func getSysctrlEnv( []string) []string {
	 := false
	for ,  := range  {
		if strings.HasPrefix(, "LC_ALL") {
			[] = "LC_ALL=C"
			 = true
		}
	}
	if ! {
		 = append(, "LC_ALL=C")
	}
	return 
}