// +build linux darwin unix

package staros

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os/user"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var clockTicks = 100 // default value

// StartTime 开机时间
func StartTime() time.Time {
	tmp, _ := readAsString("/proc/stat")
	data := splitBy(ReplaceByte9(tmp), " ")
	btime, _ := strconv.ParseInt(strings.TrimSpace(data["btime"]), 10, 64)
	return time.Unix(btime, 0)
}

// IsRoot 当前是否是管理员用户
func IsRoot() bool {
	uid, _ := user.Current()
	if uid.Uid == "0" {
		return true
	}
	return false
}

func Whoami() (uid, gid int, uname, gname, home string, err error) {
	var me *user.User
	var gup *user.Group
	me, err = user.Current()
	if err != nil {
		return
	}
	uid, _ = strconv.Atoi(me.Uid)
	gid, _ = strconv.Atoi(me.Uid)
	home = me.HomeDir
	uname = me.Username
	gup, err = user.LookupGroupId(me.Gid)
	if err != nil {
		return
	}
	gname = gup.Name
	return
}

func getCPUSample() (idle, total uint64) {
	contents, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		return
	}
	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		fields := strings.Fields(line)
		if fields[0] == "cpu" {
			numFields := len(fields)
			for i := 1; i < numFields; i++ {
				val, err := strconv.ParseUint(fields[i], 10, 64)
				if err != nil {
					fmt.Println("Error: ", i, fields[i], err)
				}
				total += val          // tally up all the numbers to get total ticks
				if i == 4 || i == 5 { // idle is the 5th field in the cpu line
					idle += val
				}
			}
			return
		}
	}
	return
}
func splitProcStat(content []byte) []string {
	nameStart := bytes.IndexByte(content, '(')
	nameEnd := bytes.LastIndexByte(content, ')')
	restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') '
	name := content[nameStart+1 : nameEnd]
	pid := strings.TrimSpace(string(content[:nameStart]))
	fields := make([]string, 3, len(restFields)+3)
	fields[1] = string(pid)
	fields[2] = string(name)
	fields = append(fields, restFields...)
	return fields
}

func getCPUSampleByPid(pid int) float64 {
	contents, err := ioutil.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat")
	if err != nil {
		return 0
	}
	fields := splitProcStat(contents)
	utime, err := strconv.ParseFloat(fields[14], 64)
	if err != nil {
		return 0
	}

	stime, err := strconv.ParseFloat(fields[15], 64)
	if err != nil {
		return 0
	}

	// There is no such thing as iotime in stat file.  As an approximation, we
	// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
	// docs).  Note: I am assuming at least Linux 2.6.18
	var iotime float64
	if len(fields) > 42 {
		iotime, err = strconv.ParseFloat(fields[42], 64)
		if err != nil {
			iotime = 0 // Ancient linux version, most likely
		}
	} else {
		iotime = 0 // e.g. SmartOS containers
	}
	return utime/float64(clockTicks) + stime/float64(clockTicks) + iotime/float64(clockTicks)
}
func CpuUsageByPid(pid int, sleep time.Duration) float64 {
	total1 := getCPUSampleByPid(pid)
	time.Sleep(sleep)
	total2 := getCPUSampleByPid(pid)
	return (total2 - total1) / sleep.Seconds() * 100
}

// CpuUsage 获取CPU使用量
func CpuUsage(sleep time.Duration) float64 {
	idle0, total0 := getCPUSample()
	time.Sleep(sleep)
	idle1, total1 := getCPUSample()
	idleTicks := float64(idle1 - idle0)
	totalTicks := float64(total1 - total0)
	cpuUsage := 100 * (totalTicks - idleTicks) / totalTicks
	return cpuUsage
	//fmt.Printf("CPU usage is %f%% [busy: %f, total: %f]\n", cpuUsage, totalTicks-idleTicks, totalTicks)
}

func DiskUsage(path string) (disk DiskStatus) {
	fs := syscall.Statfs_t{}
	err := syscall.Statfs(path, &fs)
	if err != nil {
		return
	}
	disk.All = fs.Blocks * uint64(fs.Bsize)
	disk.Free = fs.Bfree * uint64(fs.Bsize)
	disk.Available = fs.Bavail * uint64(fs.Bsize)
	disk.Used = disk.All - disk.Free
	return
}
