<template>
	<div class="server-dashboard">
		<div v-if="isSnapshotLoading && !snapshotView" class="loading-wrap">
			<t-loading text="加载系统状态..." />
		</div>
		<template v-else-if="snapshotView">
			<t-cell-group title="系统" theme="card">
				<t-cell title="操作系统" :note="Text.display(snapshotView?.snapshot?.os?.name)" />
				<t-cell title="启动时间" :note="Time.toPassedDateTime(snapshotView?.snapshot?.os?.bootAt)" />
				<t-cell
					v-if="snapshotView?.snapshot?.os?.bootAt"
					title="运行时长"
					:note="Time.duration(Time.now() - snapshotView?.snapshot?.os?.bootAt)"
				/>
				<t-cell title="更多" arrow @click="router.push({
						path: '/server/system-detail',
						query: {
							tab: 'server'
						}
					})"
				/>
			</t-cell-group>
			<t-cell-group class="section performance" title="资源" theme="card">
				<t-cell-info label="CPU" :value="Text.display(snapshotView?.snapshot?.cpu?.model)">
					<progress-group
						mode="heap"
						:progress="cpuProgressList"
						:note="Text.unit((snapshotView?.snapshot?.cpu?.usageTotal || 0) * 100, '%')"
					/>
				</t-cell-info>
				<t-cell-info class="memory" label="内存" :value="memoryUsageText">
					<progress-group
						mode="splice"
						:progress="memoryProgressList"
						:note="Text.unit(memoryUsed * 100, '%')"
					/>
				</t-cell-info>
				<t-cell-info class="memory-jvm" label="JVM 内存" :value="IOSize.format(jvmMemoryHeapMax)">
					<progress-group
						mode="heap"
						:progress="jvmMemoryProgressList"
						:note="Text.unit(jvmMemoryHeapCommited / jvmMemoryHeapMax * 100, '%')"
					/>
				</t-cell-info>
				<t-cell title="网络发送速率" :note="IOSize.speed(snapshotView?.snapshot?.network?.txBytesPerSecond)">
					<template #left-icon>
						<t-icon name="arrow-up" />
					</template>
				</t-cell>
				<t-cell title="网络接收速率" :note="IOSize.speed(snapshotView?.snapshot?.network?.rxBytesPerSecond)">
					<template #left-icon>
						<t-icon name="arrow-down" />
					</template>
				</t-cell>
				<t-cell title="更多" arrow @click="router.push({
						path: '/server/performance-detail',
						query: {
							tab: 'server'
						}
					})"
				/>
			</t-cell-group>
			<t-cell-group class="section disk" title="磁盘" theme="card">
				<t-cell-info
					class="item"
					v-for="item in snapshotView?.snapshot?.storagePartitions"
					:key="item.uuid"
					:label="item.mountPoint || item.partitionId"
					:value="`${IOSize.format(item.used)} / ${IOSize.format(item.total)}`"
					clip-text="label"
				>
					<progress-group
						mode="heap"
						:progress="[{
							color: 'var(--td-brand-color)',
							value: item.used / item.total
						}]"
						:note="Text.unit(item.used / item.total * 100, '%')"
					/>
				</t-cell-info>
				<t-cell title="更多" arrow @click="router.push({
						path: '/server/partitions-detail',
						query: {
							tab: 'server'
						}
					})"
				/>
			</t-cell-group>
		</template>
		<t-empty v-else description="暂无系统状态数据" />
		<t-cell-group class="section history" title="历史采样" theme="card">
			<t-tabs class="metric-tabs" :value="historyMetric" @change="handleHistoryMetricChange">
				<t-tab-panel
					v-for="item in historyMetricTabs"
					:key="item.value"
					:value="item.value"
					:label="item.label"
				/>
			</t-tabs>
			<div v-if="isHistoryLoading && !historyItems.length" class="loading-wrap">
				<t-loading text="加载历史数据..." />
			</div>
			<template v-else-if="historyItems.length">
				<v-chart
					ref="chartRef"
					class="chart"
					:option="historyChartOption"
					:update-options="historyChartUpdateOptions"
					:autoresize="true"
				/>
				<div class="slider bg-white">
					<span class="label">较早</span>
					<t-slider
						class="control"
						:min="0"
						:max="Math.max(historyItems.length - historyVisibleCount, 0)"
						:step="1"
						:value="historySliderValue"
						@change="handleHistorySliderChange"
					/>
					<span class="label">最新</span>
				</div>
				<t-cell-group>
					<t-cell v-for="item in currentHistorySummary" :key="item.label" :title="item.label" :note="item.value" />
				</t-cell-group>
			</template>
			<t-empty v-else description="暂无历史采样数据" />
		</t-cell-group>
	</div>
</template>

<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import { use } from "echarts/core";
import { SVGRenderer } from "echarts/renderers";
import { LineChart } from "echarts/charts";
import { GridComponent, LegendComponent, TooltipComponent } from "echarts/components";
import VChart from "vue-echarts";
import { useRouter } from "vue-router";
import type { EChartsOption, SeriesOption } from "echarts";
import SystemAPI from "@/api/SystemAPI";
import type { SystemStatusHistoryPoint, SystemStatusSnapshotView } from "@/types/System";
import { useSettingStore } from "@/store/settingStore";
import { IOSize, type LabelValue, Text, Time } from "timi-web";
import TCellInfo from "@/components/TCellInfo.vue";
import type { ProgressItem } from "@/components/ProgressGroup.vue";
import ProgressGroup from "@/components/ProgressGroup.vue";

type HistoryMetric = "cpu" | "memory" | "jvm" | "network";

const SERVER_SNAPSHOT_METRICS = ["os", "cpu", "memory", "jvm", "network", "hardware", "storage"] as const;
const SERVER_HISTORY_METRICS = ["cpu", "memory", "jvm", "network"] as const;

use([
	SVGRenderer,
	LineChart,
	GridComponent,
	TooltipComponent,
	LegendComponent
]);

defineOptions({
	name: "ServerDashboard"
});

const router = useRouter();
const settingStore = useSettingStore();

const isSnapshotLoading = ref(false);
const snapshotView = ref<SystemStatusSnapshotView | null>(null);

// ---------- CPU ----------

const cpuProgressList = computed<ProgressItem[]>(() => {
	const system = snapshotView.value?.snapshot?.cpu?.usageSystem || 0;
	const total = snapshotView.value?.snapshot?.cpu?.usageTotal || 0;
	return [
		{
			color: "var(--td-brand-color)",
			value: system,
			legend: `系统占用（${Text.unit(system * 100, '%')}）`
		},
		{
			color: "var(--td-brand-color-5)",
			value: total,
			legend: `总占用（${Text.unit(total * 100, '%')}）`
		}
	];
});

// ---------- 内存 ----------

const memoryUsed = computed(() => {
	const usedBytes = snapshotView.value?.snapshot?.memory?.usedBytes || 1;
	const totalBytes = snapshotView.value?.snapshot?.memory?.totalBytes || 1;
	return usedBytes / totalBytes;
});
const memoryUsageText = computed(() => {
	const usedBytes = snapshotView.value?.snapshot?.memory?.usedBytes;
	const totalBytes = snapshotView.value?.snapshot?.memory?.totalBytes;
	return `${IOSize.format(usedBytes)} / ${IOSize.format(totalBytes)}`;
});
const memoryTotal = computed(() => {
	const total = snapshotView.value?.snapshot?.memory?.totalBytes || 0;
	const totalSwap = snapshotView.value?.snapshot?.memory?.swapTotalBytes || 0;
	return total + totalSwap;
});
const memoryProgressList = computed<ProgressItem[]>(() => {
	const used = snapshotView.value?.snapshot?.memory?.usedBytes || 0;
	const usedSwap = snapshotView.value?.snapshot?.memory?.swapUsedBytes || 0;
	return [
		{
			color: "var(--td-brand-color)",
			value: used / memoryTotal.value,
			legend: `物理（${IOSize.format(used)}）`
		},
		{
			color: "var(--td-brand-color-5)",
			value: usedSwap / memoryTotal.value,
			legend: `交换区（${IOSize.format(usedSwap)}）`
		}
	]
});

// ---------- JVM 内存 ----------

const jvmMemoryHeapMax = computed(() => snapshotView.value?.snapshot?.jvm?.heapMaxBytes || 0);
const jvmMemoryHeapCommited = computed(() => snapshotView.value?.snapshot?.jvm?.heapCommittedBytes || 0);
const jvmMemoryHeapUsed = computed(() => snapshotView.value?.snapshot?.jvm?.heapUsedBytes || 0);
const jvmMemoryProgressList = computed<ProgressItem[]>(() => {
	return [
		{
			color: "var(--td-brand-color)",
			value: jvmMemoryHeapCommited.value / jvmMemoryHeapMax.value,
			legend: `已提交（${IOSize.format(jvmMemoryHeapCommited.value)}）`
		},
		{
			color: "var(--td-brand-color-5)",
			value: jvmMemoryHeapUsed.value / jvmMemoryHeapMax.value,
			legend: `已使用（${IOSize.format(jvmMemoryHeapUsed.value)}）`
		},
	]
})

// ---------- 历史采样图表 ----------

const isHistoryLoading = ref(false);
const historyPoints = ref<SystemStatusHistoryPoint[]>([]);
const chartRef = ref<InstanceType<typeof VChart> | null>(null);
const historyChartUpdateOptions = Object.freeze({
	notMerge: true
});
const isDarkTheme = ref(false);

// ---------- Tab ----------

const historyMetric = ref<HistoryMetric>("cpu");

const historyMetricTabs = computed(() => {
	const labelMap: Record<HistoryMetric, string> = {
		cpu: "CPU",
		memory: "内存",
		jvm: "JVM",
		network: "网络"
	};

	return SERVER_HISTORY_METRICS.map((metric) => ({
		value: metric,
		label: labelMap[metric]
	}));
});
watch(
	() => historyMetricTabs.value,
	(tabs) => {
		if (!tabs.length) {
			historyMetric.value = "cpu";
			return;
		}

		if (!tabs.some((item) => item.value === historyMetric.value)) {
			historyMetric.value = tabs[0].value;
		}
	},
	{ immediate: true }
);

function handleHistoryMetricChange(value: string): void {
	const nextValue = value as HistoryMetric;
	if (nextValue === "cpu" || nextValue === "memory" || nextValue === "jvm" || nextValue === "network") {
		historyMetric.value = nextValue;
	}
}

// ---------- 图表数据 ----------

const historyItems = computed<SystemStatusHistoryPoint[]>(() => {
	return historyPoints.value.slice().sort((left, right) => left.at - right.at);
});
const currentHistoryRange = computed(() => {
	const length = historyItems.value.length;
	if (length < 1) {
		return { startValue: 0, endValue: 0 };
	}

	const startValue = Math.min(Math.max(Math.floor(historySliderValue.value), 0), historySliderMaxStart.value);
	const endValue = Math.min(startValue + historyVisibleCount.value - 1, length - 1);
	return { startValue, endValue };
});
const visibleHistoryItems = computed<SystemStatusHistoryPoint[]>(() => {
	if (!historyItems.value.length) {
		return [];
	}
	return historyItems.value.slice(currentHistoryRange.value.startValue, currentHistoryRange.value.endValue + 1);
});

// ---------- 调整图表数据范围 ----------

const isHistorySliderTouched = ref(false);
const historySliderValue = ref(0);
const HISTORY_DEFAULT_VISIBLE_COUNT = 30;

const historyVisibleCount = computed(() => {
	if (historyItems.value.length < 1) {
		return 1;
	}
	return Math.min(HISTORY_DEFAULT_VISIBLE_COUNT, historyItems.value.length);
});
const historySliderMaxStart = computed(() => {
	return Math.max(historyItems.value.length - historyVisibleCount.value, 0);
});
watch(() => historyItems.value.length, (length) => {
	if (length < 1) {
		historySliderValue.value = 0;
		isHistorySliderTouched.value = false;
		return;
	}
	if (!isHistorySliderTouched.value) {
		historySliderValue.value = historySliderMaxStart.value;
		return;
	}
	historySliderValue.value = Math.min(historySliderValue.value, historySliderMaxStart.value);
});

function handleHistorySliderChange(value: number): void {
	historySliderValue.value = Math.min(Math.max(Math.floor(value), 0), historySliderMaxStart.value);
	isHistorySliderTouched.value = true;
}

// ---------- 当前图表摘要 ----------

const currentHistorySummary = computed<LabelValue<string, any>[]>(() => {
	if (!historyItems.value.length) {
		return [];
	}
	const lastPoint = historyItems.value[historyItems.value.length - 1];
	const summary: LabelValue<string, any>[] = [];
	if (historyMetric.value === "cpu") {
		summary.push(
			{ label: "总使用率", value: Text.unit(lastPoint.cpuUsagePercent * 100, "%") },
			{ label: "系统使用率", value: Text.unit(lastPoint.cpuSystemPercent * 100, "%") }
		);
		return summary;
	}
	if (historyMetric.value === "memory") {
		summary.push(
			{ label: "物理", value: IOSize.format(lastPoint.memoryUsedBytes) },
			{ label: "交换区", value: IOSize.format(lastPoint.swapUsedBytes) }
		);
		return summary;
	}
	if (historyMetric.value === "jvm") {
		summary.push(
			{ label: "已使用", value: IOSize.format(lastPoint.heapUsedBytes) },
			{ label: "已提交", value: IOSize.format(lastPoint.heapCommittedBytes) },
			{ label: "GC 周期耗时", value: Text.unit(lastPoint.gcCycleTimeMs, "毫秒") },
			{ label: "GC 暂停耗时", value: Text.unit(lastPoint.gcPauseTimeMs, "毫秒") }
		);
		return summary;
	}
	summary.push(
		{ label: "接收", value: IOSize.speed(lastPoint.rxBytesPerSecond) },
		{ label: "发送", value: IOSize.speed(lastPoint.txBytesPerSecond) }
	);
	return summary;
});

// ---------- 图表配置 ----------

const historyChartOption = computed((): EChartsOption => {
	const axisColor = isDarkTheme.value ? "#9aa4b2" : "#7e8a9a";
	const lineColor = isDarkTheme.value ? "rgba(255, 255, 255, .12)" : "rgba(18, 42, 66, .08)";
	const legendColor = isDarkTheme.value ? "#d7dee8" : "#334155";
	const memoryAxisMax = Math.max(snapshotView.value?.snapshot?.memory?.totalBytes || 0, 1);

	// 通用配置
	const commonOption: EChartsOption = {
		tooltip: {
			trigger: "axis",
			confine: true,
			position: (point, _params, _dom, _rect, size) => {
				const gap = 6;
				const fingerGap = 24;
				const contentSize = size?.contentSize || [0, 0];
				const viewSize = size?.viewSize || [0, 0];
				const maxLeft = Math.max(viewSize[0] - contentSize[0] - gap, gap);
				const maxTop = Math.max(viewSize[1] - contentSize[1] - gap, gap);
				const clamp = (value: number, min: number, max: number): number => {
					return Math.min(Math.max(value, min), max);
				};
				const preferLeft = point[0] - contentSize[0] - gap;
				const preferRight = point[0] + gap;
				const left = point[0] < viewSize[0] / 2
					? clamp(preferRight, gap, maxLeft)
					: clamp(preferLeft, gap, maxLeft);
				const preferTop = point[1] - contentSize[1] - fingerGap;
				const preferBottom = point[1] + fingerGap;
				const top = preferTop >= gap
					? clamp(preferTop, gap, maxTop)
					: clamp(preferBottom, gap, maxTop);
				return [left, top];
			}
		},
		legend: {
			top: 10,
			textStyle: {
				color: legendColor
			}
		},
		grid: {
			top: 40,
			left: 8,
			right: 8,
			bottom: 8,
			containLabel: true
		},
		xAxis: {
			type: "category",
			boundaryGap: false,
			data: visibleHistoryItems.value.map((item) => item.at),
			axisLabel: {
				color: axisColor,
				formatter: val => Time.toShortTime(Number(val))
			},
			axisLine: {
				lineStyle: {
					color: lineColor
				}
			}
		}
	};
	if (!historyItems.value.length) {
		return commonOption;
	}
	if (historyMetric.value === "cpu") {
		return {
			...commonOption,
			tooltip: {
				...commonOption.tooltip,
				formatter: params => {
					const list = Array.isArray(params) ? params : [params];
					const title = `时间：${Time.toDateTime(Number(list[0]?.name))}`
					const lines = list.map(item => `${item.marker}${item.seriesName}: ${Text.unit(Number(item.value) * 100, " %")}`);
					return [title, ...lines].join("<br />");
				}
			},
			yAxis: {
				type: "value",
				min: 0,
				max: 1,
				axisLabel: {
					color: axisColor,
					formatter: val => `${val * 100} %`
				},
				splitLine: {
					lineStyle: {
						color: lineColor
					}
				}
			},
			series: [
				resolveLineSeries("总使用率", visibleHistoryItems.value.map((item) => item.cpuUsagePercent!)),
				resolveLineSeries("系统使用率", visibleHistoryItems.value.map((item) => item.cpuSystemPercent!))
			]
		};
	}
	if (historyMetric.value === "memory") {
		return {
			...commonOption,
			tooltip: {
				...commonOption.tooltip,
				formatter: (params) => {
					const list = Array.isArray(params) ? params : [params];
					const title = `时间：${Time.toDateTime(Number(list[0]?.name))}`
					const lines = list.map(item => `${item.marker}${item.seriesName}: ${IOSize.format(Number(item.value))}`);
					return [title, ...lines].join("<br />");
				}
			},
			yAxis: {
				type: "value",
				max: memoryAxisMax,
				axisLabel: {
					color: axisColor,
					formatter: (value: number) => IOSize.format(value)
				},
				splitLine: {
					lineStyle: {
						color: lineColor
					}
				}
			},
			series: [
				resolveLineSeries("物理", visibleHistoryItems.value.map((item) => item.memoryUsedBytes!)),
				resolveLineSeries("交换区", visibleHistoryItems.value.map((item) => item.swapUsedBytes!))
			]
		};
	}
	if (historyMetric.value === "jvm") {
		return {
			...commonOption,
			tooltip: {
				...commonOption.tooltip,
				formatter: (params) => {
					const list = Array.isArray(params) ? params : [params];
					const title = `时间：${Time.toDateTime(Number(list[0]?.name))}`
					const lines = list.map(item => {
						const seriesName = item?.seriesName || "";
						if (seriesName === "GC 周期耗时" || seriesName === "GC 暂停耗时") {
							return `${item.marker}${item.seriesName}: ${Text.unit(Number(item.value), " 毫秒")}`
						} else {
							return `${item.marker}${item.seriesName}: ${IOSize.format(Number(item.value))}`
						}
					});
					return [title, ...lines].join("<br />");
				}
			},
			yAxis: [
				{
					type: "value",
					axisLabel: {
						color: axisColor,
						formatter: (val: number) => IOSize.format(val)
					},
					splitLine: {
						lineStyle: {
							color: lineColor
						}
					}
				},
				{
					type: "value",
					axisLabel: {
						color: axisColor,
						formatter: "{value} ms"
					},
					splitLine: {
						show: false
					}
				}
			],
			series: [
				resolveLineSeries("已使用", visibleHistoryItems.value.map((item) => item.heapUsedBytes!)),
				resolveLineSeries("已提交", visibleHistoryItems.value.map((item) => item.heapCommittedBytes!)),
				resolveLineSeries("GC 周期", visibleHistoryItems.value.map((item) => item.gcCycleTimeMs!), 1),
				resolveLineSeries("GC 暂停", visibleHistoryItems.value.map((item) => item.gcPauseTimeMs!), 1)
			]
		};
	}
	if (historyMetric.value === "network") {
		return {
			// 网络
			...commonOption,
			tooltip: {
				...commonOption.tooltip,
				formatter: (params) => {
					const list = Array.isArray(params) ? params : [params];
					const title = `时间：${Time.toDateTime(Number(list[0]?.name))}`
					const lines = list.map(item => `${item.marker}${item.seriesName}: ${IOSize.speed(Number(item.value))}`);
					return [title, ...lines].join("<br />");
				}
			},
			yAxis: {
				type: "value",
				axisLabel: {
					color: axisColor,
					formatter: (val: number) => IOSize.format(val)
				},
				splitLine: {
					lineStyle: {
						color: lineColor
					}
				}
			},
			series: [
				resolveLineSeries("接收", visibleHistoryItems.value.map((item) => item.rxBytesPerSecond!)),
				resolveLineSeries("发送", visibleHistoryItems.value.map((item) => item.txBytesPerSecond!))
			]
		};
	}
	return commonOption;
});

watch(() => settingStore.dashboard.server, restartAutoRefresh, { deep: true });

function resolveLineSeries(name: string, data: Array<number | null>, yAxisIndex?: number): SeriesOption {
	const series: SeriesOption = {
		type: "line",
		name,
		showSymbol: false,
		smooth: true,
		lineStyle: {
			width: 2
		},
		areaStyle: {
			opacity: .18
		},
		data
	};

	if (typeof yAxisIndex === "number") {
		series.yAxisIndex = yAxisIndex;
	}

	return series;
}

// ---------- 加载数据：当前状态 ----------

let snapshotTimer: number | undefined;
onUnmounted(() => {
	window.clearInterval(snapshotTimer);
	snapshotTimer = undefined;
});

async function refreshSnapshot(): Promise<void> {
	if (isSnapshotLoading.value) {
		return;
	}
	isSnapshotLoading.value = true;
	try {
		const metrics = SERVER_SNAPSHOT_METRICS.join(",");
		snapshotView.value = await SystemAPI.getStatus(metrics);
	} catch (error) {
		Toast({
			theme: "error",
			message: error instanceof Error ? error.message : "请求失败，请稍后重试"
		});
	} finally {
		isSnapshotLoading.value = false;
	}
}

// ---------- 加载数据：历史采集 ----------

let historyTimer: number | undefined;

async function refreshHistory(): Promise<void> {
	if (isHistoryLoading.value) {
		return;
	}
	isHistoryLoading.value = true;
	try {
		const metrics = SERVER_HISTORY_METRICS.join(",");
		const historyView = await SystemAPI.getStatusHistory({
			window: "1h",
			metrics
		});
		historyPoints.value = historyView.points || [];
	} catch (error) {
		Toast({
			theme: "error",
			message: error instanceof Error ? error.message : "请求失败，请稍后重试"
		});
	} finally {
		isHistoryLoading.value = false;
	}
}

onUnmounted(() => {
	window.clearInterval(historyTimer);
	historyTimer = undefined;
});

// ---------- 自动刷新 ----------

watch(() => settingStore.dashboard.server, restartAutoRefresh, { deep: true });

async function refreshDashboard(): Promise<void> {
	await Promise.all([
		refreshSnapshot(),
		refreshHistory()
	]);
}
function restartAutoRefresh(): void {
	void refreshDashboard();
	clearAutoRefreshTimer();

	const snapshotIntervalMs = Math.max(settingStore.dashboard.server.snapshotRefreshSeconds, 1) * 1000;
	const historyIntervalMs = Math.max(settingStore.dashboard.server.historyRefreshSeconds, 1) * 1000;

	snapshotTimer = window.setInterval(() => {
		void refreshSnapshot();
	}, snapshotIntervalMs);

	historyTimer = window.setInterval(() => {
		void refreshHistory();
	}, historyIntervalMs);
}

function clearAutoRefreshTimer(): void {
	if (snapshotTimer !== null) {
		window.clearInterval(snapshotTimer);
		snapshotTimer = undefined;
	}
	if (historyTimer !== null) {
		window.clearInterval(historyTimer);
		historyTimer = undefined;
	}
}
onMounted(restartAutoRefresh);
</script>

<style scoped lang="less">
.server-dashboard {
	gap: 1rem;
	display: flex;
	flex-direction: column;

	.section {

		&.disk {

			.collapses {

				:deep(.t-cell__title) {
					margin-right: 0;
				}

				.content {
					width: 100%;
					gap: .4rem;
					display: flex;
					flex-direction: column;

					.header {
						gap: .5rem;
						display: flex;
						align-items: center;
						justify-content: space-between;

						.title {
							color: var(--app-text);
							font-size: .875rem;
							font-weight: bold;
						}

						.health {
							font-size: .75rem;
							color: var(--app-sub);
						}
					}
				}
			}
		}

		&.history {
			.metric-tabs {
				width: 100%;
				--td-tab-track-width: 4rem;
			}

			.chart {
				width: 100%;
				height: 16rem;
				background: #FFF;
			}

			.slider {
				gap: .5rem;
				display: flex;
				padding: .5rem 2rem;
				align-items: center;

				.label {
					flex: none;
					font-size: .75rem;
					color: var(--app-sub);
				}

				.control {
					flex: 1;
				}
			}
		}
	}

	.loading-wrap {
		display: flex;
		min-height: 4rem;
		align-items: center;
		justify-content: center;
	}
}
</style>
