package chess

import (
	"math/rand"
	"time"

	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/netlib"
	"mongo.games.com/goserver/core/task"
	"mongo.games.com/goserver/core/timer"

	rule "mongo.games.com/game/gamerule/chess"
	"mongo.games.com/game/proto"
	proto_chesstitians "mongo.games.com/game/protocol/chesstitians"
	"mongo.games.com/game/robot/base"
)

type SCChesstitiansRoomInfoPacketFactory struct {
}

type SCChesstitiansRoomInfoHandler struct {
}

func (this *SCChesstitiansRoomInfoPacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansRoomInfo{}
	return pack
}

func (this *SCChesstitiansRoomInfoHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Tracef("(this *SCChesstitiansRoomInfoHandler) Process [%v].", s.GetSessionConfig().Id)
	if msg, ok := pack.(*proto_chesstitians.SCChesstitiansRoomInfo); ok {
		scene := base.SceneMgrSingleton.GetScene(msg.GetRoomId())
		if scene == nil {
			scene = NewChesstitiansScene(msg)
			base.SceneMgrSingleton.AddScene(scene)
		}
		if scene != nil {
			for _, pd := range msg.GetPlayers() {
				if scene.GetPlayerBySnid(pd.GetSnId()) == nil {
					p := NewChesstitiansPlayer(pd)
					if p != nil {
						scene.AddPlayer(p)
					}
				}
			}
			//logger.Logger.Infof("(this *SCChesstitiansRoomInfoHandler) SetAttribute(base.SessionAttributeSceneId) %v %v", scene.GetRoomId(), msg)
			operateTask(s, 3)
		}
	} else {
		logger.Logger.Error("SCChesstitiansRoomInfo package data error.")
	}
	return nil
}

type SCChesstitiansPlayerOpPacketFactory struct {
}

type SCChesstitiansPlayerOpHandler struct {
}

func (this *SCChesstitiansPlayerOpPacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansPlayerOp{}
	return pack
}

func (this *SCChesstitiansPlayerOpHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Tracef("(this *SCChesstitiansPlayerOpHandler) Process [%v].", s.GetSessionConfig().Id)
	if scChesstitiansOp, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerOp); ok {
		if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok {
			player := scene.GetMe(s)

			if player == base.NilPlayer {
				//logger.Logger.Errorf("(this *SCChesstitiansPlayerOpHandler) nilPlayer")
				return nil
			}
			me, ok2 := player.(*ChesstitiansPlayer)
			//logger.Logger.Infof("(this *SCChesstitiansPlayerOpHandler) player ok:%v me:%v", ok2, me)
			if ok2 && me != ChesstitiansNilPlayer {
				//logger.Logger.Tracef("SCChesstitiansPlayerOpHandler msg:%v", scChesstitiansOp)
				isMeOp := me.GetSnId() == scChesstitiansOp.GetSnId()
				if isMeOp {
					if int(scChesstitiansOp.GetOpRetCode()) == 0 {
						switch scChesstitiansOp.GetOpCode() {
						case rule.PlayerOpPlay:
						}
					} else { //操作失败
						logger.Logger.Infof("TODO: 如果操作失败，应改为操作")
						switch scChesstitiansOp.GetOpCode() {
						case rule.PlayerOpPlay:
							// ai请求失败，随便走一步
							AiRandom(s)
						}
					}
				}

				if scChesstitiansOp.GetOpCode() == rule.PlayerOpPlay && scChesstitiansOp.GetOpRetCode() == 0 {
					param := scChesstitiansOp.GetOpParam()
					//scene.chess.PlayOp(int(param[0]), int(param[1]))
					//scene.chess.NextAct()
					scene.PlayOp(int(param[0]), int(param[1]))
				}

				if !isMeOp {
					//todo 对方求和，机器人暂时都同意求和
					// 机器人模拟真人，求和，认输，悔棋等操作
					switch scChesstitiansOp.GetOpCode() {
					case rule.PlayerOpDraw:
						pack := &proto_chesstitians.SCChesstitiansPlayerOp{
							OpCode: proto.Int32(rule.PlayerOpPlay),
						}
						if rand.Int31n(100) < 80 {
							pack.OpParam = []int64{rule.RefuseDraw}
						} else {
							pack.OpParam = []int64{rule.AgreeDraw}
						}
						base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp),
							pack, []int{2, 5}...)
					}

					operateTask(s, 3)
				}

			} else {
				logger.Logger.Errorf("(this *SCChesstitiansPlayerOpHandler) nilPlayer")
			}
		} else {
			logger.Logger.Error("SCChesstitiansPlayerOp base.GetScene(s).(*ChesstitiansScene) error")
		}
	} else {
		logger.Logger.Error("SCChesstitiansPlayerOp package data error.")
	}
	return nil
}

type SCChesstitiansRoomStatePacketFactory struct {
}

type SCChesstitiansRoomStateHandler struct {
}

func (this *SCChesstitiansRoomStatePacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansRoomState{}
	return pack
}

func (this *SCChesstitiansRoomStateHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) Process [%v].", s.GetSessionConfig().Id)
	if scChesstitiansRoomState, ok := pack.(*proto_chesstitians.SCChesstitiansRoomState); ok {
		//logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) base.GetScene(s).(*ChesstitiansScene)")
		if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok {
			scene.State = scChesstitiansRoomState.State
			p := scene.GetMe(s)
			me, ok := p.(*ChesstitiansPlayer)
			//logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) get player snId:%v ok:%v me:%v", p.GetSnId(), ok, me)
			if ok && me != ChesstitiansNilPlayer {
				//logger.Logger.Infof("(this *SCChesstitiansRoomStateHandler) to roomState state:%v", scChesstitiansRoomState.GetState())
				switch scChesstitiansRoomState.GetState() {
				case int32(rule.SceneStateWaitPlayer):
					scene.Clear()
				case int32(rule.SceneStateWaitStart): //等待开始
					scene.Clear()
					if me.GetSnId() == scene.GetMasterSnid() {
						packOp := &proto_chesstitians.CSChesstitiansPlayerOp{
							OpCode: proto.Int32(3),
						}
						proto.SetDefaults(packOp)
						//if scene.GetIsAllAi() {
						//	base.DelayAISend(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp)
						//} else {
						//	base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{3, 7}...)
						//}
						base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{2, 5}...)
					}
				case int32(rule.SceneStatePlayerOp):
					logger.Logger.Infof("检查是否机器人先开始 %d", scene.CurOpIdx)
					operateTask(s, 3)
				case int32(rule.SceneStateBilled):
					scene.Clear()
					me.Clear()
				}
			} else {
				logger.Logger.Errorf("(this *SCChesstitiansRoomStateHandler) get player snId:%v", me.GetSnId())
			}
		} else {
			logger.Logger.Errorf("(this *SCChesstitiansRoomStateHandler) base.GetScene(s).(*ChesstitiansScene)")
		}
	} else {
		logger.Logger.Error("SCChesstitiansRoomState package data error.")
	}
	return nil
}

func operateTask(s *netlib.Session, times int) {
	logger.Logger.Info("@@CallableWrapper")
	var array []int64
	var code int

	if s == nil {
		return
	}

	scene, ok := base.GetScene(s).(*ChesstitiansScene)
	if !ok {
		return
	}

	p := scene.GetMe(s)
	me, ok := p.(*ChesstitiansPlayer)
	if !ok || me == ChesstitiansNilPlayer || me == nil {
		return
	}

	if scene.State != int32(rule.SceneStatePlayerOp) {
		return
	}

	if me.IsBlack == 1 && scene.GetAct() == "b" || me.IsBlack != 1 && scene.GetAct() == "w" {
		// 轮到当前行棋方
		chess := scene.chess
		fen := chess.GenFen()
		logger.Logger.Infof("fen: %s", fen)

		skillLevel := 20
		switch scene.SceneType {
		case 1:
			skillLevel = -6
		case 2:
			skillLevel = -3
		case 3:
			skillLevel = 0
		case 4:
			skillLevel = 3
		case 5:
			skillLevel = 6
		}

		var resp StockfishResp
		task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
			if scene.GetVariant() == int32(rule.ChessVarChess) {
				resp = UCIPost(fen, 500)
			} else if scene.GetVariant() == int32(rule.ChessVarMakruk) {
				resp = MakrukPost(fen, 500)
			} else if scene.GetVariant() == int32(rule.ChessVarCambodian) {
				resp = CambodianPost(fen, 500, skillLevel)
			}
			return nil
		}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
			if resp.Error == 0 && len(resp.Bestmove) >= 4 {
				array = chess.UciTwoPosToIdxList(resp.Bestmove)
				logger.Logger.Infof("fen: %v, Bestmove: %v, Ponder: %v", fen, resp.Bestmove, resp.Ponder)
				packOp := &proto_chesstitians.SCChesstitiansPlayerOp{
					OpCode:  proto.Int32(rule.PlayerOpPlay),
					OpParam: array,
				}
				base.DelaySendSecond(s, int(proto_chesstitians.ChesstitiansPacketID_PACKET_CSChesstitiansPlayerOp), packOp, []int{1, 2}...)
				//logger.Logger.Infof("CSChesstitiansPlayerOp %v", packOp.String())
			} else {
				logger.Logger.Errorf("AI错误 %v", code)
				if times > 0 {
					timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
						operateTask(s, times-1)
						return true
					}), nil, time.Second*5, 1)
				} else {
					// ai请求失败，随便走一步
					AiRandom(s)
				}
			}
		}), "UCI_Action").Start()
	}
}

type SCChesstitiansGameBilledPacketFactory struct {
}

type SCChesstitiansGameBilledHandler struct {
}

func (this *SCChesstitiansGameBilledPacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansGameBilled{}
	return pack
}

func (this *SCChesstitiansGameBilledHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Tracef("(this *SCChesstitiansGameBilledHandler) Process [%v].", s.GetSessionConfig().Id)
	if scChesstitiansBilled, ok := pack.(*proto_chesstitians.SCChesstitiansGameBilled); ok {
		if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok {
			//logger.Logger.Trace(scChesstitiansBilled)
			billData := scChesstitiansBilled.GetDatas()
			for _, data := range billData {
				p := scene.GetMe(s)
				if p == base.NilPlayer {
					continue
				}
				if me, ok2 := p.(*ChesstitiansPlayer); ok2 && me != ChesstitiansNilPlayer {
					if data.GetSnId() == me.GetSnId() { //自己的数据
						me.Coin = proto.Int64(data.GetGameCoin())
						me.IsWin = data.GetIsWin()
						me.ChessGrade = int64(data.Score)
						me.WinTimes = data.WinTimes
						me.WinScore = data.WinScore
						me.OtherScore = data.OtherScore
						me.NextRank = data.NextRank
						me.WinCoin = data.WinCoin
					}
				}
			}
		}
	} else {
		logger.Logger.Error("SCChesstitiansGameBilled package data error.")
	}
	return nil
}

type SCChesstitiansCardPacketFactory struct {
}

type SCChesstitiansCardHandler struct {
}

func (this *SCChesstitiansCardPacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansCard{}
	return pack
}

func (this *SCChesstitiansCardHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Tracef("(this *SCChesstitiansCardHandler) Process [%v].", s.GetSessionConfig().Id)
	if scChesstitiansCard, ok := pack.(*proto_chesstitians.SCChesstitiansCard); ok {
		if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok {
			scene.Chess = scChesstitiansCard.GetChess()
			scene.Act = scChesstitiansCard.GetAct()
			scene.Castling = scChesstitiansCard.GetCastling()
			scene.Enpassant = scChesstitiansCard.GetEnpassant()
			scene.ChessRound = scChesstitiansCard.GetChessRound()
			cards := scChesstitiansCard.GetCards()
			p := scene.GetMe(s)
			if me, ok2 := p.(*ChesstitiansPlayer); ok2 && me != ChesstitiansNilPlayer {
				if scene.GetState() == int32(rule.SceneStateChessInit) {
					op := scene.GetOp(s).(*ChesstitiansPlayer) // TODO:
					if cards[0] == 1 {
						me.IsBlack = 1
						op.IsBlack = 0
					} else {
						me.IsBlack = 0
						op.IsBlack = 1
					}
					scene.makeChessStruct(scene.chess)
				}
			}
		}
	} else {
		logger.Logger.Error("SCChesstitiansCardHandler package SCChesstitiansCard error.")
	}
	return nil
}

type SCChesstitiansUpdateMasterSnidPacketFactory struct {
}

type SCChesstitiansUpdateMasterSnidHandler struct {
}

func (this *SCChesstitiansUpdateMasterSnidPacketFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansUpdateMasterSnid{}
	return pack
}

func (this *SCChesstitiansUpdateMasterSnidHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	logger.Logger.Tracef("(this *SCChesstitiansUpdateMasterSnidHandler) Process [%v].", s.GetSessionConfig().Id)
	if scChesstitiansUpdateMasterSnid, ok := pack.(*proto_chesstitians.SCChesstitiansUpdateMasterSnid); ok {
		if scene, ok := base.GetScene(s).(*ChesstitiansScene); ok {
			masterSnid := scChesstitiansUpdateMasterSnid.GetMasterSnid()
			scene.MasterSnid = masterSnid //更新房主
		}
	} else {
		logger.Logger.Error("SCChesstitiansUpdateMasterSnidHandler package SCChesstitiansUpdateMasterSnid error.")
	}
	return nil
}

// 玩家进入

type SCChesstitiansPlayerEnterFactory struct {
}

func (this *SCChesstitiansPlayerEnterFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansPlayerEnter{}
	return pack
}

type SCChesstitiansPlayerEnterHandler struct {
}

func (this *SCChesstitiansPlayerEnterHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	msg, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerEnter)
	if !ok {
		logger.Logger.Error("SCChesstitiansPlayerEnterHandler error")
		return nil
	}
	logger.Logger.Trace("Recover SCChesstitiansPlayerEnterHandler ", msg.String())
	scene, ok := base.GetScene(s).(*ChesstitiansScene)
	if !ok {
		return nil
	}
	if scene == nil {
		return nil
	}
	if player := NewChesstitiansPlayer(msg.GetData()); player != nil {
		scene.AddPlayer(player)
	}
	return nil
}

// 玩家离开

type SCChesstitiansPlayerLeaveFactory struct {
}

func (this *SCChesstitiansPlayerLeaveFactory) CreatePacket() interface{} {
	pack := &proto_chesstitians.SCChesstitiansPlayerLeave{}
	return pack
}

type SCChesstitiansPlayerLeaveHandler struct {
}

func (this *SCChesstitiansPlayerLeaveHandler) Process(s *netlib.Session, packid int, pack interface{}) error {
	msg, ok := pack.(*proto_chesstitians.SCChesstitiansPlayerLeave)
	if !ok {
		logger.Logger.Error("SCChesstitiansPlayerLeaveHandler error")
		return nil
	}
	logger.Logger.Trace("Recover SCChesstitiansPlayerLeaveHandler ", msg.String())
	scene, ok := base.GetScene(s).(*ChesstitiansScene)
	if !ok {
		return nil
	}
	if scene == nil {
		return nil
	}
	if player, ok := scene.GetPlayerByPos(msg.GetPos()).(*ChesstitiansPlayer); ok && player != nil {
		scene.DelPlayer(player.GetSnId())
	}
	return nil
}

func init() {
	//SCChesstitiansRoomInfo
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), &SCChesstitiansRoomInfoHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), &SCChesstitiansRoomInfoPacketFactory{})
	//SCChesstitiansPlayerOp
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), &SCChesstitiansPlayerOpHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), &SCChesstitiansPlayerOpPacketFactory{})
	//SCChesstitiansRoomState
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), &SCChesstitiansRoomStateHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), &SCChesstitiansRoomStatePacketFactory{})
	//SCChesstitiansFinalBilled
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), &SCChesstitiansGameBilledHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), &SCChesstitiansGameBilledPacketFactory{})
	//SCChesstitiansCard
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), &SCChesstitiansCardHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), &SCChesstitiansCardPacketFactory{})
	//SCChesstitiansUpdateMasterSnid
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), &SCChesstitiansUpdateMasterSnidHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), &SCChesstitiansUpdateMasterSnidPacketFactory{})
	//ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), &SCChesstitiansPlayerEnterHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), &SCChesstitiansPlayerEnterFactory{})
	//ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave
	netlib.RegisterHandler(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), &SCChesstitiansPlayerLeaveHandler{})
	netlib.RegisterFactory(int(proto_chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), &SCChesstitiansPlayerLeaveFactory{})

	////Testjson()
	//task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
	//	logger.Logger.Info("@@CallableWrapper")
	//	stockfishResp := UCIPost("fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 500)
	//	logger.Logger.Infof("stockfishResp %v", stockfishResp)
	//	return nil
	//}), nil, "UCIPost_Action").Start()
}
