package cgroup

import (
	"path"
)

const maxMemory = 1 << 62

type MemoryStat struct {
	RSS   uint64
	Cache uint64
	Limit uint64
}

func (cg *Cgroup) MemoryStat() (*MemoryStat, error) {
	if cg.Version == V1 {
		return cg.memoryStatV1()
	}
	return cg.memoryStatV2()
}

func (cg *Cgroup) memoryStatV1() (*MemoryStat, error) {
	vars, err := readVariablesFromFile(path.Join(cgRoot, "memory", cg.subsystems["memory"], "memory.stat"))
	if err != nil {
		return nil, err
	}
	limit, err := readUintFromFile(path.Join(cgRoot, "memory", cg.subsystems["memory"], "memory.limit_in_bytes"))
	if err != nil {
		return nil, err
	}
	if limit > maxMemory {
		limit = 0
	}
	// Note from https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt:
	// Only anonymous and swap cache memory is listed as part of 'rss' stat.
	//	This should not be confused with the true 'resident set size' or the
	//	amount of physical memory used by the cgroup.
	//	'rss + mapped_file" will give you resident set size of cgroup.
	//	(Note: file and shmem may be shared among other cgroups. In that case,
	//	 mapped_file is accounted only when the memory cgroup is owner of page
	//	 cache.)
	return &MemoryStat{
		RSS:   vars["rss"] + vars["mapped_file"],
		Cache: vars["cache"],
		Limit: limit,
	}, nil
}

func (cg *Cgroup) memoryStatV2() (*MemoryStat, error) {
	current, err := readUintFromFile(path.Join(cgRoot, cg.subsystems[""], "memory.current"))
	if err != nil {
		return nil, err
	}
	vars, err := readVariablesFromFile(path.Join(cgRoot, cg.subsystems[""], "memory.stat"))
	if err != nil {
		return nil, err
	}
	limit, _ := readUintFromFile(path.Join(cgRoot, cg.subsystems[""], "memory.max"))
	return &MemoryStat{
		RSS:   current - vars["file"],
		Cache: vars["file"],
		Limit: limit,
	}, nil
}
