package profile

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/utils"
)

const (
	TIME_ELEMENT_ACTION = iota
	TIME_ELEMENT_TASK
	TIME_ELEMENT_TIMER
	TIME_ELEMENT_NET
	TIME_ELEMENT_COMMAND
	TIME_ELEMENT_MODULE
	TIME_ELEMENT_JOB
)

var TimeStatisticMgr = &timeStatisticMgr{
	elements: make(map[string]*TimeElement),
}

type TimeElement struct {
	Name        string
	ElementType int
	Times       int64
	TotalTick   int64
	MaxTick     int64
	MinTick     int64
}

type timeStatisticMgr struct {
	elements map[string]*TimeElement
	l        sync.RWMutex
}

func (this *timeStatisticMgr) WatchStart(name string, elementype int) basic.IStatsWatch {
	tw := newTimeWatcher(name, elementype)
	return tw
}

func (this *timeStatisticMgr) addStatistic(name string, elementype int, d int64) {
	this.l.Lock()
	te, exist := this.elements[name]
	if !exist {
		te = &TimeElement{
			Name:        name,
			ElementType: elementype,
			Times:       1,
			TotalTick:   d,
			MaxTick:     d,
			MinTick:     d,
		}
		this.elements[name] = te
		this.l.Unlock()
		return
	}
	te.Times++
	te.TotalTick += d
	if d < te.MinTick {
		te.MinTick = d
	}
	if d > te.MaxTick {
		te.MaxTick = d
		if Config.SlowMS > 0 && d >= int64(Config.SlowMS)*int64(time.Millisecond) {
			this.l.Unlock()
			logger.Logger.Warnf("###slow timespan name: %s  take:%s avg used:%s", strings.ToLower(te.Name), utils.ToS(time.Duration(d)), utils.ToS(time.Duration(te.TotalTick/te.Times)))
			return
		}
	}
	this.l.Unlock()
}

func (this *timeStatisticMgr) GetStats() map[string]TimeElement {
	elements := make(map[string]TimeElement)
	this.l.RLock()
	for k, v := range this.elements {
		te := *v
		te.TotalTick /= int64(time.Millisecond)
		te.MinTick /= int64(time.Millisecond)
		te.MaxTick /= int64(time.Millisecond)
		elements[k] = te
	}
	this.l.RUnlock()
	return elements
}

func (this *timeStatisticMgr) dump(w io.Writer) {
	elements := make(map[string]*TimeElement)
	this.l.RLock()
	for k, v := range this.elements {
		elements[k] = v
	}
	this.l.RUnlock()
	fmt.Fprintf(w, "| % -30s| % -10s | % -16s | % -16s | % -16s | % -16s |\n", "name", "times", "used", "max used", "min used", "avg used")
	for k, v := range elements {
		fmt.Fprintf(w, "| % -30s| % -10d | % -16s | % -16s | % -16s | % -16s |\n", strings.ToLower(k), v.Times, utils.ToS(time.Duration(v.TotalTick)), utils.ToS(time.Duration(v.MaxTick)), utils.ToS(time.Duration(v.MinTick)), utils.ToS(time.Duration(int64(v.TotalTick)/v.Times)))
	}
}

func GetStats() map[string]TimeElement {
	return TimeStatisticMgr.GetStats()
}

func init() {
	basic.StatsWatchMgr = TimeStatisticMgr
}
