<template>
	<transition name="fade">
		<div
			class="select-menu-panel"
			v-if="visible"
			:class="{ active: !closePanel }"
		>
			<div class="btn" @click="setClosePanel">
				<i class="fi fi-br-angle-left"></i>
			</div>
			<!-- 变换工具顶部 -->
			<div class="panel-select">
				<!-- <div class="panel-header">
					<div class="header-title">变换工具</div>
				</div> -->
				<!-- 分割线 -->
				<!-- <div class="panel-divider"></div> -->
				<!-- 变换工具内容 -->
				<div class="tool-content">
					<div
						class="object-item"
						v-for="v in activeObjects"
						:key="v.id"
					>
						<div class="title">{{ v.layer?.name }}</div>
						<div class="list">
							<div
								class="input"
								v-if="v.layerId !== SpecialLayerId.COLOR"
							>
								<angle-tool
									:angle="Number(Number(v.angle).toFixed(3))"
									@input="(e) => inputAngle(e, v)"
									@change="(e) => changeAngle(e, v)"
								/>
							</div>
							<div class="input">
								<span class="label"
									>{{ t("Canvas.opacity") }}:</span
								>
								<slider
									:tipFormatter="
										(v) => `${Math.round(v * 100)}%`
									"
									:value="v.opacity"
									:min="0"
									:max="1"
									:step="0.01"
									@change="(e) => changeOpacity(e, v)"
									@input="(e) => inputOpacity(e, v)"
								/>
							</div>
							<div
								class="btn"
								@click="clickflipHorizontal(v)"
								v-if="v.layerId !== SpecialLayerId.COLOR"
							>
								<i class="iconfont icon-flip-horizontal"></i>
								<p class="tip">
									{{ t("Canvas.flipHorizontal") }}
								</p>
							</div>
							<div
								class="btn"
								@click="clickflipVertical(v)"
								v-if="v.layerId !== SpecialLayerId.COLOR"
							>
								<i class="iconfont icon-flip-vertical"></i>
								<p class="tip">
									{{ t("Canvas.flipVertical") }}
								</p>
							</div>
							<!-- <div
								class="btn"
								@click="clickCropImage(v)"
								v-if="v.layerId !== SpecialLayerId.COLOR"
							>
								<i class="iconfont icon-caijian"></i>
								<p class="tip">
									{{ t("Canvas.cropAndAdd") }}
								</p>
							</div> -->
							<!-- <div
								class="btn"
								@click="clickRasterizeLayer(v)"
								v-if="v.type !== 'image'"
							>
								<span class="label">{{ t("Canvas.RasterizedLayer") }}</span>
							</div> -->
							<div class="select">
								<!-- 混合模式 -->
								<i class="iconfont icon-hunhemoshi"></i>
								<my-select
									:defaultValue="
										v.layer?.blendMode ||
										v.globalCompositeOperation
									"
									:list="layerCompositeOptions"
									@change="
										(n, o) => setLayerComposite(n, o, v, 1)
									"
									@active="
										(n, o) => setLayerComposite(n, o, v, 0)
									"
								/>
							</div>
							<!-- <div
								class="btn"
								@click="clickTest(v)"
							>
								<span class="label">测试</span>
							</div> -->
							<div
								class="select"
								v-if="v.type === 'rect' || v.type === 'image'"
							>
								<!-- 平铺 -->
								<i class="iconfont icon-repeat"></i>
								<a-select
									size="small"
									:defaultValue="
										typeof v.fill === 'object'
											? v.fill?.repeat || 'no-repeat'
											: 'no-repeat'
									"
									:options="selectOptions"
									@change="(e) => changeFillRepeat(e, v)"
								/>
							</div>
							<!-- 平铺设置 -->
							<a-popover
								v-if="v.type === 'rect'"
								trigger="click"
								destroyTooltipOnHide
								:title="t('Canvas.repeatSetting')"
							>
								<template #content>
									<repeat-setting
										:object="v"
										@inputFillAngle="
											(e) => inputFillAngle(e, v)
										"
										@changeFillAngle="
											(e) => changeFillAngle(e, v)
										"
										@inputFillOffset="
											(e) => inputFillOffset(e, v)
										"
										@changeFillOffset="
											(e) => changeFillOffset(e, v)
										"
										@inputFillScale="
											(e) => inputFillScale(e, v)
										"
										@changeFillScale="
											(e) => changeFillScale(e, v)
										"
										@inputFill_Gap="
											(x, y) => inputFill_Gap(x, y, v)
										"
										@changeFill_Gap="
											(x, y) => changeFill_Gap(x, y, v)
										"
									/>
								</template>
								<div class="btn">
									<i class="iconfont icon-gengduo"></i>
								</div>
							</a-popover>
						</div>
					</div>
				</div>
			</div>
		</div>
	</transition>
</template>

<script setup>
	import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
	import { useI18n } from "vue-i18n";
	const { t } = useI18n();
	import {
		OperationType,
		SpecialLayerId,
		SpecialType,
	} from "../../utils/layerHelper";
	import { loadImageUrlToLayer } from "../../utils/imageHelper";
	import {
		calculateRotatedTopLeftDeg,
		createPatternTransform,
		getTransformScaleAngle,
	} from "../../utils/helper";
	import { TransformCommand } from "../../commands/StateCommands";
	import {
		FillRepeatCommand,
		FillRepeatChangeCommand,
		FillRepeatGapChangeCommand,
	} from "../../commands/FillRepeatCommand";
	import { SetLayerCompositeCommand } from "../../commands/LayerCommands.js";
	import RepeatSetting from "./RepeatSetting.vue";
	import Slider from "../tools/Slider.vue";
	import AngleTool from "../tools/AngleTool.vue";
	import MySelect from "../tools/MySelect.vue";
	import EventManager from "../../utils/event.js";
	import { getSelectOptions, getLayerCompositeOptions } from "./data.js";
	const selectOptions = getSelectOptions();
	const layerCompositeOptions = getLayerCompositeOptions();
	const props = defineProps({
		canvas: {
			type: Object,
			required: true,
		},
		commandManager: {
			type: Object,
			required: true,
		},
		selectManager: {
			type: Object,
			required: true,
		},
		layerManager: {
			type: Object,
			required: true,
		},
		canvasManager: {
			type: Object,
			required: true,
		},
		toolManager: {
			type: Object,
			required: true,
		},
		activeTool: {
			type: String,
			required: false,
			default: null,
		},
	});
	// 响应式数据
	const visible = ref(false);
	//打开隐藏操作面板
	const closePanel = ref(false);
	const setClosePanel = () => {
		closePanel.value = !closePanel.value;
	};

	onMounted(() => {
		setupCanvasListeners();
	});
	onUnmounted(() => {
		removeCanvasListeners();
	});

	// 监听 activeTool 变化
	watch(
		() => props.activeTool,
		(newTool) => {
			if (newTool === OperationType.SELECT) {
				show();
			} else {
				close();
			}
		},
		{ immediate: true }
	);

	/**
	 * 显示面板
	 */
	function show() {
		if (activeObjects.value.length === 0) return;
		visible.value = true;
		closePanel.value = true;
	}

	/**
	 * 关闭面板
	 */
	function close() {
		visible.value = false;
	}
	// 获取当前选中的对象
	const activeObjects = ref([]);
	const getActiveObject = (e) => {
		console.log("==========切换激活对象", e, activeObjects);
		activeObjects.value = [...e.selected];
		// .filter((v) =>
		// 	v.specialType ? v.specialType === SpecialType.REPEAT_O : true
		// );// 过滤出印花对象
		activeObjects.value.forEach((v) => {
			v.layer = props.layerManager.getLayerById(v.layerId);
		});
		if (activeObjects.value.length === 0) {
			close();
		} else {
			show();
		}
	};
	//取消当前选中
	const cancelSelect = () => {
		activeObjects.value = [];
		close();
	};
	const lastSelectLayerId = inject("lastSelectLayerId");
	const layers = inject("layers");
	const transformObject = (
		activeObj,
		initialState,
		finalState,
		isCommand = true
	) => {
		const cmd = new TransformCommand({
			canvas: props.canvas,
			objectId: activeObj.id,
			initialState,
			finalState,
			objectType: activeObj.type,
			name: `变换 ${activeObj.type || "对象"}`,
			layerManager: props.layerManager,
			layers: layers,
			lastSelectLayerId: lastSelectLayerId,
		});
		if (isCommand) {
			props.commandManager.execute(cmd);
		} else {
			cmd.execute();
		}
	};
	// 改变不透明度
	const changeOpacity = (opacity, obj) => {
		props.layerManager?.setLayerOpacity(obj.layerId, opacity);
	};
	const inputOpacity = (opacity, obj) => {
		obj.opacity = opacity;
		props.canvas.renderAll();
	};
	// 改变角度
	const inputAngle = (angle, obj) => {
		const initialState = TransformCommand.captureTransformState(obj);
		const finalState = computeAngleState(angle, obj, initialState);
		transformObject(obj, initialState, finalState, false);
		if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState;
	};
	const changeAngle = (angle, obj) => {
		var initialState;
		if (obj.hasOwnProperty("oldState")) {
			initialState = obj.oldState;
			delete obj.oldState;
		} else {
			initialState = TransformCommand.captureTransformState(obj);
		}
		const finalState = computeAngleState(angle, obj, initialState);
		transformObject(obj, initialState, finalState);
	};
	const computeAngleState = (angle, obj, initialState) => {
		const finalState = { ...initialState };
		if (obj.originX === "left" && obj.originY === "top") {
			const width = obj.width * obj.scaleX;
			const height = obj.height * obj.scaleY;
			const left = obj.left;
			const top = obj.top;
			const { x, y } = calculateRotatedTopLeftDeg(
				width,
				height,
				left,
				top,
				obj.angle,
				angle
			);
			finalState.left = x;
			finalState.top = y;
		}
		finalState.angle = angle;
		return finalState;
	};

	// 水平翻转
	const clickflipHorizontal = (obj) => {
		const initialState = TransformCommand.captureTransformState(obj);
		const finalState = { ...initialState };
		finalState.flipX = !finalState.flipX;
		transformObject(obj, initialState, finalState);
	};
	// 垂直翻转
	const clickflipVertical = (obj) => {
		const initialState = TransformCommand.captureTransformState(obj);
		const finalState = { ...initialState };
		finalState.flipY = !finalState.flipY;
		transformObject(obj, initialState, finalState);
	};
	// 裁剪图片
	const cropImage = inject("cropImage");
	const clickCropImage = async (obj) => {
		const base64 = await props.layerManager.getLayerToBase64(obj.layerId);
		if (base64)
			cropImage(base64).then((res) => {
				loadImageUrlToLayer({
					imageUrl: res,
					layerManager: props.layerManager,
					canvas: props.canvas,
					toolManager: props.toolManager,
				});
			});
	};

	// 栅格化图层
	const clickRasterizeLayer = (obj) => {
		props.layerManager.rasterizeLayer(obj.layerId);
	};

	// 改变填充重复
	const changeFillRepeat = async (value, obj) => {
		console.log("==========改变填充重复", obj.type);
		const cmd = new FillRepeatCommand({
			canvas: props.canvas,
			layers: layers,
			canvasManager: props.canvasManager,
			layerManager: props.layerManager,
			layerId: obj.layerId,
			fillRepeat: value,
		});
		props.commandManager.execute(cmd);
	};
	// 改变填充角度
	const inputFillAngle = (angle, obj) => {
		if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
		const fill = obj.get("fill");
		const scale = getTransformScaleAngle(fill?.patternTransform).scale;
		const pattern = new fabric.Pattern({
			...fill,
			patternTransform: createPatternTransform(scale, angle),
		});
		obj.set("fill", pattern);
		props.canvas.renderAll();
	};
	const changeFillAngle = (angle, obj) => {
		const fill = obj.get("fill");
		const scale = getTransformScaleAngle(fill?.patternTransform).scale;
		const pattern = {
			patternTransform: createPatternTransform(scale, angle),
		};
		changeFill(obj, pattern);
	};
	// 改变填充便宜
	const inputFillOffset = (value, obj) => {
		if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
		const pattern = new fabric.Pattern({
			...obj.get("fill"),
			offsetX: (value.left / 100) * obj.width,
			offsetY: (value.top / 100) * obj.height,
		});
		obj.set("fill", pattern);
		props.canvas.renderAll();
	};
	const changeFillOffset = (value, obj) => {
		const pattern = new fabric.Pattern({
			offsetX: (value.left / 100) * obj.width,
			offsetY: (value.top / 100) * obj.height,
		});
		changeFill(obj, pattern);
	};
	// 改变填充缩放
	const inputFillScale = (scale, obj) => {
		if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
		const fill = obj.get("fill");
		const angle = getTransformScaleAngle(fill?.patternTransform).angle;
		const pattern = new fabric.Pattern({
			...fill,
			patternTransform: createPatternTransform(scale, angle),
		});
		obj.set("fill", pattern);
		props.canvas.renderAll();
	};
	const changeFillScale = (scale, obj) => {
		const fill = obj.get("fill");
		const angle = getTransformScaleAngle(fill?.patternTransform).angle;
		const pattern = {
			patternTransform: createPatternTransform(scale, angle),
		};
		changeFill(obj, pattern);
	};
	const changeFill = (obj, pattern) => {
		const cmd = new FillRepeatChangeCommand({
			canvas: props.canvas,
			layers: layers,
			canvasManager: props.canvasManager,
			layerManager: props.layerManager,
			layerId: obj.layerId,
			newPattern: pattern,
		});
		props.commandManager.execute(cmd);
	};
	// 改变填充间隙
	const inputFill_Gap = (gapX, gapY, obj) => {
		const cmd = new FillRepeatGapChangeCommand({
			canvas: props.canvas,
			layers: layers,
			canvasManager: props.canvasManager,
			layerManager: props.layerManager,
			layerId: obj.layerId,
			newGapX: gapX,
			newGapY: gapY,
			record: true,
		});
		cmd.execute();
	};
	const changeFill_Gap = (gapX, gapY, obj) => {
		if (obj.oldFill_) {
			obj.fill_ = { ...obj.oldFill_ };
			delete obj.oldFill_;
		}
		const cmd = new FillRepeatGapChangeCommand({
			canvas: props.canvas,
			layers: layers,
			canvasManager: props.canvasManager,
			layerManager: props.layerManager,
			layerId: obj.layerId,
			newGapX: gapX,
			newGapY: gapY,
		});
		props.commandManager.execute(cmd);
	};

	const setLayerComposite = (newValue, oldValue, obj, isCmd) => {
		const cmd = new SetLayerCompositeCommand({
			canvas: props.canvas,
			layers: layers,
			layerManager: props.layerManager,
			layerId: obj.layerId,
			newValue: newValue,
			oldValue: oldValue,
		});
		if (isCmd) {
			props.commandManager.execute(cmd);
		} else {
			cmd.execute();
		}
	};

	const clickTest = (obj) => {
		console.log("==========点击测试", obj);
	};

	// 更新选中对象属性
	const updateActiveObjects = (arrs, keys, isNumber = true) => {
		arrs.forEach((v) => {
			activeObjects.value.forEach((item) => {
				if (item.id === v.id) {
					keys.forEach(
						(key) => (item[key] = isNumber ? Number(v[key]) : v[key])
					);
				}
			});
		});
		activeObjects.value = [...activeObjects.value];
	};

	// 旋转对象时更新角度
	const objectRotatingChange = (e) => {
		const arrs = [];
		if (e.target._objects) {
			e.target._objects.forEach((v) => arrs.push(v));
		} else {
			arrs.push(e.target);
		}
		updateActiveObjects(arrs, ["angle"]);
	};

	// 对象属性修改后触发
	const objectModifiedChange = (e) => {
		console.log("==========object:modified", e.target);
	};
	// 不透明度撤销时触发
	const objectOpacityUndo = (layerId, opacity) => {
		const layerObjects = props.canvas
			.getObjects()
			.filter((obj) => obj.layerId === layerId);
		updateActiveObjects(layerObjects, ["opacity"]);
	};
	// 对象属性修改撤销时触发
	const objectModifiedUndo = (object) => {
		updateActiveObjects([object], ["angle"]);
	};
	// 组合操作撤销时触发
	const objectCompositeChange = (object) => {
		updateActiveObjects([object], ["globalCompositeOperation"], false);
	};
	/**
	 * 设置画布事件监听
	 */
	function setupCanvasListeners() {
		if (!props.canvas) return;
		// 注册事件
		props.canvas.on("selection:created", getActiveObject);
		props.canvas.on("selection:updated", getActiveObject);
		props.canvas.on("selection:cleared", cancelSelect);
		props.canvas.on("object:rotating", objectRotatingChange);
		props.canvas.on("object:modified", objectModifiedChange);
		EventManager.on("object:opacity:execute", objectOpacityUndo);
		EventManager.on("object:opacity:undo", objectOpacityUndo);
		EventManager.on("object:modified:execute", objectModifiedUndo);
		EventManager.on("object:modified:undo", objectModifiedUndo);
		EventManager.on("object:composite:execute", objectCompositeChange);
		EventManager.on("object:composite:undo", objectCompositeChange);
	}

	/**
	 * 移除画布事件监听
	 */
	function removeCanvasListeners() {
		if (!props.canvas) return;
		// 移除事件
		props.canvas.off("selection:created", getActiveObject);
		props.canvas.off("selection:updated", getActiveObject);
		props.canvas.off("selection:cleared", cancelSelect);
		props.canvas.off("object:rotating", objectRotatingChange);
		props.canvas.off("object:modified", objectModifiedChange);
		EventManager.off("object:opacity:execute", objectOpacityUndo);
		EventManager.off("object:opacity:undo", objectOpacityUndo);
		EventManager.off("object:modified:execute", objectModifiedUndo);
		EventManager.off("object:modified:undo", objectModifiedUndo);
		EventManager.off("object:composite:execute", objectCompositeChange);
		EventManager.off("object:composite:undo", objectCompositeChange);
	}
</script>

<style scoped lang="less">
	.select-menu-panel {
		position: absolute;
		bottom: 22px;
		left: 0;
		right: 0;
		// max-width: min(90vw, 640px);
		max-width: 95%;
		width: 80rem;
		margin: 0 auto;
		background-color: rgba(255, 255, 255, 0.95);
		backdrop-filter: blur(15px);
		-webkit-backdrop-filter: blur(15px);
		border-radius: 8px;
		box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
		z-index: 1000;
		color: #333;
		border: 1px solid rgba(0, 0, 0, 0.05);
		user-select: none;
		&.active {
			transform: translateY(100%);
			> .btn {
				> i {
					transform: rotate(90deg);
				}
			}
		}
		> .btn {
			width: 100%;
			height: 22px;
			text-align: center;
			cursor: pointer;
			display: flex;
			align-items: center;
			justify-content: center;

			> i {
				font-size: 1.4rem;
				transform: rotate(270deg);
			}
		}
	}

	/* 平板和手机适配 */
	@media screen and (max-width: 768px) {
		.select-menu-panel {
			bottom: 15px;
			left: 15px;
			right: 15px;
			max-width: calc(100vw - 30px);
			border-radius: 6px;
		}
	}

	@media screen and (max-width: 480px) {
		.select-menu-panel {
			bottom: 10px;
			left: 10px;
			right: 10px;
			max-width: calc(100vw - 20px);
		}
	}

	.select-menu-panel.is-active {
		transform: translateY(0);
	}

	.panel-header {
		padding: 8px 15px;
		border-bottom: 1px solid rgba(0, 0, 0, 0.05);
		background-color: rgba(255, 255, 255, 0.8);
		border-radius: 8px 8px 0 0;
	}

	.header-title {
		font-size: 13px;
		font-weight: 500;
		color: #333;
		text-align: left;
	}

	.panel-select {
		// padding: 0 0 10px;
	}

	/* 平板适配 */
	@media screen and (max-width: 768px) {
		.panel-header {
			padding: 6px 12px;
			border-radius: 6px 6px 0 0;
		}
	}

	/* 手机适配 */
	@media screen and (max-width: 480px) {
		.panel-header {
			padding: 5px 10px;
		}

		.header-title {
			font-size: 12px;
		}
	}

	.tool-btn {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		background-color: rgba(0, 0, 0, 0.05);
		border: none;
		border-radius: 6px;
		padding: 6px;
		color: #333;
		cursor: pointer;
		transition: all 0.2s;
	}

	.tool-btn span {
		margin-top: 0;
		font-size: 12px;
	}

	.tool-btn svg {
		width: 24px;
		height: 24px;
	}

	.tool-btn:hover {
		background-color: rgba(0, 0, 0, 0.08);
	}

	.tool-btn.active {
		background-color: #007aff;
		color: white;
	}

	.panel-divider {
		height: 1px;
		background-color: rgba(0, 0, 0, 0.05);
		margin: 0 10px 5px 10px;
	}

	.tool-content {
		overflow-y: auto;
		max-height: 20rem;
		margin-top: 1rem;
		padding: 0 1.5rem;
		> .object-item {
			border-bottom: 1px solid rgba(0, 0, 0, 0.05);
			padding: 1rem 0;
			&:last-child {
				border-bottom: none;
			}
			> .title {
				text-align: left;
				margin-bottom: 0.5rem;
			}
			> .list {
				display: flex;
				> div {
					display: flex;
					align-items: center;
					justify-content: center;
					margin-right: 1.5rem;
					position: relative;
					&:last-child {
						margin-right: 0;
					}
					> .iconfont {
						font-size: 1.8rem;
					}
					> .label {
						font-size: 1.3rem;
						margin: 0 0.5rem;
					}
					> .angle-tool {
						width: 9rem;
					}
					> .tip {
						position: absolute;
						top: -5px;
						left: 50%;
						transform: translate(-50%, -100%);
						background-color: rgba(0, 0, 0, 0.7);
						color: white;
						padding: 0.4rem 0.8rem;
						border-radius: 0.4rem;
						// margin-left: 0.8rem;
						font-size: 1.2rem;
						white-space: nowrap;
						pointer-events: none;
						display: none;
						&::after {
							content: "";
							position: absolute;
							top: 97%;
							left: 50%;
							transform: translateX(-50%);
							width: 0;
							height: 0;
							border-left: 5px solid transparent;
							border-right: 5px solid transparent;
							border-top: 5px solid rgba(0, 0, 0, 0.8);
						}
					}
					&:hover {
						> .tip {
							display: block;
						}
					}
				}
				> div.input {
					font-size: 1.4rem;
					color: #474747;
					> .label {
						margin-right: 0.5rem;
						font-size: 1.4rem;
					}
					> .iconfont {
						margin-right: 0.4rem;
					}
					> .slider {
						width: 8rem;
					}
				}
				> div.select {
					> .iconfont {
						margin-right: 4px;
					}
					> .my-select,
					> .ant-select {
						width: 12rem;
						text-align: left;
						font-size: 1.4rem;
					}
				}
				> div.btn {
					min-width: 2.8rem;
					cursor: pointer;
					border-radius: 2px;
					transition: background-color 0.2s;
					background-color: rgba(0, 0, 0, 0);
					&:hover {
						background-color: rgba(0, 0, 0, 0.08);
					}
				}
				> div.color {
					width: 4rem;
					height: 2.5rem;
					cursor: pointer;
					background-image: linear-gradient(to bottom, #ff0000, #ffff00);
				}
			}
		}
	}

	/* 平板适配 - 每行4个按钮 */
	@media screen and (max-width: 768px) {
		.tool-content {
			grid-template-columns: repeat(3, 1fr);
			gap: 8px 6px;
			padding: 0 8px;
		}
	}

	/* 手机适配 - 每行3个按钮 */
	@media screen and (max-width: 480px) {
		.tool-content {
			grid-template-columns: repeat(3, 1fr);
			gap: 6px 4px;
			padding: 0 6px;
		}

		.header-btn {
			font-size: 11px;
			padding: 2px 4px;
			min-width: 28px;
		}
	}
</style>
