package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"../gameapi"
	//"net"
	"time"

	"../log"
	"../nnet"
	"../nt"
	"../utils"
	"go.uber.org/zap"
)

var (
	// nnet 配置参数
	gconf = &nnet.HubConfig{
		SizeOfSendChan: 4096,
		SizeOfRecvChan: 4096,
		ReadBufSize:    2048,
		WriteBufSize:   2048,
		Timeout:        2 * time.Second,
		Tick:           6 * time.Second, // 定时回调
		ReadTimeout:    12 * time.Second,
	}

	lconf = &nnet.HubConfig{
		SizeOfSendChan: 4096,
		SizeOfRecvChan: 4096,
		ReadBufSize:    2048,
		WriteBufSize:   2048,
		Timeout:        2 * time.Second,
		Tick:           6 * time.Second,
		ReadTimeout:    0,
	}

	ServerStatus *WsServerStatus = NewWsServerStatus()
	Server       *nnet.WsServer

	LoginStatus *LoginClientStatus = NewLoginClientStatus()
	Login       *nnet.WsClient     // = nnet.NewWsClient()
)

// 初始化
func init_by_conf() {
	var err error
	err = nt.InstInit("lobby", VERSION, FileConf)
	utils.ErrPanic(err)
	// 连接 login
	ClearAgentFlags()
	ClearLogicFlags()

	go func() {
		err = reconnect_login()
		utils.ErrPanic(err)
	}()

	// 连接 eventbus
	// 连接 mq
	// 启动 rpc 服务器
	err = createRpcServer(nt.InstConf.RpcAddr, ChQuit)
	utils.ErrPanic(err)
	// 注册自己
	rawjson, err := json.Marshal(nt.InstConf)
	utils.ErrPanic(err)

	path_of_self := nt.ZkP.Lobbies + "/" + nt.InstName
	err = nt.Zk.CreateEmpNode(path_of_self, rawjson)
	utils.ErrPanic(err)
	// 监视自己
	nt.RegisterNodeCallback(path_of_self, on_notify_node_event)
	// 监视钱包
	nt.RegisterNodeCallback(nt.ZkP.Wallet, on_notify_node_event)
	nt.RegisterNodeCallback(nt.ZkP.Games, on_notify_node_event)
	nt.RegisterNodeCallback(nt.ZkP.Channels, on_notify_node_event)
	nt.RegisterNodeCallback(nt.ZkP.ClientAddrs, on_notify_node_event)

	err = nt.DynConf.MonitorLobbies()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorCheckin7()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorVip()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorClientAddrs()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorGames()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorChannels()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorKv()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorWallet()
	utils.ErrPanic(err)

	err = nt.DynConf.MonitorGameTasks()
	utils.ErrPanic(err)

	gameapi.InitGameAPIConf()

	//
	err = nt.DynConf.MonitorTurntable()
	if err != nil {
		log.Warn("turnable not config", zap.Error(err))
		nt.DynConf.Turntable = nt.DefaultTurnTableConf
	}
	// 连接 api @NOTICE api 不通则重试，不用退出, 所有以上的故障则属于部署问题。
	//
	mod_init()

	fmt.Printf("%v Boot At %s all ok.\n", nt.InstName, nt.BootTime.Format("2006-01-02 15:04:05"))
}

// 监听客户端连接
func listenForClients() {
	if nt.InstConf.Host == "" {
		panic("Not config Listen ip address")
	}
	if nt.InstConf.Port <= 1024 || nt.InstConf.Port > 65500 {
		panic("Not valid Listen Port")
	}
	listen_addr := fmt.Sprintf("%s:%d", nt.InstConf.Host, nt.InstConf.Port)

	Server = nnet.NewWsServer(gconf, ServerStatus, new(KgProtocol), listen_addr, map[string]nnet.CallbackWsPath{
		"/trace_server": func(w http.ResponseWriter, r*http.Request){

			var seses []uint64
			for sesId, _ := range Server.GetAllSessions() {
				seses = append(seses, sesId)
			}
			var response = utils.H{
				"num": len(seses),
				"guids": seses,
			}
			bytes, err := json.Marshal(response)
			if err != nil {
				return
			}

			w.Write(bytes)
		},
	})

	go func() {
		err := Server.Start()
		utils.ErrPanic(err)
	}()

	log.Debug("ws server", zap.String("info", "started"))

	fmt.Println("Server started")
}

func reconnect_login() error {
	data, err := nt.Zk.GetNodeData("conf/servers/login")
	if nil != err {
		return err
	}
	var zkconf ZkLoginConf
	err = json.Unmarshal(data, &zkconf)
	if nil != err {
		return err
	}
	_, addr := utils.Choice(zkconf.Addrs)
	Login = nnet.NewWsClient(lconf, LoginStatus, new(LoginProtocol), addr)
	err = Login.Start()
	if err != nil {
		return err
	}
	return nil
}

// 重连 goroutine
// 返回一个 channel,当向这个 channel 发送数据时，开始重连
func start_reconn_thread(interval time.Duration) chan<- int {
	chSignal := make(chan int, 1)

	go func() {
		defer func() {
			close(chSignal)
			log.Debug("quit from start_reconn_thread()")
		}()

		for {
			count := 0
			select {
			case <-ChQuit:
				log.Debug("reconn: system quit")
				return
			case <-chSignal:
				log.Debug("start reconn")
				for {
					if Online == 0 {
						log.Info("quit login reconnect process. Online=0")
						return
					}
					count += 1
					err := reconnect_login()
					if err != nil {
						log.Warn("reconn failed", zap.Error(err), zap.Int("times", count))
						time.Sleep(interval)
					} else {
						log.Info("reconn success", zap.Int("times", count))
						break
					}
				}
			}
		}

	}()

	return chSignal
}
