/**
 * @Author: LiXiang
 * @Data: 2020/1/4 下午2:43
 */
package tpro

import (
	"errors"
	"sync"
)

// TPSessionPool TP服务端连接池
type TPSessionPool interface {
	GetSession(id string) (TPSession, bool)
	AddSession(id string, session TPSession) (bool, error)
	DeleteSession(id string) bool
	Closes()
	Size() int
	SetAddAfter(func(session TPSession)) // 添加连接时的回调
	SetAddBefore(func(session TPSession) error)
	SetDeleteCallBack(func(session string)) // 删除链接时的回调
}

func NewTPSessionPool() TPSessionPool {
	return &tpSessionPool{
		mutex:          sync.RWMutex{},
		pool:           make(map[string]TPSession),
		isClose:        false,
		addAfter:       nil,
		addBefore:      nil,
		deleteCallBack: nil,
	}
}

type tpSessionPool struct {
	mutex          sync.RWMutex
	pool           map[string]TPSession
	isClose        bool
	addAfter       func(session TPSession)
	addBefore      func(session TPSession) error
	deleteCallBack func(sessionId string)
}

func (t *tpSessionPool) SetAddAfter(f func(session TPSession)) {
	t.addAfter = f
	// atomic.StoreUintptr((*uintptr)(unsafe.Pointer(&t.addAfter)), *(*uintptr)(unsafe.Pointer(&f)))
}
func (t *tpSessionPool) SetAddBefore(f func(session TPSession) error) {
	t.addBefore = f
}
func (t *tpSessionPool) SetDeleteCallBack(f func(session string)) {
	t.deleteCallBack = f
	// atomic.StoreUintptr((*uintptr)(unsafe.Pointer(&t.deleteCallBack)), *(*uintptr)(unsafe.Pointer(&f)))
}

func (t *tpSessionPool) Size() int {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return len(t.pool)
}
func (t *tpSessionPool) GetSession(id string) (session TPSession, isExist bool) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	session, isExist = t.pool[id]
	return
}

func (t *tpSessionPool) DeleteSession(id string) (isExist bool) {
	t.mutex.Lock()
	_, isExist = t.pool[id]
	if isExist {
		_ = t.pool[id].Close()
		delete(t.pool, id)
		defer t.deleteCallBack(id)
	}
	t.mutex.Unlock()
	return isExist
}

func (t *tpSessionPool) Closes() {
	t.mutex.Lock()
	if t.isClose {
		t.mutex.Unlock()
		return
	}
	t.isClose = true
	t.mutex.Unlock()
	for k, _ := range t.pool {
		_ = t.DeleteSession(k)
	}

}

func (t *tpSessionPool) AddSession(id string, service TPSession) (isExist bool, err error) {
	if err := t.addBefore(service); err != nil { // 无论manager是否被关闭都会调用
		return false, err
	}
	t.mutex.Lock()
	if t.isClose {
		t.mutex.Unlock()
		return false, errors.New("sessionPool is close")
	}
	defer t.addAfter(service)
	_, isExist = t.pool[id]
	t.pool[id] = service
	t.mutex.Unlock()
	return isExist, nil
}
