/**
 * @file    ws-client.js
 * @brief   WebSocket 客户端封装 — 自动重连 + 事件分发
 */


var WsClient = {
	ws: null, url: '', status: 'disconnected', timer: null,
	msgCbs: [], statusCbs: [],

	onMsg: function(cb)     { this.msgCbs.push(cb); },
	onStatus: function(cb)  { this.statusCbs.push(cb); },

	connect: function(url) {
		if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING))
		{
			return;
		}

		this.url = url;
		this._setStatus('connecting');

		try
		{
			this.ws = new WebSocket(url);
			this.ws.binaryType = 'arraybuffer';
		}
		catch (e)
		{
			this._setStatus('disconnected');
			this._schedule();
			return;
		}

		this.ws.onopen = function() {
			var self = WsClient;
			self._setStatus('connected');
			if (self.timer) { clearTimeout(self.timer); self.timer = null; }
			self.send({ type: 'get_cmds' });
		};

		this.ws.onclose = function() {
			var self = WsClient;
			self._setStatus('disconnected');
			self.ws = null;
			self._schedule();
		};

		this.ws.onerror = function() {};

		this.ws.onmessage = function(e) {
			var self = WsClient;
			var raw = e.data;

			if (raw instanceof ArrayBuffer)
			{
				self.msgCbs.forEach(function(cb) { try { cb(null, raw); } catch(ex) {} });
				return;
			}

			var data = null;
			try { data = JSON.parse(raw); } catch(ex) {}

			self.msgCbs.forEach(function(cb) { try { cb(data || raw, null); } catch(ex) {} });
		};
	},

	send: function(obj) {
		if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
		{
			if (typeof toast === 'function') { toast(I18n.t('toast.ws_error'), 'error'); }
			return;
		}

		var raw = (typeof obj === 'object') ? JSON.stringify(obj) : String(obj);
		this.ws.send(raw);
	},

	_setStatus: function(s) {
		if (this.status === s) return;
		this.status = s;

		var dot = document.getElementById('nav-status-dot');
		var txt = document.getElementById('nav-status-text');
		if (dot) { dot.className = 'nav-status-dot ' + s; }
		if (txt)
		{
			var map = { connected: I18n.t('status.connected'), disconnected: I18n.t('status.disconnected'), connecting: I18n.t('status.connecting') };
			txt.textContent = map[s] || s;
		}

		this.statusCbs.forEach(function(cb) { cb(s); });
	},

	_schedule: function() {
		var self = this;
		if (self.timer) return;
		self.timer = setTimeout(function() {
			self.timer = null;
			if (self.status === 'disconnected') { self.connect(self.url); }
		}, 3000);
	}
};
