package cgroup

import (
	"fmt"
	"io/ioutil"
	"path"
	"strconv"
	"strings"
)

type CPUStat struct {
	UsageSeconds         float64
	ThrottledTimeSeconds float64
	LimitCores           float64
}

func (cg Cgroup) CpuStat() (*CPUStat, error) {
	if cg.Version == V1 {
		return cg.cpuStatV1()
	}
	return cg.cpuStatV2()
}

func (cg Cgroup) cpuStatV1() (*CPUStat, error) {
	throttling, err := readVariablesFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.stat"))
	if err != nil {
		return nil, err
	}
	usageNs, err := readIntFromFile(path.Join(cgRoot, "cpuacct", cg.subsystems["cpuacct"], "cpuacct.usage"))
	if err != nil {
		return nil, err
	}
	periodUs, err := readIntFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.cfs_period_us"))
	if err != nil {
		return nil, err
	}
	quotaUs, err := readIntFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.cfs_quota_us"))
	if err != nil {
		return nil, err
	}
	res := &CPUStat{
		UsageSeconds:         float64(usageNs) / 1e9,
		ThrottledTimeSeconds: float64(throttling["throttled_time"]) / 1e9,
	}
	if quotaUs > 0 {
		res.LimitCores = float64(quotaUs) / float64(periodUs)
	}
	return res, nil
}

func (cg Cgroup) cpuStatV2() (*CPUStat, error) {
	vars, err := readVariablesFromFile(path.Join(cgRoot, cg.subsystems[""], "cpu.stat"))
	if err != nil {
		return nil, err
	}
	res := &CPUStat{
		UsageSeconds:         float64(vars["usage_usec"]) / 1e6,
		ThrottledTimeSeconds: float64(vars["throttled_usec"]) / 1e6,
	}
	payload, err := ioutil.ReadFile(path.Join(cgRoot, cg.subsystems[""], "cpu.max"))
	if err != nil {
		return nil, err
	}
	data := strings.TrimSpace(string(payload))
	parts := strings.Fields(data)
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid cpu.max payload: %s", data)
	}
	if parts[0] == "max" { //no limit
		return res, nil
	}
	quotaUs, err := strconv.ParseUint(parts[0], 10, 64)
	if err != nil {
		return nil, fmt.Errorf("invalid quota value in cpu.max: %s", parts[0])
	}
	periodUs, err := strconv.ParseUint(parts[1], 10, 64)
	if err != nil {
		return nil, fmt.Errorf("invalid period value in cpu.max: %s", parts[1])
	}
	if periodUs > 0 {
		res.LimitCores = float64(quotaUs) / float64(periodUs)
	}
	return res, nil
}
