class SocketMgr {
	private static _instance: SocketMgr;
	private debugUrl: string = 'test-ws-baccarat.eosget.io';
	private releaseUrl: string = 'ws-baccarat.eosget.io';//
	static get instance(): SocketMgr {
		if (!this._instance) {
			this._instance = new SocketMgr();
		}
		return this._instance;
	}

	get url(): string {
		return ''
	}

	// 以下方法提供给WebSocket+protobuf使用

	private socketClient: egret.WebSocket;
	private heartBeatTimer: egret.Timer;
	public heartBeatTimes: number = 0;
	private reconnectTimer: egret.Timer;
	private connected = false;
	private reconnecting = false;
	private reconnectTimes: number = 0;
	private connectedCBs: { callback: Function, thisObj: any }[] = [];
	private reconnectCBs: { callback: Function, thisObj: any }[] = [];
	private errorCBs: { callback: Function, thisObj: any }[] = [];
	private needReconnect = false;
	private hideLoading = true;

	public constructor() {
	}

	/**
	 * 发送非protobuf消息
	 */
	public sendNoProtobufMsg(data: Object | string): void {
		if (this.socketClient == null) return;
		let msg = null
		if (data.constructor === Object) {
			msg = JSON.stringify(data)
		}
		DEBUG && console.log(`[Send To Server]protoId:${msg}`);
		this.socketClient.writeUTF(msg)
	}

	openSocket(): void {
		if (this.socketClient) return

		this.socketClient = new egret.WebSocket();
		this.socketClient.addEventListener(egret.ProgressEvent.SOCKET_DATA, this.onReceive, this);
		this.socketClient.addEventListener(egret.Event.CONNECT, this.onConnect, this);
		this.socketClient.addEventListener(egret.Event.CLOSE, this.onClose, this);
		this.socketClient.addEventListener(egret.IOErrorEvent.IO_ERROR, this.onError, this);
		this.socketClient.type = egret.WebSocket.TYPE_STRING;

		this.socketClient.connectByUrl(`wss://${this.url}`)
	}
	/**
 * 连接上服务器的回调
 */
	private onConnect(): void {
		DEBUG && console.log("已连上服务器！");
		this.connected = true;
		this.reconnectTimes = 0;
		// 心跳
		if (this.heartBeatTimer) {
			this.heartBeatTimer.stop();
		}
		this.heartBeatTimer = new egret.Timer(5000, 0);
		this.heartBeatTimer.addEventListener(egret.TimerEvent.TIMER, this.onHeartBeat, this);
		this.heartBeatTimer.start();

		this.connectedCBs.forEach((v) => {
			v.callback.call(v.thisObj);
		});

		GameMgr.instance.dispatchEvent(new SocketEvent(SocketEvent.RECONNECT_MSG));

		if (this.reconnecting) {
			this.reconnecting = false;
		}
	}

	private onReceive(ev: egret.Event): void {
		let msgStr = this.socketClient.readUTF();
		if (!msgStr) return
		let msg = JSON.parse(msgStr)
		DEBUG && console.log(`[Receive From Server]:${msgStr}`);
		let socketEv = new SocketEvent(SocketEvent.PROTO_MSG);
		socketEv.protoId = msg.channel
		socketEv.data = msg.data
		GameMgr.instance.dispatchEvent(socketEv);
	}

	public onClose(): void {
		DEBUG && console.log("socket closed!");

		if (this.connected) {
			this.connected = false;
			this.heartBeatTimer.stop();
			this.heartBeatTimer = null;
		}

		this.reconnect();
	}

	private reconnect(): void {
		this.reconnectTimes++;
		if (this.reconnectTimes >= 3) {
			DEBUG && console.log('网络状况较差，请检查网络');
			return;
		}
		DEBUG && console.log("Reconnect login!");
		this.connectedCBs = [];
		this.errorCBs = [];
		this.reconnecting = true;

		if (this.socketClient) {
			this.socketClient.removeEventListener(egret.ProgressEvent.SOCKET_DATA, this.onReceive, this);
			this.socketClient.removeEventListener(egret.Event.CONNECT, this.onConnect, this);
			this.socketClient.removeEventListener(egret.Event.CLOSE, this.onClose, this);
			this.socketClient.removeEventListener(egret.IOErrorEvent.IO_ERROR, this.onError, this);
			this.socketClient = null;
		}

		this.openSocket();
	}

	/**
	 * 连接发生错误
	 */
	private onError(res: any) {
		console.error("网络连接异常中断");

		this.errorCBs.forEach((v) => {
			v.callback.call(v.thisObj, res.errMsg);
		});

		this.onClose();
	}

	private onHeartBeat(): void {
		let msg = { act: 'alive' }
		this.sendNoProtobufMsg(msg)
	}

	/**
     * 添加连上服务器监听
     */
	public addConnectListener(callback: Function, thisObj: any): void {
		this.connectedCBs.push({ callback, thisObj });
	}

    /**
     * 添加连接错误监听
     */
	public addErrorListener(callback: Function, thisObj: any): void {
		this.errorCBs.push({ callback, thisObj });
	}

}