/**
 * @file    ui.js
 * @brief   通用 UI 组件 — Toast, Modal, Table, Icon, Utilities
 */


/* ==================================================================
 * SVG 内联图标 (Lucide 风格, 18×18)
 * ================================================================== */
var Icon = {
	_map: {
		monitor:   '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>',
		dashboard: '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>',
		signal:    '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>',
		link:      '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>',
		zap:       '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>',
		sliders:   '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>',
		settings:  '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>',
		database:  '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>',
		bug:       '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="5" width="16" height="12" rx="3"/><path d="M8 5V3h8v2M12 12v4M8 12h.01M16 12h.01"/></svg>',
		cpu:       '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></svg>',
		activity:  '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>',
		terminal:  '<svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>',
		check:     '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>',
		plus:      '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>',
		trash:     '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>',
		edit:      '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>',
		alert:     '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#ff4757" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>',
	},

	get: function(name, cls) {
		return (this._map[name] || '').replace('class="icon-svg"', 'class="' + (cls || 'icon-svg') + '"');
	}
};

/* ==================================================================
 * Toast 通知
 * ================================================================== */
function toast(msg, type) {
	type = type || 'info';
	var el = document.createElement('div');
	el.className = 'toast toast-' + type;
	el.textContent = msg;
	document.getElementById('toast-container').appendChild(el);
	setTimeout(function() { el.style.opacity = '0'; el.style.transition = 'opacity 0.3s'; }, 2500);
	setTimeout(function() { el.remove(); }, 2800);
}

/* ==================================================================
 * Modal 模态框
 * ================================================================== */
function showModal(title, bodyHtml, onOk, okText) {
	okText = okText || I18n.t('toast.confirm');

	var overlay = document.createElement('div');
	overlay.className = 'modal-overlay';
	overlay.innerHTML = '<div class="modal-box">'
		+ '<div class="modal-title">' + title + '</div>'
		+ '<div class="modal-body">' + bodyHtml + '</div>'
		+ '<div class="modal-actions">'
		+ '<button class="btn btn-cancel">' + I18n.t('toast.cancel') + '</button>'
		+ '<button class="btn btn-primary btn-ok">' + okText + '</button>'
		+ '</div></div>';

	document.body.appendChild(overlay);

	overlay.querySelector('.btn-cancel').onclick = function() { overlay.remove(); };
	overlay.querySelector('.btn-ok').onclick   = function() { onOk(overlay); };
	overlay.addEventListener('click', function(e) {
		if (e.target === overlay) { overlay.remove(); }
	});

	return overlay;
}

function getModalVal(overlay, selector) {
	var el = overlay.querySelector(selector);
	return el ? el.value : '';
}

/* ==================================================================
 * Table 渲染工具
 * ================================================================== */
function esc(s) {
	return String(s || '')
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;');
}

function fmtVal(v) {
	return (v == null || v === '') ? '\u2014' : esc(String(v));
}

function td(s, cls) {
	return '<td class="' + (cls || '') + '">' + s + '</td>';
}

function th(s) {
	return '<th>' + s + '</th>';
}

/**
 * @brief 渲染信号管理页面通用模板
 */
function renderSignalPage(type, headers, extraCols, actionsForRow) {
	var signals = SignalStore.get(type);
	var rows = '';

	if (signals.length === 0)
	{
		rows = '<tr class="empty-row"><td colspan="' + headers.length + '">' + I18n.t('signal.no_data') + '</td></tr>';
	}
	else
	{
		signals.forEach(function(s) {
			var xtra = extraCols ? extraCols(s) : '';
			var acts = actionsForRow ? actionsForRow(s) : '';
			rows += '<tr>'
				+ '<td class="cell-mono">' + esc(s.saddr) + '</td>'
				+ '<td class="cell-desc">' + esc(s.desc || '') + '</td>'
				+ xtra
				+ '<td class="cell-val">' + fmtVal(s.val) + '</td>'
				+ '<td>' + acts + '</td>'
				+ '</tr>';
		});
	}

	return '<div class="card">'
		+ '<div class="card-header"><span>' + type.toUpperCase() + ' \u2014 ' + signals.length + ' signals</span>'
		+ '<div class="btn-group">'
		+ '<button class="btn btn-primary btn-sm" onclick="SignalActions.add(\'' + type + '\')">' + Icon.get('plus') + ' ' + I18n.t('signal.add') + '</button>'
		+ '<button class="btn btn-danger btn-sm" onclick="SignalActions.batchDel(\'' + type + '\')">' + Icon.get('trash') + ' ' + I18n.t('signal.delete_sel') + '</button>'
		+ '</div></div>'
		+ '<div class="table-wrap"><table class="signal-table">'
		+ '<thead><tr>' + headers.map(function(h) { return th(h); }).join('') + '<th>' + I18n.t('signal.val') + '</th><th>' + I18n.t('signal.action') + '</th></tr></thead>'
		+ '<tbody id="tbody-' + type + '">' + rows + '</tbody>'
		+ '</table></div></div>';
}

/* ==================================================================
 * 信号 CRUD 操作
 * ================================================================== */
var SignalActions = {
	add: function(type) {
		var body = '';
		body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.saddr') + '</label><input class="form-input" id="mf-saddr" placeholder="e.g. plc.st.out.0"></div>';
		body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.desc') + '</label><input class="form-input" id="mf-desc" placeholder="Signal description"></div>';

		if (type === 'in')
		{
			body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.link') + '</label><input class="form-input" id="mf-link" placeholder="e.g. self_ptl.st.0"></div>';
		}
		else
		{
			body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.type') + '</label>'
				+ '<select class="form-select" id="mf-dtype">'
				+ '<option value="U8">U8</option><option value="U16">U16</option>'
				+ '<option value="U32">U32</option><option value="F32">F32</option>'
				+ '<option value="STR">STR</option></select></div>';
		}

		if (type === 'yk' || type === 'ao' || type === 'param')
		{
			body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.ctrl') + '</label>'
				+ '<select class="form-select" id="mf-ctrl">'
				+ '<option value="0">' + I18n.t('signal.ctrl_direct') + '</option>'
				+ '<option value="1">' + I18n.t('signal.ctrl_sbo') + '</option></select></div>';
		}

		showModal(I18n.t('signal.add_title') + ' (' + type.toUpperCase() + ')', body, function(ov) {
			var saddr = getModalVal(ov, '#mf-saddr');
			if (!saddr) { toast(I18n.t('toast.saddr_required'), 'error'); return; }
			var msg = { saddr: saddr, signal_type: type, curd: 'add', signal_data: '', setting_zone: '0' };
			if (type === 'in') { msg.link_saddr = getModalVal(ov, '#mf-link'); }
			WsClient.send(msg);
			toast(I18n.t('toast.add_sent') + ': ' + saddr, 'success');
			ov.remove();
		});
	},

	del: function(type, saddr) {
		showModal(I18n.t('signal.confirm_del'),
			'<p>' + I18n.t('signal.confirm_del_msg') + ' <strong>' + esc(saddr) + '</strong>?</p>',
			function(ov) {
				WsClient.send({ saddr: saddr, signal_type: type, curd: 'del', setting_zone: '0' });
				SignalStore.remove(type, saddr);
				Router.dispatch(Router.currentHash);
				toast(I18n.t('toast.del_sent') + ': ' + saddr, 'info');
				ov.remove();
			}, Icon.get('trash') + ' ' + I18n.t('signal.delete'));
	},

	batchDel: function(type) {
		var checked = [].slice.call(document.querySelectorAll('#tbody-' + type + ' input[type=checkbox]:checked'));
		if (checked.length === 0) { toast(I18n.t('toast.no_selected'), 'error'); return; }
		showModal(I18n.t('signal.confirm_batch_del'),
			'<p>' + I18n.t('signal.confirm_batch_msg') + ' ' + checked.length + ' ' + I18n.t('signal.confirm_batch_del') + '?</p>',
			function(ov) {
				checked.forEach(function(cb) {
					var saddr = cb.closest('tr').dataset.saddr;
					WsClient.send({ saddr: saddr, signal_type: type, curd: 'del', setting_zone: '0' });
				});
				toast(checked.length + ' ' + I18n.t('toast.batch_del_sent'), 'info');
				ov.remove();
			}, Icon.get('trash') + ' ' + I18n.t('signal.delete') + ' ' + checked.length);
	},

	set: function(type, saddr) {
		showModal(I18n.t('signal.set_title') + ' \u2014 ' + esc(saddr),
			'<div class="form-group"><label class="form-label">' + I18n.t('signal.new_val') + '</label><input class="form-input" id="mf-val" placeholder="Value"></div>',
			function(ov) {
				var val = getModalVal(ov, '#mf-val');
				WsClient.send({ saddr: saddr, signal_type: type, curd: 'set', signal_data: val, setting_zone: '0' });
				toast(I18n.t('toast.set_sent') + ': ' + saddr + ' = ' + val, 'success');
				ov.remove();
			}, I18n.t('signal.set'));
	}
};
