package instance

import (
	"common/discover"
	"common/log"
	"common/proto/sc/sc_pb"
	"common/proto/ss/grpc_pb"
	"common/utils"
	"context"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"runtime/debug"
	"scene/internal/grpc_server/stream_client"
	"scene/internal/npc"
	"sync"
	"time"
)

// Instance 场景类
type Instance struct {
	wg            sync.WaitGroup
	players       map[int64]*npc.PlayerNode // 存储所有玩家节点 [usn]
	ctx           context.Context           // 停止指令
	cancel        context.CancelFunc        // 停止函数
	logger        *zap.SugaredLogger        // 日志
	lastLogicTime int64                     // 上次逻辑帧执行时间（毫秒时间戳）

	SID        int64              // 服务ID
	InstanceID int32              // 副本ID
	UniqueNo   int64              // 唯一编号
	EventIn    chan proto.Message // 消息入口
}

// NewScene 初始化场景
func NewScene(sid int64, instanceID int32) *Instance {
	s := &Instance{
		players:    make(map[int64]*npc.PlayerNode),
		SID:        sid,
		InstanceID: instanceID,
		UniqueNo:   utils.SnowflakeInstance().Generate().Int64(),
		EventIn:    make(chan proto.Message, 1024),
	}
	s.logger = log.GetLogger().Named(fmt.Sprintf("instance %v:%v", s.InstanceID, s.UniqueNo))
	s.ctx, s.cancel = context.WithCancel(context.Background())
	return s
}

func (i *Instance) Start(ttl int64) {
	i.wg.Add(1)
	go func() {
		defer i.wg.Done()
		defer func() {
			if err := recover(); err != nil {
				i.logger.Infof("instance err: %v", err)
				debug.PrintStack()
			}
		}()
		Mgr.Add(i.UniqueNo, i)
		defer func() {
			i.logger.Infof("instance destroy")
			discover.UnRegisterInstance(i.UniqueNo)
			close(i.EventIn)
			Mgr.Delete(i.UniqueNo)
		}()

		if err := discover.RegisterInstance(i.SID, i.InstanceID, i.UniqueNo, ttl); err != nil {
			i.logger.Errorf("register discover error")
			return
		}
		// 场景心跳
		tick := time.NewTicker(50 * time.Millisecond)
		i.lastLogicTime = time.Now().UnixMilli()
		for {
			select {
			case <-i.ctx.Done():
				return
			case <-tick.C:
				now := time.Now().UnixMilli()
				i.onLogic(now - i.lastLogicTime)
				i.lastLogicTime = now
			case e := <-i.EventIn:
				i.onEvent(e)
			}
		}
	}()
	i.logger.Infof("new scene start")
}

// 网络帧，将玩家的操作储存起来，到逻辑帧再处理玩家操作
func (i *Instance) onEvent(e proto.Message) {
	switch v := e.(type) {
	case *grpc_pb.EnterReq:
		i.players[v.USN] = npc.NewPlayerNode(v.GatewaySID, v.USN)
	case *grpc_pb.ActionReq:
		if node, ok := i.players[v.USN]; ok {
			node.AddAction(v)
		}
	case *grpc_pb.LeaveReq:
		delete(i.players, v.USN)
	}
}

// 逻辑帧
func (i *Instance) onLogic(delta int64) {
	positionUpdate := make([]*sc_pb.PositionInfo, 0)
	sid := int64(0)
	// 处理玩家指令
	for _, node := range i.players {
		if node.LogicAction(delta) {
			positionUpdate = append(positionUpdate, &sc_pb.PositionInfo{
				USN: node.USN,
				X:   int32(node.Position[0] * 100),
				Y:   int32(node.Position[1] * 100),
			})
			sid = node.GatewaySID
		}
	}
	if len(positionUpdate) > 0 {
		payload, _ := proto.Marshal(&sc_pb.S2C_Position{
			Info: positionUpdate,
		})
		if err := stream_client.SendMessageToGateway(sid, stream_client.FunToClient, &grpc_pb.ToClientReq{
			USN:       -1,
			MessageID: int32(sc_pb.MessageID_MESSAGE_ID_POSITION),
			Payload:   payload,
		}); err != nil {
			i.logger.Errorf("send action err: %v", err)
		}
	}
}
