package main

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"../log"
	"../mod"
	"../nnet"
	"../nt"
	pb "../protos"
	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"
)

// 根据 Pb 协议结构体，获得协议字符串名称
func GetMsgName(st proto.Message) string {
	name := proto.MessageName(st)
	parts := strings.Split(name, ".")

	return parts[len(parts)-1]
}

// 根据协议名称获得协议ID
func GetMsgId(name string) uint32 {
	msg_id, ok := pb.MsgId_value[name]
	if !ok {
		log.Warn("GetMsgId(name)", zap.String("name", name))
		return 0
	}
	return uint32(msg_id)
}

// @ses: session
// @st: Response 内部结构的 struct
// @params: [0] 发送后是否关闭 [1] 关闭原因
func SendToClient(ses nnet.ISession, guid uint64, st proto.Message, params ...int32) {
	// 不做 guid 合法性检查，而直接发出消息
	bst, err := proto.Marshal(st)
	if nil != err {
		log.Error("proto.Marshal", zap.Error(err))
		return
	}
	name := GetMsgName(st)
	id := GetMsgId(name)
	res := &pb.Response{
		MsgId:   id,
		Guid:    guid,
		MsgName: name,
		Res:     bst,
	}

	bytes_res, err := proto.Marshal(res)
	if nil != err {
		log.Error("proto.Marshal", zap.Error(err))
		return
	}

	var closed int32 = 0
	var reason int32 = 0
	if len(params) > 0 {
		closed = params[0]
	}
	if len(params) > 1 {
		reason = params[1]
	}
	err = ses.AWrite(NewKgPacket(bytes_res, closed, reason), 0)
	if nil != err {
		log.Error("proto.AWrite", zap.Error(err))
	}
}

// @ses: session
// @st: Response 内部结构的 struct
func SyncSendToClient(ses nnet.ISession, guid uint64, st proto.Message) {
	// 不做 guid 合法性检查，而直接发出消息
	bst, err := proto.Marshal(st)
	if nil != err {
		log.Error("proto.Marshal", zap.Error(err))
		return
	}
	name := GetMsgName(st)
	id := GetMsgId(name)
	res := &pb.Response{
		MsgId:   id,
		Guid:    guid,
		MsgName: name,
		Res:     bst,
	}

	bytes_res, err := proto.Marshal(res)
	if nil != err {
		log.Error("proto.Marshal", zap.Error(err))
		return
	}

	err = ses.Write(NewKgPacket(bytes_res), 1*time.Second)
	//log.Debug("ddd", zap.Uint64("guid", guid), zap.Int("len", len(bst)))
	if nil != err {
		log.Error("proto.Write", zap.Error(err))
	}
}

// 同实例广播
func BroadcastToClient(st proto.Message) {
	sess := Server.GetAllSessions()
	//TODO 优化
	Server.Wg().Add(1)
	go func() {
		start := time.Now()
		for id, ses := range sess {
			SendToClient(ses, id, st)
		}
		Server.Wg().Done()
		lasts := time.Since(start)
		log.Debug("broadcast", zap.Duration("used", lasts), zap.Int("sesNum", len(sess)))
	}()
}

// 关闭某个用户连接
func KillUserSession(guid uint64, ses nnet.ISession, reason pb.ReasonDisconn) {
	cmd_offline := &pb.S_Offline{}
	cmd_offline.Reason = int32(reason)
	SendToClient(ses, guid, cmd_offline, 1, int32(reason))
	//ses.Close(int32(reason))
	fmt.Println("closed session:", ses.Id(), "size:", Server.GetSessionNum())
}

// 两种格式的转换
func UserToS_Login(user *pb.User, sl *pb.S_Login) {
	sl.Ec = int32(pb.ErrCode_OK)
	sl.Account = user.Account
	sl.Nickname = user.Nickname
	sl.Icon = user.Icon
	sl.Phone = user.Phone
	sl.Balance = user.Balance
	sl.Bank = user.Bank
	sl.BankPass = user.BankPass
	sl.Ip = user.Ip
	sl.Area = user.Area
	sl.Vip = user.Vip
	sl.Qq = user.Qq
	sl.Wechat = user.Wechat
	sl.Bets = user.Bets
	sl.NextVipNeed = GetNextVipNeed(user.Vip)
	sl.Alipay = user.Alipay
	sl.Name = user.Name
	sl.Email = user.Email
	sl.Birthday = user.Birthday
	//取出断线重连信息并格式化 返回给客户端

	log.Debug("断线重连",zap.Uint64("guid",user.Guid), zap.String("Regameinfo", user.Regameinfo))
	if len(user.Regameinfo) > 0 {
		strArr := strings.Split(user.Regameinfo, "#")
		gameId, err := strconv.Atoi(strArr[0])
		if err != nil {
			log.Debug("UserToS_Login gameId strconv.Atoi err", zap.Error(err))
		}
		var gameParam []string
		for n := 1; n < len(strArr); n++ {
			gameParam = append(gameParam, strArr[n])
		}

		res := &pb.ReconnectInfo{}
		res.GameId = int32(gameId)
		res.GameParam = gameParam[:len(gameParam)-1]
		type_view ,err := strconv.Atoi(gameParam[len(gameParam)-1])
		if err != nil{
			log.Debug("UserToS_Login type_view strconv.Atoi err", zap.Error(err))
		}
		res.ViewType = int32(type_view)
		sl.GameInfo = res
		log.Debug("UserToS_Login GameInfo success", zap.Any("data", sl.GameInfo))
	}
}

func ClearAgentFlags() {
	users, err := mod.GetAgentUsers(nt.InstConf.Id)
	if err != nil {
		for _, guid := range users {
			mod.RemoveUserAgent(guid, nt.InstConf.Id)
		}
	}

	mod.TryRemoveUsersOfAgent(nt.InstConf.Id)

	_ = mod.ClearAgentUsers(nt.InstConf.Id)
}

func ClearLogicFlags() {
	users, err := mod.GetLogicUsers(nt.InstConf.Id)
	if nil != err {
		for _, guid := range users {
			_ = mod.RemoveUserLogic(guid, int64(nt.InstConf.Id))
		}
	}
	_ = mod.ClearLogicUsers(nt.InstConf.Id)
}
