package net

import (
	"github.com/gorilla/websocket"
	"log/slog"
	"slgserver/server/slgserver/conn"
	"slgserver/server/slgserver/pos"
	"sync"
	"sync/atomic"
)

var ConnMgr = Mgr{}
var cid int64 = 0

type Mgr struct {
	cm sync.RWMutex
	um sync.RWMutex
	rm sync.RWMutex

	connCache map[int64]WSConn
	userCache map[int]WSConn
	roleCache map[int]WSConn
}

func (this *Mgr) NewConn(wsSocket *websocket.Conn, needSecret bool) *ServerConn {
	this.cm.Lock()
	defer this.cm.Unlock()

	id := atomic.AddInt64(&cid, 1)
	if this.connCache == nil {
		this.connCache = make(map[int64]WSConn)
	}

	if this.userCache == nil {
		this.userCache = make(map[int]WSConn)
	}

	if this.roleCache == nil {
		this.roleCache = make(map[int]WSConn)
	}

	c := NewServerConn(wsSocket, needSecret)
	c.SetProperty("cid", id)
	this.connCache[id] = c

	return c
}

func (this *Mgr) UserLogin(conn WSConn, session string, uid int) {
	this.um.Lock()
	defer this.um.Unlock()

	oldConn, ok := this.userCache[uid]
	if ok {
		if conn != oldConn {
			slog.Warn("rob login",
				"uid", uid,
				"oldAddr", oldConn.Addr(),
				"newAddr", conn.Addr())

			//这里需要通知旧端被抢登录
			oldConn.Push("robLogin", nil)
		}
	}
	this.userCache[uid] = conn
	conn.SetProperty("session", session)
	conn.SetProperty("uid", uid)
}

func (this *Mgr) UserLogout(conn WSConn) {
	this.removeUser(conn)
}

func (this *Mgr) removeUser(conn WSConn) {
	this.um.Lock()
	uid, err := conn.GetProperty("uid")
	if err == nil {
		//只删除自己的conn
		id := uid.(int)
		c, ok := this.userCache[id]
		if ok && c == conn {
			delete(this.userCache, id)
		}
	}
	this.um.Unlock()

	this.rm.Lock()
	rid, err := conn.GetProperty("rid")
	if err == nil {
		//只删除自己的conn
		id := rid.(int)
		c, ok := this.roleCache[id]
		if ok && c == conn {
			delete(this.roleCache, id)
		}
	}
	this.rm.Unlock()

	conn.RemoveProperty("session")
	conn.RemoveProperty("uid")
	conn.RemoveProperty("role")
	conn.RemoveProperty("rid")
}

func (this *Mgr) RoleEnter(conn WSConn, rid int) {
	this.rm.Lock()
	defer this.rm.Unlock()
	conn.SetProperty("rid", rid)
	this.roleCache[rid] = conn
}

func (this *Mgr) RemoveConn(conn WSConn) {
	this.cm.Lock()
	cid, err := conn.GetProperty("cid")
	if err == nil {
		delete(this.connCache, cid.(int64))
		conn.RemoveProperty("cid")
	}
	this.cm.Unlock()

	this.removeUser(conn)
}

func (this *Mgr) PushByRoleId(rid int, msgName string, data interface{}) bool {
	if rid <= 0 {
		return false
	}
	this.rm.Lock()
	defer this.rm.Unlock()
	conn, ok := this.roleCache[rid]
	if ok {
		conn.Push(msgName, data)
		return true
	} else {
		return false
	}
}

func (this *Mgr) Count() int {
	this.cm.RLock()
	defer this.cm.RUnlock()

	return len(this.connCache)
}

func (this *Mgr) Push(pushSync conn.PushSync) {

	proto := pushSync.ToProto()
	belongRIds := pushSync.BelongToRId()
	isCellView := pushSync.IsCellView()
	x, y := pushSync.Position()
	cells := make(map[int]int)

	//推送给开始位置
	if isCellView {
		cellRIds := pos.RPMgr.GetCellRoleIds(x, y, 8, 6)
		for _, rid := range cellRIds {
			//是否能出现在视野
			if can := pushSync.IsCanView(rid, x, y); can {
				this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
				cells[rid] = rid
			}
		}
	}

	//推送给目标位置
	tx, ty := pushSync.TPosition()
	if tx >= 0 && ty >= 0 {
		var cellRIds []int
		if isCellView {
			cellRIds = pos.RPMgr.GetCellRoleIds(tx, ty, 8, 6)
		} else {
			cellRIds = pos.RPMgr.GetCellRoleIds(tx, ty, 0, 0)
		}

		for _, rid := range cellRIds {
			if _, ok := cells[rid]; !ok {
				if can := pushSync.IsCanView(rid, tx, ty); can {
					this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
					cells[rid] = rid
				}
			}
		}
	}

	//推送给自己
	for _, rid := range belongRIds {
		if _, ok := cells[rid]; !ok {
			this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
		}
	}

}

func (this *Mgr) pushAll(msgName string, data interface{}) {

	this.rm.Lock()
	defer this.rm.Unlock()
	for _, conn := range this.roleCache {
		conn.Push(msgName, data)
	}
}
