/**
 * Initialize a new `Emitter`.
 *
 * @api public
 */

function Emitter(obj) {
    if (obj) return mixin(obj);
};

/**
 * Mixin the emitter properties.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

function mixin(obj) {
    for (var key in Emitter.prototype) {
        obj[key] = Emitter.prototype[key];
    }
    return obj;
}

/**
 * Listen on the given `event` with `fn`.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.on =
Emitter.prototype.addEventListener = function (event, fn) {
    this._callbacks = this._callbacks || {};
    (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
    .push(fn);
    return this;
};

/**
 * Adds an `event` listener that will be invoked a single
 * time then automatically removed.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.once = function (event, fn) {
    function on() {
        this.off(event, on);
        fn.apply(this, arguments);
    }

    on.fn = fn;
    this.on(event, on);
    return this;
};

/**
 * Remove the given callback for `event` or all
 * registered callbacks.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function (event, fn) {
    this._callbacks = this._callbacks || {};

    // all
    if (0 == arguments.length) {
        this._callbacks = {};
        return this;
    }

    // specific event
    var callbacks = this._callbacks['$' + event];
    if (!callbacks) return this;

    // remove all handlers
    if (1 == arguments.length) {
        delete this._callbacks['$' + event];
        return this;
    }

    // remove specific handler
    var cb;
    for (var i = 0; i < callbacks.length; i++) {
        cb = callbacks[i];
        if (cb === fn || cb.fn === fn) {
            callbacks.splice(i, 1);
            break;
        }
    }
    return this;
};

/**
 * Emit `event` with the given args.
 *
 * @param {String} event
 * @param {Mixed} ...
 * @return {Emitter}
 */

Emitter.prototype.emit = function (event) {
    this._callbacks = this._callbacks || {};
    var args = [].slice.call(arguments, 1),
        callbacks = this._callbacks['$' + event];

    if (callbacks) {
        callbacks = callbacks.slice(0);
        for (var i = 0, len = callbacks.length; i < len; ++i) {
            callbacks[i].apply(this, args);
        }
    }

    return this;
};

/**
 * Return array of callbacks for `event`.
 *
 * @param {String} event
 * @return {Array}
 * @api public
 */

Emitter.prototype.listeners = function (event) {
    this._callbacks = this._callbacks || {};
    return this._callbacks['$' + event] || [];
};

/**
 * Check if this emitter has `event` handlers.
 *
 * @param {String} event
 * @return {Boolean}
 * @api public
 */

Emitter.prototype.hasListeners = function (event) {
    return !!this.listeners(event).length;
};

function bind(obj, fn) {
    if ('string' == typeof fn) fn = obj[fn];
    if ('function' != typeof fn) throw new Error('bind() requires a function');
    var args = [].slice.call(arguments, 2);
    return function () {
        return fn.apply(obj, args.concat([].slice.call(arguments)));
    }
};

/**
 * WebSocket管理器
 * 支持最多同时实例化5个socket连接（微信环境限制目前最多为5个）
 * @param {String} url socket地址
 * @param {Object} opts 配置
 * 
 */
function WsManager(url, opts) {
    if(!(this instanceof WsManager)) {
        return new WsManager(url, opts)
    }

    if(url && (typeof url == 'object')) {
        opts = url
        url = undefined
    }

    Emitter(this)

    opts = opts || {}

    opts.path = opts.path || '/'

    this.opts = opts
    this.url = url
    this.lastPing = null
    this.socketCache = [] //缓存socket队列
    this.socketMaxCache = 5 //最大缓存socket实例数量
    this.readyState = 'closed' //当前socket状态
    this.binaryType = opts.binaryType || 'blob' //数据传输类型
    this._reconnectTimes = 0 //重连次数
    this._reconnectionDelay = opts.reconnectionDelay || 1000 //重连延迟
    this.reconnection(opts.reconnection !== false) // 是否自动重连
    this.reconnectionAttempts(opts.reconnectionAttempts || Infinity) //重连最大尝试次数
    this.timeout(null == opts.timeout ? 20000 : opts.timeout)
    this.logStyle = 'color:blue; font-size:16px;font-weight:bold;'
    this.keepAliveInterval = 20000
    this.keepAliveTimeout = null
    this.keepAliveMsg = '0'

    this.autoConnect = opts.autoConnect !== false //是否自动连接
    if(this.autoConnect) {
        this.connect()
    }
}


WsManager.prototype.connect = function(fn) {
    if(~this.readyState.indexOf('open')) {
        return this
    }

    this.readyState = 'opening'
    let _socket = CC_WECHATGAME ? this.openWxConnect() : this.openH5Connect()
    this.socketCache.push(_socket)
    this.socket = _socket
}

WsManager.prototype.reconnect = function() {
    clearInterval(this.keepAliveTimeout)

    if(this._reconnectTimes < this._reconnectionAttempts) {
        if(this.socket) {
            this.socket.close()
        }
        this._reconnectTimes += 1

        console.log(`%c [Socket正在尝试第${this._reconnectTimes}次重连]`, this.logStyle);
        this.readyState = 'reconnecting'
        setTimeout(() => {
            this.socket = CC_WECHATGAME ? this.openWxConnect() : this.openH5Connect()
        }, this._reconnectionDelay);
    } else {
        if(this.socket) {
            this.socket.close()
        }
        console.log(`%c [达到最大重连失败次数，Socket关闭]`, this.logStyle);
    }
}

WsManager.prototype.keepAlive = function() {
    let alivemsg = this.keepAliveMsg
    this.keepAliveTimeout = setInterval(() => {
        if(CC_WECHATGAME) {
            this.socket.send({
                data: alivemsg
            })
        } else {
            this.socket.binaryType = 'blob'
            this.socket.send(alivemsg)
        }
    }, this.keepAliveInterval)
}

WsManager.prototype.openWxConnect = function() {
    let _header = {}
    let _socket = wx.connectSocket({
        url: this.url,
        header: _header,
        success: function(ret) {}
    })

    _socket.onOpen((res) => {
        this.readyState = 'open'
        this.emit('open', res)
        console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);

        // 每隔一段时间发一个心跳包保持连接状态
        this.keepAlive()
    })

    _socket.onClose((res) => {
        this.readyState = 'closed'
        
        //只要关闭就重连（暂时性处理）
        if(this._reconnection) {
            this.reconnect()
        }

        this.emit('close', res)
        console.log(`%c [Socket连接关闭: ${res}]`, this.logStyle)
    })

    _socket.onMessage((res) => {
        if(res.data != 1) {
            this.emit('message', res.data)
        }
        console.log(`%c [接收到Socket消息: ${JSON.stringify(res.data)}]`, this.logStyle);
    })

    _socket.onError((res) => {
        if(this._reconnection) {
            this.reconnect()
        } else {
            _socket.close()
        }

        this.emit('error', res.errMsg)
        console.log(`%c [Socket错误: ${res.errMsg}]`, this.logStyle);
    })

    return _socket
}

WsManager.prototype.openH5Connect = function() {
    let _socket = new WebSocket(this.url);

    _socket.onopen = (event) => {
        this.readyState = 'open'
        this.emit('open', event)
        console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);

        // 每隔一段时间发一个心跳包保持连接状态
        this.keepAlive()
    }

    _socket.onclose = (event) => {
        this.readyState = 'closed';
        let code = event.code
        let reason = event.reason
        let wasClean = event.wasClean

        //只要关闭就重连（暂时性处理）
        if(this._reconnection) {
            this.reconnect()
        }

        this.emit('close', event)
        console.log(`%c [Socket连接关闭: ${reason}]`, this.logStyle)
    }

    _socket.onmessage = (event) => {
        if(event.data != 1) {
            this.emit('message', event.data)
        }
        console.log(`%c [接收到Socket消息: ${event.data}]`, this.logStyle);
    }

    _socket.onerror = (event) => {
        if(this._reconnection) {
            this.reconnect()
        } else {
            _socket.close()
        }

        this.emit(event)
        console.log(`%c [Socket错误: ${JSON.stringify(event)}]`, this.logStyle);
    }
    return _socket
}

WsManager.prototype.send = function(data) {
    console.log(`%c [发送Socket数据: ${data}]`, this.logStyle);

    if(CC_WECHATGAME) {
        let buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
        this.socket.send({
            data: buffer,
            success: function(res) {
                console.log('Success: ', JSON.stringify(res));
            },
            fail: function(res) {
                console.log('Fail: ', JSON.stringify(res));
            },
            complete: function(res) {
                console.log('Complete: ', JSON.stringify(res));
            }
        })
    } else {
        this.socket.binaryType = this.binaryType
        this.socket.send(data)
    }
}

// WsManager.prototype.onOpen = function(res) {
//     if(CC_WECHATGAME) {
//         this.socket.onOpen((res) => {
//             this.readyState = 'open'
//             callback && callback(res)
//             console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);
//         })
//     } else {
//         this.socket.onopen = () => {
//             this.readyState = 'open'
//             callback && callback()
//             console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);
//         }
//     } 
// }

// WsManager.prototype.onClose = function(callback) {
//     if(CC_WECHATGAME) {
//         this.socket.onClose((res) => {
//             this.readyState = 'closed'
//             callback && callback(res)
//             console.log(`%c [Socket连接关闭: ${res}]`, this.logStyle)
//         })
//     } else {
//         this.socket.onclose = (event) => {
//             this.readyState = 'closed';
//             let code = event.code
//             let reason = event.reason
//             let wasClean = event.wasClean
//             callback && callback(event)
//             console.log(`%c [Socket连接关闭: ${reason}]`, this.logStyle)
//         }
//     }
// }

// WsManager.prototype.onMessage = function(callback) {
//     if(CC_WECHATGAME) {
//         this.socket.onMessage((res) => {
//             let data = res.data
//             callback && callback(data)
//             console.log(`%c [接收到Socket消息: ${JSON.stringify(data)}]`, this.logStyle);
//         })
//     } else {
//         this.socket.onmessage = (event) => {
//             let data = event.data
//             callback && callback(data)
//             console.log(`%c [接收到Socket消息: ${JSON.stringify(data)}]`, this.logStyle);
//         }
//     }
// }

// WsManager.prototype.onError = function(callback) {
//     if(CC_WECHATGAME) {
//         this.socket.onError((res) => {
//             callback && callback(res.errMsg)
//             console.log(`%c [Socket错误: ${res.errMsg}]`, this.logStyle);
//         })
//     } else {
//         this.socket.onerror = (event) => {
//             callback && callback(event.data)
//             console.log(`%c [Socket错误: ${event.data}]`, this.logStyle);
//         }
//     }
// }

/**
 * 配置socket连接超时时间
 * @param {Number} v 连接超时时间
 * @return {WsManager} WsManager对象实例
 * @api public
 */
WsManager.prototype.timeout = function(v) {
    if(!arguments.length) {
        return this._timeout
    }
    this._timeout = v
    return this
}

/**
 * 自动重连配置
 * @param {Boolean} v 是否自动重连true / false
 * @return {WsManager} WsManager对象实例
 * @api public
 */
WsManager.prototype.reconnection = function(v) {
    if(!arguments.length) {
        return this._reconnection
    }
    this._reconnection = !!v
    return this
}

/**
 * 配置最大重连次数
 * @param {Number} v 最大重连次数
 * @return {WsManager} WsManager对象实例
 * @api public
 */
WsManager.prototype.reconnectionAttempts = function(v) {
    if (!arguments.length) {
        return this._reconnectionAttempts
    }
    this._reconnectionAttempts = v
    return this
}

module.exports = WsManager

// var Ws = function() {
//     this.socket = new io(Api.SocketLocal, {
//         autoConnect: false,         //自动连接
//         reconnection: true,         //断开自动重连
//         reconnectionDelay: 2000,    //自动重连时间，默认为2000毫秒
//         reconnectionAttempts: 10,   //最大重连尝试次数，默认为Infinity
//         forceNew: true,             //
//     })
//     this.socket.on("connect", () => {
//         console.log(`Socket connected: ${Api.SocketLocal}`);
//     })
// }

// Ws.prototype.open = function() {
//     this.socket.open()
// }

// /**
//  * 监听Websocket事件
//  * @param {String} eventName 监听事件名称
//  * @param {Function} callback 事件触发回调
//  */
// Ws.prototype.on = function(eventName, callback) {
//     if(eventName) {
//         this.socket.on(eventName, data => {
//             console.log(`Socket On Event: ${eventName}`);
//             callback && callback(data)
//         })
//     }
// }

// /**
//  * 上报Websocket报文
//  * @param {String} eventName 上报事件名
//  * @param {Object} data 上报数据
//  */
// Ws.prototype.emit = function(eventName, data) {
//     if(eventName) {
//         console.log(`Socket Do Emit: ${eventName}`, data);
//         this.socket.emit(eventName, data);
//     }
// }

// module.exports = new Ws();