package main

import (
	"bytes"
	"common/logger"
	//pb "common/protocol"
	pb "WorldTester/pb"
	"encoding/binary"
	"errors"
	"log"
	"net"
	"reflect"
	"time"

	"github.com/golang/protobuf/proto"
)

type MessageHandler func(conn net.Conn, header pb.MessageHeader, msg interface{})

type MessageInfo struct {
	msgType    reflect.Type
	msgHandler MessageHandler
}

var (
	msg_map                 = make(map[pb.MSGID]MessageInfo)
	Max_Message_Size uint16 = 4096
)

func RegisterMessage(msgid pb.MSGID, msg interface{}, handler MessageHandler) {

	var info MessageInfo
	info.msgType = reflect.TypeOf(msg.(proto.Message))
	info.msgHandler = handler

	msg_map[msgid] = info
}

func getTcpClient(uid uint32) net.Conn {
	conn, err := net.Dial("tcp", server_addr)
	if err != nil {
		logger.Info("connect server:%+v error:%=v", server_addr, err.Error())
		log.Println("connect server:%+v error:%=v", server_addr, err.Error())
		return nil
	}
	logger.Info("connect server success! for uid:%+v addr:%v", uid, server_addr)
	log.Println("connect server success! for uid:%+v addr:%v", uid, server_addr)
	return conn
}

/*
func clientSendLoop(conn net.Conn) {
	sms := make([]byte, 128)
	_ = sms
	for {
		fmt.Print("\nenter command:")
		//		_, err := fmt.Scan(&sms)
		reader := bufio.NewReader(os.Stdin)
		strBytes, _, err := reader.ReadLine()
		if err != nil {
			fmt.Println("invalid input:", err.Error())
			break
		}
		handleCommand(conn, string(strBytes))
	}
	fmt.Println("send loop exist")
}
*/

func clientRecvLoop(player *PlayerInfo) {
	tmp := make([]byte, 10240)
	buf := make([]byte, 0, 10240)
	var buf_len uint32
	defer player.conn.Close()

	for {
		player.conn.SetReadDeadline(time.Now().Add(time.Second))
		n, err := player.conn.Read(tmp)
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				checkPlayer(player)
				continue
			}
			logger.Info("recv conn error:%+v", err)
			break
		}
		buf = append(buf, tmp[:n]...)
		buf_len += uint32(n)

		for buf_len >= uint32(Min_Message_Size) {
			logger.Info("clientRecvLoop receive message")
			el, err := handleBuffer(player.conn, buf, buf_len)
			if err != nil {
				logger.Info("handler buffer error:%+v", err)
				break
			} else {
				buf_len -= el
				buf = buf[el:]
			}
		}
		checkPlayer(player)
	}
	logger.Info("recv loop exist for uid:%v conn:%v", player.uid, player.conn)
}
func handleBuffer(conn net.Conn, buffer []byte, size uint32) (eat_len uint32, err error) {
	var header pb.MessageHeader
	rd := bytes.NewReader(buffer[0:Min_Message_Size])
	binary.Read(rd, binary.BigEndian, &header)

	if header.PackageLen > Max_Message_Size {
		logger.Info("error got invalid message size:%+v", header.PackageLen)
		eat_len = size
		err = nil
		return
	}

	if uint32(header.PackageLen) > size {
		logger.Info("error message not enough, buffer size:%v, pakcet len:%v header:%+v\n",
			size, header.PackageLen, header)
		eat_len = 0
		err = errors.New("error not enough")
		return
	}

	dispatchRawData(conn, header, buffer[Min_Message_Size:header.PackageLen])

	eat_len = uint32(header.PackageLen)
	err = nil
	return
}

func dispatchRawData(conn net.Conn, header pb.MessageHeader, data []byte) {
	logger.Info("<-- dispatch new message header:%+v\n", header)
	msgid := pb.MSGID(header.MsgID)

	if info, ok := msg_map[msgid]; ok {
		msg := reflect.New(info.msgType.Elem()).Interface()
		err := proto.Unmarshal(data, msg.(proto.Message))
		if err != nil {
			return
		}
		info.msgHandler(conn, header, msg)
		return
	} else {
		logger.Info("invalid msgid from server:%+v", msgid)
	}
}
