//go:build robot
// +build robot

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math/rand"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/aceld/zinx/zinx_app_demo/mmo_game/pb"
	"github.com/golang/protobuf/proto"
)

type Message struct {
	Len   uint32
	MsgID uint32
	Data  []byte
}

type TcpClient struct {
	conn     net.Conn
	X        float32
	Y        float32
	Z        float32
	V        float32
	PID      int32
	isOnline chan bool
}

func (this *TcpClient) Unpack(headdata []byte) (head *Message, err error) {
	headBuf := bytes.NewReader(headdata)

	head = &Message{}

	// 读取Len
	if err = binary.Read(headBuf, binary.LittleEndian, &head.Len); err != nil {
		return nil, err
	}

	// 读取MsgID
	if err = binary.Read(headBuf, binary.LittleEndian, &head.MsgID); err != nil {
		return nil, err
	}

	// 封包太大
	//if head.Len > MaxPacketSize {
	//	return nil, packageTooBig
	//}

	return head, nil
}

func (this *TcpClient) Pack(msgID uint32, dataBytes []byte) (out []byte, err error) {
	outbuff := bytes.NewBuffer([]byte{})
	// 写Len
	if err = binary.Write(outbuff, binary.LittleEndian, uint32(len(dataBytes))); err != nil {
		return
	}
	// 写MsgID
	if err = binary.Write(outbuff, binary.LittleEndian, msgID); err != nil {
		return
	}

	//all pkg data
	if err = binary.Write(outbuff, binary.LittleEndian, dataBytes); err != nil {
		return
	}

	out = outbuff.Bytes()

	return
}

func (this *TcpClient) SendMsg(msgID uint32, data proto.Message) {

	// 进行编码
	binaryData, err := proto.Marshal(data)
	if err != nil {
		fmt.Println(fmt.Sprintf("marshaling error:  %s", err))
		return
	}

	sendData, err := this.Pack(msgID, binaryData)
	if err == nil {
		_, _ = this.conn.Write(sendData)
	} else {
		fmt.Println(err)
	}

	return
}

func (this *TcpClient) AIRobotAction() {
	//聊天或者移动

	//随机获得动作
	tp := rand.Intn(2)
	if tp == 0 {
		content := fmt.Sprintf("hello 我是player %d, 你是谁?", this.PID)
		msg := &pb.Talk{
			Content: content,
		}
		this.SendMsg(2, msg)
	} else {
		//移动
		x := this.X
		z := this.Z

		randPos := rand.Intn(2)
		if randPos == 0 {
			x -= float32(rand.Intn(10))
			z -= float32(rand.Intn(10))
		} else {
			x += float32(rand.Intn(10))
			z += float32(rand.Intn(10))
		}

		//纠正坐标位置
		if x > 410 {
			x = 410
		} else if x < 85 {
			x = 85
		}

		if z > 400 {
			z = 400
		} else if z < 75 {
			z = 75
		}

		//移动方向角度
		randV := rand.Intn(2)
		v := this.V
		if randV == 0 {
			v = 25
		} else {
			v = 335
		}
		//封装Position消息
		msg := &pb.Position{
			X: x,
			Y: this.Y,
			Z: z,
			V: v,
		}

		fmt.Println(fmt.Sprintf("player ID: %d. Walking...", this.PID))
		//发送移动MsgID:3的指令
		this.SendMsg(3, msg)
	}
}

/*
处理一个回执业务
*/
func (this *TcpClient) DoMsg(msg *Message) {
	//处理消息
	fmt.Println(fmt.Sprintf("msg ID :%d, data len: %d", msg.MsgID, msg.Len))
	if msg.MsgID == 1 {
		//服务器回执给客户端 分配ID

		//解析proto
		syncpID := &pb.SyncPID{}
		_ = proto.Unmarshal(msg.Data, syncpID)

		//给当前客户端ID进行赋值
		this.PID = syncpID.PID
	} else if msg.MsgID == 200 {
		//服务器回执客户端广播数据

		//解析proto
		bdata := &pb.BroadCast{}
		_ = proto.Unmarshal(msg.Data, bdata)

		//初次玩家上线 广播位置消息
		if bdata.Tp == 2 && bdata.PID == this.PID {
			//本人
			//更新客户端坐标
			this.X = bdata.GetP().X
			this.Y = bdata.GetP().Y
			this.Z = bdata.GetP().Z
			this.V = bdata.GetP().V
			fmt.Println(fmt.Sprintf("player ID: %d online.. at(%f,%f,%f,%f)", bdata.PID, this.X, this.Y, this.Z, this.V))

			//玩家已经成功上线
			this.isOnline <- true

		} else if bdata.Tp == 1 {
			fmt.Println(fmt.Sprintf("世界聊天,玩家%d说的话是: %s", bdata.PID, bdata.GetContent()))
		}
	}
}

func (this *TcpClient) Start() {
	go func() {
		for {
			//读取服务端发来的数据 ==》 SyncPID
			//1.读取8字节
			//第一次读取，读取数据头
			headData := make([]byte, 8)

			if _, err := io.ReadFull(this.conn, headData); err != nil {
				fmt.Println(err)
				return
			}
			pkgHead, err := this.Unpack(headData)
			if err != nil {
				return
			}
			//data
			if pkgHead.Len > 0 {
				pkgHead.Data = make([]byte, pkgHead.Len)
				if _, err := io.ReadFull(this.conn, pkgHead.Data); err != nil {
					return
				}
			}

			//处理服务器回执业务
			this.DoMsg(pkgHead)
		}
	}()

	// 10s后，断开连接
	for {
		select {
		case <-this.isOnline:
			go func() {
				for {
					this.AIRobotAction()
					time.Sleep(time.Second)
				}
			}()
		case <-time.After(time.Second * 10):
			_ = this.conn.Close()
			return
		}
	}
}

func NewTcpClient(ip string, port int) *TcpClient {
	addrStr := fmt.Sprintf("%s:%d", ip, port)
	conn, err := net.Dial("tcp", addrStr)
	if err != nil {
		fmt.Println("net.Dial err: ", err)
		panic(err)
	}

	client := &TcpClient{
		conn:     conn,
		PID:      0,
		X:        0,
		Y:        0,
		Z:        0,
		V:        0,
		isOnline: make(chan bool),
	}

	fmt.Println(fmt.Sprintf("conn: %+v. Connected to server...", conn))

	return client
}

func main() {
	// 开启一个waitgroup，同时运行3个goroutine

	runtime.GOMAXPROCS(runtime.NumCPU())
	var wg sync.WaitGroup
	wg.Add(3)

	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			client := NewTcpClient("127.0.0.1", 8999)
			client.Start()
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			client := NewTcpClient("127.0.0.1", 8999)
			client.Start()
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			client := NewTcpClient("127.0.0.1", 8999)
			client.Start()
		}
	}()

	fmt.Println("AI robot start")
	wg.Wait()

	fmt.Println("AI robot exit")
	select {}
}
