const supportPostMessage = 'postMessage' in window

const serverMessageName = 'AMPframe'
const serverProjectName = 'AMP'

const clientMessageName = 'AMPclient'
const clientProjectName = 'AMP'
const clientTargetName = 'AMPframe'

class Target {
    constructor(target, name, prefix) {
        let errMsg = ''
        if (arguments.length < 2) {
            errMsg = 'target error - target and name are both required'
        } else if (typeof target !== 'object') {
            errMsg = 'target error - target itself must be window object'
        } else if (typeof name !== 'string') {
            errMsg = 'target error - target name must be string type'
        }
        if (errMsg) {
            throw new Error(errMsg)
        }
        this.target = target
        this.name = name
        this.prefix = prefix
    }
    send(msg) {
        if (supportPostMessage) {
            this.target.postMessage(`${this.prefix}|${this.name}__Messenger__${msg}`, '*')
        } else {
            const targetFunc = window.navigator[this.prefix + this.name]
            if (typeof targetFunc === 'function') {
                targetFunc(this.prefix + msg, window)
            } else {
                throw new Error('target callback function is not defined')
            }
        }
    }
}

class EfuMessenger {
    constructor(messengerName, projectName) {
        this.targets = {}
        this.name = messengerName
        this.listenFunc = []
        this.prefix = projectName
        this.initListen()
    }
    addTarget(target, name) {
        const targetObj = new Target(target, name, this.prefix)
        this.targets[name] = targetObj
    }
    listen(callback) {
        let i = 0
        const len = this.listenFunc.length
        let cbIsExist = false
        for (; i < len; i++) {
            // 增加多组件对象定义重复声明判断
            let o_fn_str = this.listenFunc[i].toString()
            let n_fn_str = callback.toString()
            if (o_fn_str === n_fn_str) {
                this.listenFunc[i] = callback
                cbIsExist = true
                break
            }
        }
        if (!cbIsExist) {
            this.listenFunc.push(callback)
        }
    }
    clear() {
        this.listenFunc = []
    }
    send(msg) {
        const { targets } = this
        let target
        for (target in targets) {
            if (targets.hasOwnProperty(target)) {
                targets[target].send(msg)
            }
        }
    }
    initListen() {
        const self = this
        const generalCallback = function (msg) {
            if (typeof msg === 'object' && msg.data) {
                msg = msg.data
            }
            if (typeof msg === 'string') {
                const msgPairs = msg.split('__Messenger__')
                var msg = msgPairs[1]
                const pairs = msgPairs[0].split('|')
                const prefix = pairs[0]
                const name = pairs[1]
                for (let i = 0; i < self.listenFunc.length; i++) {
                    if (prefix + name === self.prefix + self.name) {
                        self.listenFunc[i](msg)
                    }
                }
            }
        }
        if (supportPostMessage) {
            if ('addEventListener' in document) {
                window.addEventListener('message', generalCallback, false)
            } else if ('attachEvent' in document) {
                window.attachEvent('onmessage', generalCallback)
            }
        } else {
            // 兼容IE 6/7
            window.navigator[this.prefix + this.name] = generalCallback
        }
    }
}

class server {
    constructor(messageName, projectName) {
        this.messageName = messageName || serverMessageName
        this.projectName = projectName || serverProjectName
        return new EfuMessenger(this.messageName, this.projectName)
    }
}

class client {
    constructor(targetName, messageName, projectName) {
        this.messengerOBJ = null
        this.messageName = messageName || clientMessageName
        this.targetName = targetName || clientTargetName
        this.projectName = projectName || clientProjectName
        this.init()
    }
    init() {
        this.messengerOBJ = new EfuMessenger(this.messageName, this.projectName)
        this.messengerOBJ.addTarget(window.parent, this.targetName)
    }
    openPortalTabPage(params) {
        this.messengerOBJ.targets[this.targetName].send(
            JSON.stringify({
                name: this.messageName,
                action: 'openPortalTabPage',
                params: { ...params }
            })
        )
    }
    openPortalTabByModuleCode(params) {
        this.messengerOBJ.targets[this.targetName].send(
            JSON.stringify({
                name: this.messageName,
                action: 'openPortalTabByModuleCode',
                params: { ...params }
            })
        )
    }
}

export { EfuMessenger, server, client }
