package module

import (
	"container/list"
	"time"

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

const (
	///module state
	ModuleStateInvalid int = iota
	ModuleStateInit
	ModuleStateRun
	ModuleStateShutdown
	ModuleStateWaitShutdown
	ModuleStateFini
	///other
	ModuleMaxCount = 1024
)

var (
	AppModule = newModuleMgr()
)

type ModuleEntity struct {
	lastTick     time.Time
	tickInterval time.Duration
	priority     int
	module       Module
	quited       bool
}

type PreloadModuleEntity struct {
	priority int
	module   PreloadModule
}

type ModuleMgr struct {
	*basic.Object
	modules       *list.List
	modulesByName map[string]*ModuleEntity
	preloadModule *list.List
	state         int
	waitShutAct   chan interface{}
	waitShutCnt   int
	waitShut      bool
	currTimeSec   int64
	currTimeNano  int64
	currTime      time.Time
}

func newModuleMgr() *ModuleMgr {
	mm := &ModuleMgr{
		modules:       list.New(),
		preloadModule: list.New(),
		modulesByName: make(map[string]*ModuleEntity),
		waitShutAct:   make(chan interface{}, ModuleMaxCount),
		state:         ModuleStateInvalid,
	}

	return mm
}

func (this *ModuleMgr) GetCurrTime() time.Time {
	return this.currTime
}

func (this *ModuleMgr) GetCurrTimeSec() int64 {
	return this.currTimeSec
}

func (this *ModuleMgr) GetCurrTimeNano() int64 {
	return this.currTimeNano
}

func (this *ModuleMgr) RegisteModule(m Module, tickInterval time.Duration, priority int) {
	logger.Logger.Infof("module [%16s] registe;interval=%v,priority=%v", m.ModuleName(), tickInterval, priority)
	mentiry := &ModuleEntity{
		lastTick:     time.Now(),
		tickInterval: tickInterval,
		priority:     priority,
		module:       m,
	}

	this.modulesByName[m.ModuleName()] = mentiry

	for e := this.modules.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*ModuleEntity); ok {
			if priority < me.priority {
				this.modules.InsertBefore(mentiry, e)
				return
			}
		}
	}
	this.modules.PushBack(mentiry)
}

func (this *ModuleMgr) UnregisteModule(m Module) {
	for e := this.modules.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*ModuleEntity); ok {
			if me.module == m {
				delete(this.modulesByName, m.ModuleName())
				this.modules.Remove(e)
				return
			}
		}
	}
}

func (this *ModuleMgr) RegistePreloadModule(m PreloadModule, priority int) {
	mentiry := &PreloadModuleEntity{
		priority: priority,
		module:   m,
	}

	for e := this.preloadModule.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*PreloadModuleEntity); ok {
			if priority < me.priority {
				this.preloadModule.InsertBefore(mentiry, e)
				return
			}
		}
	}
	this.preloadModule.PushBack(mentiry)
}

func (this *ModuleMgr) UnregistePreloadModule(m PreloadModule) {
	for e := this.preloadModule.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*PreloadModuleEntity); ok {
			if me.module == m {
				this.preloadModule.Remove(e)
				return
			}
		}
	}
}

func (this *ModuleMgr) Start() *utils.Waitor {
	logger.Logger.Info("Startup PreloadModules")
	for e := this.preloadModule.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*PreloadModuleEntity); ok {
			me.module.Start()
		}
	}
	logger.Logger.Info("Startup PreloadModules [ok]")

	this.Object = basic.NewObject(core.ObjId_CoreId,
		"core",
		Config.Options,
		this)
	this.UserData = this
	core.LaunchChild(this.Object)
	core.AppCtx.CoreObj = this.Object
	this.state = ModuleStateInit
	//给模块预留调度的空间，防止主线程直接跑过去
	select {
	case <-time.After(time.Second):
	}
	return this.Object.Waitor
}

func (this *ModuleMgr) Close() {
	this.state = ModuleStateShutdown
}

func (this *ModuleMgr) init() {
	logger.Logger.Info("Start Initialize Modules")
	defer logger.Logger.Info("Start Initialize Modules [ok]")
	for e := this.modules.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*ModuleEntity); ok && !me.quited {
			logger.Logger.Infof("module [%16s] init...", me.module.ModuleName())
			me.safeInit()
			logger.Logger.Infof("module [%16s] init[ok]", me.module.ModuleName())
		}
	}
	this.state = ModuleStateRun
}

func (this *ModuleMgr) update() {
	nowTime := time.Now()
	this.currTime = nowTime
	this.currTimeSec = nowTime.Unix()
	this.currTimeNano = nowTime.UnixNano()
	for e := this.modules.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*ModuleEntity); ok && !me.quited {
			me.safeUpt(nowTime)
		}
	}
}

func (this *ModuleMgr) shutdown() {
	if this.waitShut {
		return
	}
	logger.Logger.Info("ModuleMgr shutdown()")
	this.waitShut = true
	this.state = ModuleStateWaitShutdown
	for e := this.modules.Front(); e != nil; e = e.Next() {
		if me, ok := e.Value.(*ModuleEntity); ok {
			logger.Logger.Infof("module [%16s] shutdown...", me.module.ModuleName())
			me.safeShutdown(this.waitShutAct)
			logger.Logger.Infof("module [%16s] shutdown[ok]", me.module.ModuleName())
			this.waitShutCnt++
		}
	}
}

func (this *ModuleMgr) checkShutdown() bool {
	select {
	case param := <-this.waitShutAct:
		logger.Logger.Infof("module [%16s] shutdowned", param)
		if name, ok := param.(string); ok {
			me := this.getModuleEntityByName(name)
			if me != nil && !me.quited {
				me.quited = true
				this.waitShutCnt--
			}
		}
	case _ = <-time.After(time.Second):
		logger.Logger.Trace("ModuleMgr.checkShutdown wait...")
		for e := this.modules.Front(); e != nil; e = e.Next() {
			if me, ok := e.Value.(*ModuleEntity); ok {
				if me.quited == false {
					logger.Logger.Infof("Module [%v] wait shutdown...", me.module.ModuleName())
				}
			}
		}
		//default:
	}
	if this.waitShutCnt == 0 {
		this.state = ModuleStateFini
		return true
	}
	this.update()
	return false
}

func (this *ModuleMgr) tick() {

	switch this.state {
	case ModuleStateInit:
		this.init()
	case ModuleStateRun:
		this.update()
	case ModuleStateShutdown:
		this.shutdown()
	case ModuleStateWaitShutdown:
		this.checkShutdown()
	case ModuleStateFini:
		this.fini()
	}
}

func (this *ModuleMgr) fini() {
	core.Terminate(this.Object)
	this.state = ModuleStateInvalid
	logger.Logger.Info("=============ModuleMgr fini=============")
	logger.Logger.Flush()
}

func (this *ModuleMgr) getModuleEntityByName(name string) *ModuleEntity {
	if me, exist := this.modulesByName[name]; exist {
		return me
	}
	return nil
}

func (this *ModuleMgr) GetModuleByName(name string) Module {
	if me, exist := this.modulesByName[name]; exist {
		return me.module
	}
	return nil
}

func (this *ModuleEntity) safeInit() {
	defer utils.DumpStackIfPanic("ModuleEntity.safeInit")
	this.module.Init()
}

func (this *ModuleEntity) safeUpt(nowTime time.Time) {
	defer utils.DumpStackIfPanic("ModuleEntity.safeTick")

	if this.tickInterval == 0 || nowTime.Sub(this.lastTick) >= this.tickInterval {
		this.lastTick = nowTime
		watch := profile.TimeStatisticMgr.WatchStart(fmt.Sprintf("/module/%v/update", this.module.ModuleName()), profile.TIME_ELEMENT_MODULE)
		if watch != nil {
			defer watch.Stop()
		}
		this.module.Update()
	}
}

func (this *ModuleEntity) safeShutdown(shutWaitAck chan<- interface{}) {
	defer utils.DumpStackIfPanic("ModuleEntity.safeShutdown")
	this.module.Shutdown()
}

func (this *ModuleMgr) OnStart() {}
func (this *ModuleMgr) OnStop()  {}
func (this *ModuleMgr) OnTick() {
	this.tick()
}

func RegistePreloadModule(m PreloadModule, priority int) {
	AppModule.RegistePreloadModule(m, priority)
}

func RegisteModule(m Module, tickInterval time.Duration, priority int) {
	AppModule.RegisteModule(m, tickInterval, priority)
}

func UnregisteModule(m Module) {
	AppModule.waitShutAct <- m.ModuleName()
}

func Start() *utils.Waitor {
	err := core.ExecuteHook(core.HOOK_BEFORE_START)
	if err != nil {
		logger.Logger.Error("ExecuteHook(HOOK_BEFORE_START) error", err)
	}
	return AppModule.Start()
}

func Stop() {
	AppModule.Close()
	err := core.ExecuteHook(core.HOOK_AFTER_STOP)
	if err != nil {
		logger.Logger.Error("ExecuteHook(HOOK_BEFORE_START) error", err)
	}
}
