//+build darwin

package staros

import (
	"encoding/binary"
	"fmt"
	"golang.org/x/sys/unix"
	"os/exec"
	"strconv"
	"strings"
	"unsafe"
)

// Memory 系统内存信息
func Memory() (MemStatus,error) {
	return darwinMemory()
}

type swapUsage struct {
	Total     uint64
	Avail     uint64
	Used      uint64
	Pagesize  int32
	Encrypted bool
}

func darwinMemory() (MemStatus, error) {
	var err error
	var res MemStatus
	vm_stat, err := exec.LookPath("vm_stat")
	if err != nil {
		return res, err
	}
	out, err := exec.Command(vm_stat).CombinedOutput()
	if err != nil {
		return res, err
	}
	totalString, err := unix.Sysctl("hw.memsize")
	if err != nil {
		return res, err
	}

	// unix.sysctl() helpfully assumes the result is a null-terminated string and
	// removes the last byte of the result if it's 0 :/
	totalString += "\x00"

	res.All = uint64(binary.LittleEndian.Uint64([]byte(totalString)))

	lines := strings.Split(string(out), "\n")
	pagesize := uint64(unix.Getpagesize())
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) < 2 {
			continue
		}
		key := strings.TrimSpace(fields[0])
		value := strings.Trim(fields[1], " .")
		switch key {
		case "Pages free":
			free, e := strconv.ParseUint(value, 10, 64)
			if e != nil {
				err = e
			}
			res.Free = free * pagesize
		case "Pages inactive":
			inactive, e := strconv.ParseUint(value, 10, 64)
			if e != nil {
				err = e
			}
			res.Available = inactive * pagesize
		case "Pages active":
			active, e := strconv.ParseUint(value, 10, 64)
			if e != nil {
				err = e
			}
			_ = active * pagesize
		case "Pages wired down":
			wired, e := strconv.ParseUint(value, 10, 64)
			if e != nil {
				err = e
			}
			_ = wired * pagesize
		}
	}
	res.Available += res.Free
	res.Used = res.All - res.Available
	//swap
	value, err := unix.SysctlRaw("vm.swapusage")
	if err != nil {
		return res, err
	}
	if len(value) != 32 {
		return res, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value))
	}
	swap := (*swapUsage)(unsafe.Pointer(&value[0]))
	res.SwapAll = swap.Total
	res.SwapUsed = swap.Used
	res.SwapFree = swap.Avail
	return res, err
}
