<template>
	<view class="physics-page">
		<view class="top-bar">
			<view class="top-bar-btn" @tap="goBack">
				<text class="nav-glyph">←</text>
			</view>
			<view class="top-bar-title">
				<text class="top-bar-title-text">光栅卡工作室</text>
				<text class="top-bar-subtitle">倾斜手机 · 重力感应预览</text>
			</view>
			<view class="top-bar-actions">
				<view class="top-bar-chip" @tap.stop="onRecalibrate">
					<text class="top-bar-chip-text">校零</text>
				</view>
				<view class="top-bar-btn" @tap="onMore">
					<text class="nav-glyph">⋮</text>
				</view>
			</view>
		</view>

		<view class="canvas-wrap">
			<view class="canvas-bg" />
			<LenticularCard
				class="preview-stack"
				:layers="layers"
				:transforms="layerTransforms"
				:gyro-source="gyroSourceLabel"
				:skip-built-in-touch="true"
				:tilt-hint-text="''"
				@simulate="simulate"
			/>
		</view>

		<view class="tool-dock">
			<view class="dock-rim" />
			<view class="layer-strip">
				<view class="layer-strip-header">
					<text class="layer-strip-title">图层素材</text>
					<text class="layer-strip-hint">点击替换 · 长按重置</text>
				</view>
				<view class="layer-slots">
					<view
						v-for="layer in layers"
						:key="layer.id"
						class="layer-slot"
						:class="{ 'layer-slot--filled': !!layer.src }"
						@tap="pickImage(layer.id)"
						@longpress="resetLayer(layer.id)"
					>
						<image v-if="layer.src" class="layer-slot-img" :src="layer.src" mode="aspectFill" />
						<view v-else class="layer-slot-placeholder" :style="placeholderStyle(layer)">
							<text class="layer-slot-icon">＋</text>
						</view>
						<view class="layer-slot-meta">
							<text class="layer-slot-name">{{ layer.label }}</text>
							<text class="layer-slot-tag">{{ layer.src ? '已上传' : '点击上传' }}</text>
						</view>
						<view v-if="layer.src" class="layer-slot-badge" @tap.stop="resetLayer(layer.id)">
							<text class="layer-slot-badge-icon">×</text>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
export default {
	inheritAttrs: false,
};
</script>

<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import LenticularCard from '@/components/lenticular/LenticularCard.vue'
import { useLenticularPreview } from '@/composables/useLenticularPreview.js'
import { useLenticularStudioTilt } from '@/composables/useLenticularStudioTilt.js'
import {
	buildLenticularLayersTwo,
	LENTICULAR_STUDIO_STORAGE_KEY,
} from '@/utils/castloveMintForm.js'

const layers = ref([])
const gyroSourceLabel = ref('simulation')

const { physics, layerTransforms, simulate } = useLenticularPreview(layers)

/** 相对进入时基准的倾角标量（度），每满 15° 一档；左右倾斜等价 */
const STUDIO_DISCRETE_STEP_DEG = 15
/** 档位施密特迟滞（度），减轻在档位边界来回跳 */
const DISCRETE_STEP_HYST_DEG = 4

const discreteStableStep = ref(0)

/**
 * 将相对基准的倾角标量（度，非负）映射为 LenticularEngine 的 gamma。
 * @param {number} tiltMagDeg 相对校零的倾角幅度（度）；由 composable 已做低通时可为非负标量
 */
function simulateFromSignedDegrees(tiltMagDeg) {
	const ls = layers.value || []
	const n = Math.max(1, ls.length)
	const absDeg = Math.abs(Number(tiltMagDeg) || 0)
	const STEP = STUDIO_DISCRETE_STEP_DEG
	const MARGIN = DISCRETE_STEP_HYST_DEG
	let s = discreteStableStep.value
	while (absDeg >= (s + 1) * STEP + MARGIN) s++
	while (s > 0 && absDeg <= s * STEP - MARGIN) s--
	discreteStableStep.value = s
	const idx = s % n
	const sens = physics.tiltSensitivity / 100
	const mul = 0.44 + sens * 0.52
	const u = (idx + 0.5) / n
	const gPick = Math.max(-1, Math.min(1, 2 * u - 1))
	const gamma = Math.max(-1, Math.min(1, gPick / mul))
	simulate(gamma, 0)
}

const { start: startTilt, stop: stopTilt, recalibrate: recalibrateTilt } = useLenticularStudioTilt({
	simulate,
	simulateFromSignedDegrees,
	gyroSourceLabel,
	useStudioAccelDirect: true,
	onTiltDriverFallback: () => {
		discreteStableStep.value = 0
	},
})

function goBack() {
	uni.navigateBack({
		delta: 1,
		fail: () => {},
	})
}

function onMore() {
	uni.showActionSheet({
		itemList: ['恢复为进入工作室时的图片'],
		success: (res) => {
			if (res.tapIndex === 0) {
				const raw = uni.getStorageSync(LENTICULAR_STUDIO_STORAGE_KEY)
				if (!raw) return
				try {
					const p = typeof raw === 'string' ? JSON.parse(raw) : raw
					layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
					uni.showToast({ title: '已恢复', icon: 'none' })
				} catch (e) {
					console.error(e)
				}
			}
		},
	})
}

function placeholderStyle(layer) {
	if (layer.background) {
		return { background: layer.background }
	}
	return { background: 'rgba(6,14,32,0.55)' }
}

function pickImage(layerId) {
	uni.chooseImage({
		count: 1,
		sizeType: ['compressed'],
		sourceType: ['album', 'camera'],
		success: (res) => {
			const path = res.tempFilePaths && res.tempFilePaths[0]
			if (!path) return
			const idx = layers.value.findIndex((l) => l.id === layerId)
			if (idx < 0) return
			const cur = layers.value[idx]
			const next = { ...cur, src: path, background: undefined }
			if (layerId === 'base') next.dots = undefined
			const copy = [...layers.value]
			copy[idx] = next
			layers.value = copy
		},
		fail: (err) => {
			const msg = (err && err.errMsg) || ''
			if (!msg.includes('cancel')) {
				uni.showToast({ title: '选择图片失败', icon: 'none' })
			}
		},
	})
}

function resetLayer(layerId) {
	const fresh = buildLenticularLayersTwo('', '')
	const defLayer = fresh.find((l) => l.id === layerId)
	const idx = layers.value.findIndex((l) => l.id === layerId)
	if (idx < 0 || !defLayer) return
	const copy = [...layers.value]
	copy[idx] = { ...defLayer }
	layers.value = copy
}

function onRecalibrate() {
	discreteStableStep.value = 0
	recalibrateTilt()
	uni.showToast({ title: '已重置水平基准', icon: 'none' })
}

onMounted(() => {
	try {
		const raw = uni.getStorageSync(LENTICULAR_STUDIO_STORAGE_KEY)
		if (!raw) {
			uni.showToast({ title: '缺少素材数据，请返回上一步', icon: 'none' })
			setTimeout(() => goBack(), 1600)
			return
		}
		const p = typeof raw === 'string' ? JSON.parse(raw) : raw
		layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
	} catch (e) {
		console.error('[lenticular-studio] load payload', e)
		uni.showToast({ title: '数据无效', icon: 'none' })
		setTimeout(() => goBack(), 1600)
		return
	}
	nextTick(() => {
		/* 离散换图：略降过渡混合，档位切换更利落 */
		physics.angleStability = 6
		physics.transitionSmoothness = 12
		physics.tiltSensitivity = 96
		physics.sensorDeadzoneStrength = 0
		startTilt()
	})
})

onUnmounted(() => {
	stopTilt()
})
</script>

<style>
.nav-glyph {
	font-size: 22px;
	line-height: 1;
	font-weight: 600;
	color: inherit;
}
</style>

<style scoped>
.physics-page {
	width: 100vw;
	height: 100vh;
	display: flex;
	flex-direction: column;
	background: #0b1326;
	color: #dae2fd;
	overflow: hidden;
	position: relative;
}

.top-bar {
	position: relative;
	z-index: 50;
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 0 16px;
	height: 56px;
	padding-top: calc(env(safe-area-inset-top) + 4px);
	background: rgba(11, 19, 38, 0.88);
	border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.top-bar-actions {
	display: flex;
	align-items: center;
	gap: 4px;
}

.top-bar-chip {
	padding: 6px 12px;
	border-radius: 999px;
	background: rgba(45, 52, 73, 0.75);
	border: 1px solid rgba(255, 255, 255, 0.12);
}

.top-bar-chip-text {
	font-size: 12px;
	font-weight: 600;
	color: #ddb7ff;
}

.top-bar-btn {
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #ddb7ff;
}

.top-bar-title {
	display: flex;
	flex-direction: column;
	align-items: center;
}

.top-bar-title-text {
	font-size: 18px;
	font-weight: 600;
	color: #dae2fd;
}

.top-bar-subtitle {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.16em;
	color: #4cd7f6;
	margin-top: 2px;
}

.canvas-wrap {
	flex: 1;
	min-height: 0;
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 8px 10px 4px;
	z-index: 10;
}

.preview-stack {
	position: relative;
	z-index: 1;
	width: 100%;
	height: 100%;
	min-height: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

.canvas-bg {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(ellipse at 50% 35%, rgba(132, 43, 210, 0.18), transparent 55%),
		radial-gradient(ellipse at 50% 85%, rgba(76, 215, 246, 0.1), transparent 60%);
	pointer-events: none;
}

.tool-dock {
	position: relative;
	z-index: 20;
	margin: 0 10px calc(10px + env(safe-area-inset-bottom));
	max-width: 460px;
	align-self: center;
	width: calc(100% - 20px);
	background: rgba(34, 42, 61, 0.88);
	border: 1px solid rgba(77, 67, 84, 0.45);
	border-radius: 20px;
	padding: 12px 14px;
	display: flex;
	flex-direction: column;
	gap: 10px;
	box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
	overflow: hidden;
}

.dock-rim {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 1px;
	background: linear-gradient(to right, transparent, rgba(221, 183, 255, 0.45), transparent);
}

.layer-strip {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.layer-strip-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 0 2px;
}

.layer-strip-title {
	font-size: 13px;
	font-weight: 600;
	color: #dae2fd;
}

.layer-strip-hint {
	font-size: 11px;
	color: rgba(207, 194, 214, 0.6);
}

.layer-slots {
	display: flex;
	gap: 8px;
}

.layer-slot {
	flex: 1;
	position: relative;
	display: flex;
	flex-direction: column;
	border-radius: 12px;
	overflow: hidden;
	background: rgba(6, 14, 32, 0.55);
	border: 1px solid rgba(255, 255, 255, 0.08);
}

.layer-slot--filled {
	border-color: rgba(76, 215, 246, 0.5);
	box-shadow: 0 0 14px rgba(76, 215, 246, 0.15);
}

.layer-slot-img {
	width: 100%;
	height: 56px;
	display: block;
}

.layer-slot-placeholder {
	width: 100%;
	height: 56px;
	display: flex;
	align-items: center;
	justify-content: center;
	background-size: cover;
	background-position: center;
}

.layer-slot-icon {
	font-size: 22px;
	color: rgba(221, 183, 255, 0.85);
}

.layer-slot-meta {
	display: flex;
	flex-direction: column;
	padding: 6px 8px 8px;
	gap: 2px;
	background: rgba(6, 14, 32, 0.55);
}

.layer-slot-name {
	font-size: 12px;
	font-weight: 600;
	color: #dae2fd;
}

.layer-slot-tag {
	font-size: 10px;
	color: rgba(207, 194, 214, 0.7);
}

.layer-slot--filled .layer-slot-tag {
	color: #4cd7f6;
}

.layer-slot-badge {
	position: absolute;
	top: 4px;
	right: 4px;
	width: 22px;
	height: 22px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.55);
	border: 1px solid rgba(255, 255, 255, 0.18);
}

.layer-slot-badge-icon {
	font-size: 14px;
	color: #dae2fd;
}
</style>
