import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import size from 'lodash-es/size.js'
import evem from './evem.mjs'
import ispint from './ispint.mjs'
import isestr from './isestr.mjs'
import iseobj from './iseobj.mjs'
import haskey from './haskey.mjs'
import cint from './cint.mjs'
/**
* 偵測單元是否在線
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/alive.test.mjs Github}
* @memberOf wsemi
* @param {Integer} [timeAlive=10000] 輸入判斷單元是否斷線之延時整數,單位為毫秒ms,預設為10000
* @param {Integer} [timeDetect=50] 輸入偵測佇列間隔時間整數,單位為毫秒ms,預設為50
* @returns {Object} 回傳事件物件,可呼叫事件on、trigger、get。trigger給予單元的唯一key字串與攜帶數據data物件,on為監聽事件,需自行監聽message事件取得單元進出事件。get事件可取得alive內視為存活的單元清單
* @example
*
* async function topAsync() {
*
* function test1() {
* return new Promise((resolve, reject) => {
* let ms = []
*
* let oAL = alive({ timeAlive: 1500 })
* let t = Date.now()
*
* let a = { data: 123 }
* let b = { data: '34.56' }
*
* setTimeout(() => {
* console.log(parseInt((Date.now() - t)) + 'ms', 'trigger a1')
* oAL.trigger('a', a)
* }, 500)
*
* setTimeout(() => {
* console.log(parseInt((Date.now() - t)) + 'ms', 'trigger a2')
* oAL.trigger('a', a)
* }, 1900)
*
* setTimeout(() => {
* console.log(parseInt((Date.now() - t)) + 'ms', 'trigger b1')
* oAL.trigger('b', b)
* }, 1000)
*
* setTimeout(() => {
* console.log(parseInt((Date.now() - t)) + 'ms', 'trigger b2')
* oAL.trigger('b', b)
* }, 3000)
*
* oAL.on('message', function({ eventName, key, data, now }) {
* console.log(parseInt((Date.now() - t)) + 'ms', { eventName, key, data, now })
* ms.push(eventName + '|' + key)
* })
*
* setTimeout(() => {
* resolve(ms)
* }, 5000)
*
* })
* }
* console.log('test1')
* let r1 = await test1()
* console.log(JSON.stringify(r1))
* // test1
* // 503ms trigger a1
* // 508ms { eventName: 'enter', key: 'a', data: { data: 123 }, now: 1 }
* // 1001ms trigger b1
* // 1003ms { eventName: 'enter', key: 'b', data: { data: '34.56' }, now: 2 }
* // 1901ms trigger a2
* // 2523ms { eventName: 'leave', key: 'b', data: { data: '34.56' }, now: 1 }
* // 3002ms trigger b2
* // 3004ms { eventName: 'enter', key: 'b', data: { data: '34.56' }, now: 2 }
* // 3430ms { eventName: 'leave', key: 'a', data: { data: 123 }, now: 1 }
* // 4544ms { eventName: 'leave', key: 'b', data: { data: '34.56' }, now: 0 }
* // ["enter|a","enter|b","leave|b","enter|b","leave|a","leave|b"]
*
* }
* topAsync().catch(() => {})
*
*/
function alive(opt = {}) {
let ev = evem()
let q = {} //queue
let t = null //timer
//timeAlive
let timeAlive = get(opt, 'timeAlive')
if (!ispint(timeAlive)) {
timeAlive = 10000
}
timeAlive = cint(timeAlive)
//timeDetect
let timeDetect = get(opt, 'timeDetect')
if (!ispint(timeDetect)) {
timeDetect = 50
}
timeDetect = cint(timeDetect)
function detect() {
if (t !== null) {
return
}
t = setInterval(() => {
//console.log('q', q)
//detect
each(q, (v, key) => {
//t
let t = Date.now() - v.time
//check, 超過指定延時則視為離開
if (t > timeAlive) {
//cloneDeep
let r = cloneDeep(q[key])
//刪除佇列內key紀錄
delete q[key]
//emit leave
ev.emit('message', { eventName: 'leave', key, data: r.data, now: size(q) })
}
})
//clear, 當無任何訊息存在
if (!iseobj(q)) {
clearInterval(t)
t = null
}
}, timeDetect)
}
function trigger(key, data) {
//check
if (!isestr(key)) {
console.log('trigger need key')
return
}
//check
if (!haskey(q, key)) {
setTimeout(() => { //因需判斷是否為新單元故需放於update前, 而emit內可能會被存取q, 故需要用setTimeout脫勾使q為被更新資訊, 才能正確取得當前單元數量
//emit enter
ev.emit('message', { eventName: 'enter', key, data, now: size(q) })
}, 1)
}
//update
q[key] = { data, time: Date.now() }
//detect
detect()
}
function _get() {
let rs = []
each(q, (v, k) => {
rs.push({ key: k, data: v.data })
})
rs = cloneDeep(rs) //使用cloneDeep避免外部修改內部數據
return rs
}
//save
ev.trigger = trigger
ev.get = _get
return ev
}
export default alive