package baseroom

import (
	"fmt"
	"game/common/proto/pb"
	"game/common/utils"
	"github.com/fox/fox/ipb"
	"github.com/fox/fox/log"
	"github.com/fox/fox/processor"
	"github.com/fox/fox/service"
	"github.com/fox/fox/timer"
	"github.com/golang/protobuf/proto"
	"time"
)

type PlayerType int

const (
	PT_User  PlayerType = 0
	PT_Robot PlayerType = 1
	PT_All   PlayerType = 2
)

// 房间基类，不要直接用于业务
type BaseRoom[Seat ISeat] struct {
	id            int
	roomType      int // 房间配置id 初级，中级，高级
	gameId        int // 玩法配置id color玩法id
	gameNo        string
	seats         []Seat
	processor     *processor.Processor
	timeProcessor *processor.TimerProcessor

	timeTypes map[timer.ITimeType]uint32
	srv       service.IService
}

func NewBaseRoom[Seat ISeat](id, roomType, gameId, seatNum int, srv service.IService) (*BaseRoom[Seat], pb.ErrCode) {
	room := &BaseRoom[Seat]{
		id:            id,
		roomType:      roomType,
		gameId:        gameId,
		gameNo:        "",
		seats:         make([]Seat, seatNum),
		timeTypes:     make(map[timer.ITimeType]uint32),
		srv:           srv,
		processor:     processor.NewProcessor(),
		timeProcessor: processor.NewTimerProcessor(),
	}

	return room, pb.ErrCode_OK
}

func (r *BaseRoom[Seat]) Id() int {
	return r.id
}

func (r *BaseRoom[Seat]) RoomType() int {
	return r.roomType
}

func (r *BaseRoom[Seat]) GameId() int {
	return r.gameId
}

func (r *BaseRoom[Seat]) GetService() service.IService {
	return r.srv
}

// 入座玩家数量
func (r *BaseRoom[Seat]) SeatPlayerNum(playerType PlayerType) int {
	num := 0
	for _, seat := range r.seats {
		if !seat.Empty() {
			switch playerType {
			case PT_All:
				num++
			case PT_User:
				if !seat.Player().IsRobot() {
					num++
				}
			case PT_Robot:
				if seat.Player().IsRobot() {
					num++
				}
			}
		}
	}
	return num
}

// 返回是否有空座位及空座位编号
func (r *BaseRoom[Seat]) FindEmptySeat() (bool, Seat) {
	for _, seat := range r.seats {
		if seat.Empty() {
			return true, seat
		}
	}
	return false, utils.TValue[Seat]{}.V
}

// 查找玩家所在的座位
func (r *BaseRoom[Seat]) FindSeat(uid int64) (bool, Seat) {
	for _, seat := range r.seats {
		if !seat.Empty() && seat.Player().Id() == uid {
			return true, seat
		}
	}
	return false, utils.TValue[Seat]{}.V
}

// 查找玩家所在的座位
func (r *BaseRoom[Seat]) GetSeat(seatNo int) (bool, Seat) {
	if seatNo < 0 || seatNo >= len(r.seats) {
		return false, utils.TValue[Seat]{}.V
	}
	return true, r.seats[seatNo]
}

// 站起时player为nil，坐下时player非nil
func (r *BaseRoom[Seat]) SitDownOrStandup(player IPlayer, seat int) bool {
	if seat < 0 || seat >= len(r.seats) {
		log.Error(r.SeatLog(r.seats[seat], "out of range"))
		return false
	}
	if player != nil && r.seats[seat].Player() != nil {
		log.Error(r.SeatLog(r.seats[seat], "is already here"))
		return false
	}
	r.seats[seat].SetPlayer(player)
	return true
}

func (r *BaseRoom[Seat]) RangeSeat(proc func(Seat) bool) {
	for _, seat := range r.seats {
		if !seat.Empty() && !proc(seat) {
			return
		}
	}
}

func (r *BaseRoom[Seat]) FilterSeat(proc func(Seat) bool) []Seat {
	seats := make([]Seat, 0)
	for _, seat := range r.seats {
		if !seat.Empty() && proc(seat) {
			seats = append(seats, seat)
		}
	}
	return seats
}

func (r *BaseRoom[Seat]) DebugSendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) {
	log.Debug(r.UserLog(user.Id(), "send msg:%v %v", msgId, msg.String()))
}

func (r *BaseRoom[Seat]) SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) {
	r.DebugSendMsg(user, msgId, msg)
	if user.Robot() != nil {
		user.Robot().OnMessage(msgId, msg)
	} else {
		iMsg := ipb.MakeMsgEx(r.srv.Name(), 0, user.Id(), int32(msgId), msg)
		_ = r.srv.SendByServiceId(int(pb.ServiceTypeId_STI_Gate), iMsg)
	}
}

func (r *BaseRoom[Seat]) Broadcast(msgId pb.MsgId, msg proto.Message, exclude ...IPlayer) {
	for _, seat := range r.seats {
		if !seat.Empty() {
			exist := false
			for _, excludePlayer := range exclude {
				if excludePlayer.Id() == seat.Player().Id() {
					exist = true
					break
				}
			}
			if !exist {
				r.SendMsg(seat.Player(), msgId, msg)
			}
		}
	}
}

func (r *BaseRoom[Seat]) NewTimer(timerType timer.ITimeType, duration time.Duration, args ...any) {
	if _, ok := r.timeTypes[timerType]; ok {
		log.Error(r.Log("timer type:%v is exist.can not new timer", timerType.String()))
		// if timerType == TtPlayerAct {
		//	log.Debug(r.Log(log.StackTrace()))
		// }
		return
	}
	tid := r.srv.NewTimer(duration+time.Duration(10)*time.Millisecond, func() {
		if err := r.timeProcessor.Dispatch(timerType, args...); err != nil {
			log.ErrorF(r.Log("timer dispatch err:%v", err))
		}
	}, true, r.Log("start type:%v timer", timerType.String()))
	r.timeTypes[timerType] = tid
	// log.Debug(r.Log("start type:%v timer", timerType.String()))
}

func (r *BaseRoom[Seat]) CancelTimer(timerType timer.ITimeType) {
	if tid, ok := r.timeTypes[timerType]; ok {
		r.srv.CancelTimer(tid)
		delete(r.timeTypes, timerType)
		// log.Debug(r.Log("stop type:%v timer, rest timer:%+v", timerType.String(), r.timeTypes))
	}
}

func (r *BaseRoom[Seat]) CancelAllTimer() {
	for _, tid := range r.timeTypes {
		r.srv.CancelTimer(tid)
		// log.Debug(r.Log("start type:%v timer", timerType.String()))
	}
	r.timeTypes = make(map[timer.ITimeType]uint32)
}

func (r *BaseRoom[Seat]) Log(format string, a ...any) string {
	head := fmt.Sprintf("room:%v type:%v ", r.id, r.roomType)
	return head + fmt.Sprintf(format, a...)
}

func (r *BaseRoom[Seat]) SeatLog(seat Seat, format string, a ...any) string {
	head := ""
	if seat.Player() != nil {
		head = fmt.Sprintf("room:%v type:%v seat:%v user:%v robot:%v ", r.id, r.roomType, seat.No(), seat.Player().Id(), seat.Player().Robot() != nil)
	} else {
		head = fmt.Sprintf("room:%v type:%v seat:%v ", r.id, r.roomType, seat.No())
	}

	return head + fmt.Sprintf(format, a...)
}

func (r *BaseRoom[Seat]) UserLog(uid int64, format string, a ...any) string {
	var seat Seat
	exist := false
	for _, st := range r.seats {
		if !st.Empty() && st.Player().Id() == uid {
			seat = st
			exist = true
			break
		}
	}
	if exist {
		return r.SeatLog(seat, format, a...)
	} else {
		return r.Log(format, a...)
	}
}

func (r *BaseRoom[Seat]) Unmarshal(cmd int32, data []byte) (any, error) {
	return r.processor.Unmarshal(cmd, data)
}

func (r *BaseRoom[Seat]) Dispatch(user IPlayer, cmd int32, params ...any) error {
	inp := make([]any, 0, len(params)+1)
	inp = append(inp, user)
	inp = append(inp, params...)
	return r.processor.Dispatch(cmd, inp...)
}

func (r *BaseRoom[Seat]) RegisterMessages(metas processor.RegisterMetas) {
	r.processor.RegisterMessages(metas)
}

// 注册时间事件及处理
func (r *BaseRoom[Seat]) RegisterTimerMessages(metas processor.RegisterTimerMetas) {
	r.timeProcessor.RegisterMessages(metas)
}

// 初始化房间
func (r *BaseRoom[Seat]) OnInit() {

}
