package basic

import (
	"container/list"
	"sync"
	"sync/atomic"
	"time"

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

const (
	DefaultQueueBacklog int = 4
)

var (
// Waitor = utils.NewWaitor()
)

//	 Base class for need alone goroutine objects
//	 that easy to start and when to exit the unified management
//		Feature.
//			establish a tree structure between objects
//			asynchronous message queue
type Object struct {
	*utils.Waitor
	sync.RWMutex
	//  Identify
	Id int

	//  Name
	Name string

	//  True if termination was already initiated. If so, we can destroy
	//  the object if there are no more child objects or pending term acks.
	terminating bool

	//  True if termination was already finished.
	terminated bool

	//  Sequence number of the last command sent to this object.
	sentSeqnum uint32

	//  Sequence number of the last command processed by this object.
	processedSeqnum uint32

	//  Number of events we have to get before we can destroy the object.
	termAcks int

	//  List of all objects owned by this object. We are responsible
	//  for deallocating them before we quit.
	childs *container.SynchronizedMap

	//  Socket owning this object. It's responsible for shutting down
	//  this object.
	owner *Object

	//	Command queue
	que *list.List

	//	Configuration Options
	opt Options

	//	Currently resides goroutine id. I do not know how get it.
	gid int

	//
	waitActive chan struct{}
	//
	waitEnlarge chan struct{}

	//	UserData
	UserData interface{}
	//
	sinker Sinker
	//
	timer *time.Ticker
	//object local storage
	ols [OLS_MAX_SLOT]interface{}
	//
	recvCmdCnt int64
	//
	sendCmdCnt int64
	//
	cond *Cond
}

func NewObject(id int, name string, opt Options, sinker Sinker) *Object {
	o := &Object{
		Id:          id,
		Name:        name,
		opt:         opt,
		sinker:      sinker,
		waitActive:  make(chan struct{}, 1),
		waitEnlarge: make(chan struct{}, 1),
		childs:      container.NewSynchronizedMap(),
		cond:        NewCond(1),
	}

	o.init()
	go func() {
		defer func() {
			if err := recover(); err != nil {
				logger.Logger.Error(o, "panic, o.ProcessCommand error=", err)
			}
		}()
		o.ProcessCommand()
	}()

	return o
}

func (o *Object) GetTreeName() string {
	name := o.Name
	parent := o.owner
	for parent != nil {
		name = parent.Name + "/" + name
		parent = parent.owner
	}
	return "/" + name
}

func (o *Object) init() {
	o.que = list.New()
}

// Active inner goroutine
func (o *Object) Active() {
	o.waitActive <- struct{}{}
}

// Launch the supplied object and become its owner.
func (o *Object) LaunchChild(c *Object) {
	if c == nil {
		return
	}

	if c.owner != nil {
		panic("An object can have only one parent node")
	}

	c.owner = o
	c.Waitor = o.Waitor
	c.Active()
	c.safeStart()

	//  Take ownership of the object.
	SendOwn(o, c)
}

// thread safe
func (o *Object) GetChildById(id int) *Object {
	c := o.childs.Get(id)
	if cc, ok := c.(*Object); ok {
		return cc
	}
	return nil
}

// When another owned object wants to send command to this object
// it calls this function to let it know it should not shut down
// before the command is delivered.
func (o *Object) incSeqnum() {
	atomic.AddUint32(&(o.sentSeqnum), 1)
}

// Special handler called after a command that requires a seqnum
// was processed. The implementation should catch up with its counter
// of processed commands here.
func (o *Object) ProcessSeqnum() {
	//  Catch up with counter of processed commands.
	o.processedSeqnum++

	//  We may have catched up and still have pending terms acks.
	o.checkTermAcks()
}

// Check whether all the peding term acks were delivered.
// If so, deallocate this object.
func (o *Object) checkTermAcks() {
	name := o.GetTreeName()
	logger.Logger.Debugf("(%v) object checkTermAcks terminating=%v processedSeqnum=%v sentSeqnum=%v termAcks=%v ", name, o.terminating, o.processedSeqnum, o.sentSeqnum, o.termAcks)
	if o.terminating && o.processedSeqnum == o.sentSeqnum && o.termAcks == 0 {

		//  Sanity check. There should be no active children at this point.

		//  The root object has nobody to confirm the termination to.
		//  Other nodes will confirm the termination to the owner.
		if o.owner != nil {
			logger.Logger.Debugf("(%v)->(%v) Object SendTermAck ", o.Name, o.owner.Name)
			SendTermAck(o.owner)
		}

		//  Deallocate the resources.
		o.processDestroy()
	}
}

// Ask owner object to terminate this object. It may take a while
// while actual termination is started. This function should not be
// called more than once.
func (o *Object) Terminate(s *Object) {
	//  If termination is already underway, there's no point
	//  in starting it anew.
	if o.terminating {
		return
	}

	name := o.GetTreeName()
	logger.Logger.Debugf("(%v) object Terminate ", name)
	//  As for the root of the ownership tree, there's noone to terminate it,
	//  so it has to terminate itself.
	if o.owner == nil {
		o.processTerm()
		return
	}

	//  If I am an owned object, I'll ask my owner to terminate me.
	SendTermReq(o.owner, o)
}

// Term handler is protocted rather than private so that it can
// be intercepted by the derived class. This is useful to add custom
// steps to the beginning of the termination process.
func (o *Object) processTerm() {
	//  Double termination should never happen.
	if o.terminating {
		return
	}

	//  Send termination request to all owned objects.
	cnt := 0
	childs := o.childs.Items()
	for _, c := range childs {
		if cc, ok := c.(*Object); ok && cc != nil {
			SendTerm(cc)
			cnt++
		}
	}
	o.termAcks += cnt

	name := o.GetTreeName()
	logger.Logger.Debugf("(%v) object processTerm, termAcks=%v", name, o.termAcks)

	o.safeStop()
	//  Start termination process and check whether by chance we cannot
	//  terminate immediately.
	o.terminating = true
	o.checkTermAcks()
}

// A place to hook in when phyicallal destruction of the object
// is to be delayed.
func (o *Object) processDestroy() {
	name := o.GetTreeName()
	logger.Logger.Debugf("(%v) object processDestroy ", name)
	o.terminated = true
	//clear ols
	o.OlsClrValue()
}

func (o *Object) GetPendingCommandCnt() int {
	o.RLock()
	cnt := o.que.Len()
	o.RUnlock()
	return cnt
}

// Enqueue command
func (o *Object) SendCommand(c Command, incseq bool) bool {
	if incseq {
		o.incSeqnum()
	}

	o.Lock()
	o.que.PushBack(c)
	o.Unlock()

	atomic.AddInt64(&o.sendCmdCnt, 1)

	//notify
	o.cond.Signal()
	return true
}

// Dequeue command and process it.
func (o *Object) ProcessCommand() {

	//wait for active
	<-o.waitActive

	//deamon or no
	if o.Waitor != nil {
		o.Waitor.Add(o.Name, 1)
		defer o.Waitor.Done(o.Name)
	}

	var tickMode bool
	if o.opt.Interval > 0 && o.sinker != nil && o.timer == nil {
		o.timer = time.NewTicker(o.opt.Interval)
		defer o.timer.Stop()
		tickMode = true
	}

	name := o.GetTreeName()
	logger.Logger.Debug("(", name, ") object active!!!")
	doneCnt := 0
	for !o.terminated {
		cnt := o.GetPendingCommandCnt()
		if cnt == 0 {
			if tickMode {
				if o.cond.WaitForTick(o.timer) {
					//logger.Logger.Debug("(", name, ") object safeTick 1 ", time.Now())
					o.safeTick()
					doneCnt = 0
					continue
				}
			} else {
				o.cond.Wait()
			}
		}

		o.Lock()
		e := o.que.Front()
		if e != nil {
			o.que.Remove(e)
		}
		o.Unlock()

		if e != nil {
			if cmd, ok := e.Value.(Command); ok {
				o.safeDone(cmd)
				doneCnt++
			}
		}

		if tickMode {
			select {
			case <-o.timer.C:
				//logger.Logger.Debug("(", name, ") object safeTick 2 ", time.Now())
				o.safeTick()
				doneCnt = 0
			default:
			}
			// 在一个心跳周期内待处理任务过多
			// cnt 剩余任务数量（待处理任务）
			// MaxDone 允许最大待处理任务数量
			// doneCnt 当前心跳周期内已经处理的任务数量
			if doneCnt > o.opt.MaxDone || cnt > o.opt.MaxDone {
				logger.Logger.Warn("(", name, ") object queue cmd count(", cnt, ") maxdone(", o.opt.MaxDone, ")", " this tick process cnt(", doneCnt, ")")
			}
		}
	}

	cnt := o.GetPendingCommandCnt()
	logger.Logger.Debug("(", name, ") object ProcessCommand done!!! queue rest cmd count(", cnt, ") ")
}

func (o *Object) safeDone(cmd Command) {
	defer utils.DumpStackIfPanic("Object::Command::Done")
	if StatsWatchMgr != nil {
		watch := StatsWatchMgr.WatchStart(fmt.Sprintf("/object/%v/cmdone", o.Name), 4)
		if watch != nil {
			defer watch.Stop()
		}
	}

	err := cmd.Done(o)
	atomic.AddInt64(&o.recvCmdCnt, 1)
	if err != nil {
		panic(err)
	}
}

func (o *Object) safeStart() {
	defer utils.DumpStackIfPanic("Object::OnStart")

	if o.sinker != nil {
		o.sinker.OnStart()
	}
}

func (o *Object) safeTick() {
	defer utils.DumpStackIfPanic("Object::OnTick")

	if o.sinker != nil {
		o.sinker.OnTick()
	}
}

func (o *Object) safeStop() {
	defer utils.DumpStackIfPanic("Object::OnStop")

	if o.sinker != nil {
		o.sinker.OnStop()
	}
}

func (o *Object) IsTermiated() bool {
	return o.terminated
}

func (o *Object) StatsSelf() (stats CmdStats) {
	stats.PendingCnt = int64(o.GetPendingCommandCnt())
	stats.SendCmdCnt = atomic.LoadInt64(&o.sendCmdCnt)
	stats.RecvCmdCnt = atomic.LoadInt64(&o.recvCmdCnt)
	return
}

func (o *Object) GetStats() map[string]CmdStats {
	if o.childs == nil {
		return nil
	}
	stats := make(map[string]CmdStats)
	stats[o.GetTreeName()] = o.StatsSelf()
	childs := o.childs.Items()
	for _, c := range childs {
		if cc, ok := c.(*Object); ok && cc != nil {
			stats[cc.GetTreeName()] = cc.StatsSelf()
			subStats := cc.GetStats()
			if subStats != nil && len(subStats) > 0 {
				for k, v := range subStats {
					stats[k] = v
				}
			}
		}
	}
	return stats
}
