package cpu

import (
	"fmt"
	"sync/atomic"
	"time"
)

var (
	cores   uint64
	maxFreq uint64
	quota   float64
	usage   uint64

	preSystem uint64
	preTotal  uint64
)

func init() {
	cpus, err := perCPUUsage()
	if err != nil {
		panic(fmt.Errorf("stat/sys/cpu: perCPUUsage() failed!err:=%v", err))
	}
	cores = uint64(len(cpus))

	sets, err := cpuSets()
	if err != nil {
		panic(fmt.Errorf("stat/sys/cpu: cpuSets() failed!err:=%v", err))
	}
	quota = float64(len(sets))
	cq, err := cpuQuota()
	if err == nil {
		if cq != -1 {
			var period uint64
			if period, err = cpuPeriod(); err != nil {
				panic(fmt.Errorf("stat/sys/cpu: cpuPeriod() failed!err:=%v", err))
			}
			limit := float64(cq) / float64(period)
			if limit < quota {
				quota = limit
			}
		}
	}
	maxFreq = cpuMaxFreq()

	preSystem, err = systemCPUUsage()
	if err != nil {
		panic(fmt.Errorf("sys/cpu: systemCPUUsage() failed!err:=%v", err))
	}
	preTotal, err = totalCPUUsage()
	if err != nil {
		panic(fmt.Errorf("sys/cpu: totalCPUUsage() failed!err:=%v", err))
	}

	go func() {
		ticker := time.NewTicker(time.Millisecond * 250)
		defer ticker.Stop()
		for {
			<-ticker.C
			cpu := refreshCPU()
			if cpu != 0 {
				atomic.StoreUint64(&usage, cpu)
			}
		}
	}()
}

func refreshCPU() (u uint64) {
	total, err := totalCPUUsage()
	if err != nil {
		return
	}
	system, err := systemCPUUsage()
	if err != nil {
		return
	}
	if system != preSystem {
		u = uint64(float64((total-preTotal)*cores*1e3) / (float64(system-preSystem) * quota))
	}
	preSystem = system
	preTotal = total
	return u
}

// Stat cpu stat.
type Stat struct {
	Usage uint64 // cpu use ratio.
}

// Info cpu info.
type Info struct {
	Frequency uint64
	Quota     float64
}

// ReadStat read cpu stat.
func ReadStat(stat *Stat) {
	stat.Usage = atomic.LoadUint64(&usage)
}

// GetInfo get cpu info.
func GetInfo() Info {
	return Info{
		Frequency: maxFreq,
		Quota:     quota,
	}
}