package server_tcp

import (
	"compress/zlib"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"common/clientMgr"
	"common/config"
	. "common/model"
	"framework/goroutineMgr"
	"goutil/intAndBytesUtil"
	"goutil/logUtil"
	"goutil/timeUtil"
	"goutil/zlibUtil"
)

const (
	// 包头的长度
	con_HEADER_LENGTH = 4

	// 客户端失效的秒数
	con_CLIENT_EXPIRE_SECONDS int64 = 300
)

var (
	// 全局客户端的id，从1开始进行自增
	globalClientId int32 = 0

	// 字节的大小端顺序
	byterOrder = binary.LittleEndian
)

// 定义客户端对象，以实现对客户端连接的封装
type Client struct {
	// 唯一标识
	id int32

	// 客户端连接对象
	conn net.Conn

	// 接收到的消息内容
	receiveData []byte

	// 待发送的数据
	sendData []*ServerResponseObject

	// 连接是否关闭(通过此字段来协调receiveData和sendData方法)
	closed bool

	// 锁对象（用于控制对sendDatap的并发访问；receiveData不需要，因为是同步访问）
	mutex sync.Mutex

	// 玩家Id
	playerId int64

	// 上次活跃时间
	activeTime int64
}

// 获取唯一标识
func (this *Client) GetId() int32 {
	return this.id
}

// 获取玩家Id
// 返回值：
// 玩家Id
func (this *Client) GetPlayerId() int64 {
	return this.playerId
}

// 玩家登陆
// playerId：玩家Id
// 返回值：无
func (this *Client) PlayerLogin(playerId int64) {
	this.playerId = playerId
}

// 获取远程地址（IP_Port）
func (this *Client) GetRemoteAddr() string {
	items := strings.Split(this.conn.RemoteAddr().String(), ":")

	return fmt.Sprintf("%s_%s", items[0], items[1])
}

// 获取远程地址（IP）
func (this *Client) getRemoteShortAddr() string {
	items := strings.Split(this.conn.RemoteAddr().String(), ":")

	return items[0]
}

// 获取待发送的数据
// 返回值：
// 待发送数据项
// 是否含有有效数据
func (this *Client) getSendData() (responseObj *ServerResponseObject, exists bool) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	// 如果没有数据则直接返回
	if len(this.sendData) == 0 {
		return
	}

	// 取出第一条数据,并为返回值赋值
	responseObj = this.sendData[0]
	exists = true

	// 删除已经取出的数据
	this.sendData = this.sendData[1:]

	return
}

// 发送数据
// sendDataItemObj:待发送数据项
// 返回值：无
func (this *Client) SendMessage(responseObj *ServerResponseObject) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	this.sendData = append(this.sendData, responseObj)
}

// 清空待发送数据
func (this *Client) ClearSendData() {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	this.sendData = make([]*ServerResponseObject, 0, 16)
}

// 发送字节数组消息
// responseObj:返回值对象
func (this *Client) sendResponseObject(responseObj *ServerResponseObject) error {
	beforeTime := time.Now().Unix()

	// 序列化发送的数据
	content, _ := json.Marshal(responseObj)

	// 进行zlib压缩
	if config.GetBaseConfig().IfCompressData {
		content, _ = zlibUtil.Compress(content, zlib.DefaultCompression)
	}

	// 获得数据内容的长度
	contentLength := len(content)

	// 将长度转化为字节数组
	header := intAndBytesUtil.Int32ToBytes(int32(contentLength), byterOrder)

	// 将头部与内容组合在一起
	message := append(header, content...)

	// 发送消息
	if err := this.sendMessage(message); err != nil {
		return err
	}

	// 如果发送的时间超过3秒，则记录下来
	if time.Now().Unix()-beforeTime > 3 {
		logUtil.WarnLog("消息Size:%d, UseTime:%d", contentLength, time.Now().Unix()-beforeTime)
	}

	return nil
}

func (this *Client) sendMessage(message []byte) error {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	if _, err := this.conn.Write(message); err != nil {
		return err
	}

	return nil
}

// 客户端活跃
// 返回值：无
func (this *Client) Active() {
	atomic.StoreInt64(&this.activeTime, time.Now().Unix())
}

// 判断客户端是否超时（超过300秒不活跃算作超时）
// 返回值：是否超时
func (this *Client) Expired() bool {
	return time.Now().Unix() > this.activeTime+con_CLIENT_EXPIRE_SECONDS
}

// 客户端连接对象断开
// 返回值：无
func (this *Client) Close() {
	this.conn.Close()
	this.closed = true
	this.playerId = 0
}

// 格式化
func (this *Client) String() string {
	return fmt.Sprintf("{Id:%d, RemoteAddr:%s, activeTime:%s, playerId:%s}", this.id, this.GetRemoteAddr(), timeUtil.Format(time.Unix(this.activeTime, 0), "yyyy-MM-dd HH:mm:ss"), this.playerId)
}

// -----------------------------实现IConnection接口方法end-------------------------

// 客户端启动函数
func (this *Client) start() {
	go this.handleReceiveData()
	go this.handleSendData()
}

// 获取有效的消息
// 返回值：
// 消息内容
// 是否含有有效数据
func (this *Client) getReceiveData() (message []byte, exists bool) {
	// 判断是否包含头部信息
	if len(this.receiveData) < con_HEADER_LENGTH {
		return
	}

	// 获取头部信息
	header := this.receiveData[:con_HEADER_LENGTH]

	// 将头部数据转换为内部的长度
	contentLength := intAndBytesUtil.BytesToInt32(header, byterOrder)

	// 约定len(message) == 0,为心跳请求
	if contentLength == 0 {
		// 将对应的数据截断，以得到新的内容，并返回心跳内容
		this.receiveData = this.receiveData[con_HEADER_LENGTH:]
		if err := this.sendMessage([]byte{}); err != nil {
			return
		}
		return
	}

	// 判断长度是否满足
	if len(this.receiveData) < con_HEADER_LENGTH+int(contentLength) {
		return
	}

	// 提取消息内容
	message = this.receiveData[con_HEADER_LENGTH : con_HEADER_LENGTH+contentLength]
	exists = true

	// 将对应的数据截断，以得到新的数据
	this.receiveData = this.receiveData[con_HEADER_LENGTH+contentLength:]

	return
}

// 处理客户端收到的数据
func (this *Client) handleReceiveData() {
	// 处理goroutine数量
	goroutineName := "server_tcp.handleReceiveData"
	goroutineMgr.MonitorZero(goroutineName)
	defer goroutineMgr.ReleaseMonitor(goroutineName)
	defer clientMgr.Disconnect(this)

	// 无限循环，不断地读取数据，解析数据，处理数据
	for {
		if this.closed {
			break
		}

		// 先读取数据，每次读取1024个字节
		readBytes := make([]byte, 2048*2)

		// Read方法会阻塞，所以不用考虑异步的方式
		n, err := this.conn.Read(readBytes)
		if err != nil {
			break
		}

		// 将读取到的数据追加到已获得的数据的末尾，并更新activeTime
		this.receiveData = append(this.receiveData, readBytes[:n]...)
		this.Active()

		// 处理数据
		for {
			message, exists := this.getReceiveData()
			if !exists {
				break
			}

			clientMgr.HandleRequest(this, message)
		}
	}
}

// 处理需要客户端发送的数据
func (this *Client) handleSendData() {
	// 处理goroutine数量
	goroutineName := "server_tcp.handleSendData"
	goroutineMgr.MonitorZero(goroutineName)
	defer goroutineMgr.ReleaseMonitor(goroutineName)
	defer clientMgr.Disconnect(this)

	for {
		if this.closed {
			break
		}

		// 如果发送出现错误，表示连接已经断开，则退出方法；
		if sendDataItemObj, exists := this.getSendData(); exists {
			// 判断是否为断开客户端连接的数据
			if sendDataItemObj.IsDisconnect() {
				break
			}

			if err := this.sendResponseObject(sendDataItemObj); err != nil {
				return
			}
		} else {
			time.Sleep(5 * time.Millisecond)
		}
	}
}

// 新建客户端对象
// conn：连接对象
// 返回值：客户端对象的指针
func newClient(conn net.Conn) *Client {
	// 获得自增的id值
	getIncrementId := func() int32 {
		atomic.AddInt32(&globalClientId, 1)
		return globalClientId
	}

	return &Client{
		id:          getIncrementId(),
		conn:        conn,
		receiveData: make([]byte, 0, 2048*2),
		sendData:    make([]*ServerResponseObject, 0, 16),
		activeTime:  time.Now().Unix(),
		playerId:    0,
	}
}
