import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, ref } from "vue";
import ABCJS, {
	AbcElem,
	AbcVisualParams,
	ClickListenerAnalysis,
	ClickListenerDrag,
	NoteTimingEvent,
	SynthObjectController,
} from "abcjs";
import "ABCJS/ABCJS-audio.css";
import styles from "./index.module.less";
import { showConfirmDialog, showToast } from "vant";
import Keys from "../component/keys";
import { Collapse, CollapseItem } from "@varlet/ui";
import { IAbc, IMeasure, INote, INoteActive } from "../types";
import {
	ABC_DATA,
	createMeasure,
	createNote,
	formateAbc,
	getKeyStep,
	moveNoteKey,
	renderMeasures,
} from "./runtime";
import TheIcon from "/src/components/The-icon";
import { cloneDeep } from "lodash";
import TheSpeed from "./component/the-speed";
import { getImage } from "./images";
import {
	NButton,
	NDropdown,
	NGi,
	NGrid,
	NIcon,
	NInput,
	NInputNumber,
	NModal,
	NPopover,
	NPopselect,
	NSelect,
	NSpace,
	NSpin,
	useMessage,
} from "naive-ui";
import { LongArrowAltDown, LongArrowAltUp, GripLinesVertical } from "@vicons/fa";
import { svg2canvas } from "/src/utils/svg2canvas";
import { downloadFile } from "/src/utils";
import FileBtn, { IFileBtnType } from "./component/file-btn";
import TheSetting from "./component/the-setting";
import { useRoute } from "vue-router";
import { api_musicSheetCreationDetail, api_musicSheetCreationUpdate } from "../api";
import instrumentsNames from "/src/constant/instrmentsNames.json";
import { ALL_NOTES, ALL_Pitches } from "./noteData";
import { Close } from "@vicons/ionicons5";
import { UseDraggable } from "@vueuse/components";
import { getQuery } from "/src/utils/queryString";
import Metronome, { metronomeData } from "/src/helpers/metronome";
import cleanDeep from "clean-deep";

export const initMusic = (total: number): IMeasure[] => {
	return new Array(total).fill(0).map((item, index) => {
		return {
			measureNumber: index + 1,
			barline: "|",
			celf: "",
			key: "",
			repeat: "",
			meter: "",
			notes: [
				{
					accidental: "",
					clef: "",
					meter: "",
					content: "z",
					noteType: "4",
					play: [],
					key: "",
					speed: "",
					dynamics: "",
					dCode: "",
					tie: "",
					tCode: "",
					dot: "",
					slus: "",
					tieline: "",
					segno: "",
				},
			],
		};
	});
};

function moveNote(note: string, step: number) {
	var x = ALL_Pitches.indexOf(note);
	if (x >= 0) {
		const _note = ALL_Pitches[x - step];
		return _note ? _note : note;
	}
	return note;
}

export default defineComponent({
	name: "Home",
	setup() {
		const route = useRoute();
		const message = useMessage();
		const popup = reactive({
			instrument: false,
			selectSubjectShow: false, // 声部弹窗
			moveKeyShow: false, // 移调弹窗
			speedShow: false, // 节拍弹窗
			accidentalsShow: false, // 临时升降记号弹窗
			clefShow: false, // 谱号弹窗
			keyShow: false, // 调号弹窗
			meterShow: false, // 拍号弹窗
			playShow: false, // 演奏技法弹窗
			dynamicsShow: false, // 力度标记弹窗
			barShow: false, // 小节弹窗
			mearseDeleteShow: false,
			staffShow: false, // 五线谱弹窗
			settingShow: false, // 设置弹窗
			selectMearesShow: false, // 选择小节弹窗
		});
		const data = reactive({
			loading: true,
			drawCount: 0,
			isSave: true,
			musicId: "",
			musicName: "", // 曲谱名称
			creator: "", // 创建者
			subjectId: "", // 声部
			speed: "",
			music: "",
			playState: false, // 播放状态
			active: null as unknown as INoteActive,
			select: {
				state: false,
				list: [] as any[],
				parmas: null as any,
			},
			isClickNote: false,
			/** 音符类型 */
			noteType: "",
			/** 选择小节范围 */
			selectMeasures: {
				state: false,
				x: 0,
				y: 0,
				start: 1,
				startNote: null as any,
				end: 0,
				endNote: null as any,
				max: 30,
			},
			slide: ["note", "meter", "dynamics"],

			morePlay: false, // 更多演奏技法

			addMearseType: "pre" as "pre" | "next" | "finish", // 添加小节类型
			addMearseNumber: 1, // 添加小节数量

			deleteMearseType: "ing" as "ing" | "finish", // 删除小节类型
			loadingAudioSrouce: false, // 加载音频资源
			/** 移调类型 */
			moveKeyType: "inset" as "inset" | "up" | "down", // 移调类型
			activePlayNote: null as any, // 当前演奏音符
		});
		const noteTypes = ABC_DATA.types.map((item) => item.value).filter(Boolean);
		const accidentals = ABC_DATA.accidentals.map((item) => item.value).filter(Boolean);
		const clefs = ABC_DATA.clef.map((item) => item.value).filter(Boolean);
		const playTypes = ABC_DATA.play.map((item) => item.value).filter(Boolean);
		const dynamics = ABC_DATA.dynamics
			.map((item) => item.value)
			.flat()
			.filter(Boolean);
		const barTypes = ABC_DATA.bar.map((item) => item.value).filter(Boolean);
		console.log("🚀 ~ noteTypes:", noteTypes, accidentals, clefs, playTypes, dynamics);

		/** 点击音符 */
		const clickListener = (
			abcElem: AbcElem,
			tuneNumber: number,
			classes: string,
			analysis: ClickListenerAnalysis,
			drag: ClickListenerDrag
		) => {
			// console.log("🚀 ~ data.select.state:", data.select.state);
			let indexStr: any = abcElem.chord?.find((n) => n.position === "left")?.name || "";
			indexStr = indexStr.split(".").map((n: string) => Number(n));
			const active = {
				...cloneDeep(abcElem),
				measureIndex: indexStr[0],
				noteIndex: indexStr[1],
				isFirstChecked: true,
			};

			// 选择范围模式
			if (data.select.state) {
				data.select.list.push(active);
				if (data.select.list.length === 1) {
					showToast("请先选择结束音符");
				}
				if (data.select.list.length === 2) {
					console.log(data.select.list);
					data.select.list = data.select.list.sort((a, b) => a.startChar - b.startChar);
					handleSelectNote();
				}
				return;
			}
			data.active = active;

			console.log(
				"🚀 ~ abcElem:",
				abcElem,
				data.music.substring(data.active.startChar, data.active.endChar)
			);
			if (data.active?.el_type === "note") {
				const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
				if (totalTime) {
					const progress = (data.active as any).currentTrackMilliseconds / 1000 / totalTime;
					// console.log("🚀 ~ data.active:", progress);
					(abcData.synthControl as any).seek(progress);
				}
			}
			// if (abcElem.el_type === "tempo") {
			// 	abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
			// }
			if (drag && drag.step) {
				handleChange({ type: "move", value: { action: "drag", step: drag.step } });
				return;
			}
			if (!abcElem?.midiPitches) return;
			ABCJS.synth.playEvent(abcElem.midiPitches, abcElem.midiGraceNotePitches, 1000);
		};

		const textAreaRef = ref();
		const abcData = reactive({
			visualObj: null as any,
			midiBuffer: null as unknown as ABCJS.MidiBuffer,
			abcOptions: {
				selectionColor: "#0f81ff",
				jazzchords: true,
				add_classes: true,
				clickListener: clickListener,
				responsive: "resize",
				dragging: true,
				selectTypes: ["note"],
				visualTranspose: 0,
				wrap: {
					minSpacing: 0.1,
					maxSpacing: 2.7,
					preferredMeasuresPerLine: 4,
				},
				staffwidth: 800,
			} as AbcVisualParams,
			synthControl: null as unknown as SynthObjectController,
			synthOptions: {
				program: 0,
				// soundFontUrl: "/soundFonts/",
				// soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/", // 默认 FluidR3_GM
				// soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/MusyngKite/", // Musyng Kite
			},
			abc: {
				celf: "K:treble",
				minUnit: "L:1/4",
				meter: "M:4/4",
				speed: "Q:1/4=60",
				key: "K:C",
				visualTranspose: 0,
				subjectCode: "acoustic_grand_piano",
				measures: initMusic(30),
			} as IAbc,
		});

		/** 添加音符 */
		const handleCreateNote = (measureIndex: number, noteIndex: number, options: INote) => {
			// console.log("🚀 ~ noteIndex:", noteIndex);
			const measure = abcData.abc.measures[measureIndex];
			if (measure) {
				measure.notes.splice(noteIndex + 1, 0, options);
			}
		};

		const hideCursor = () => {
			const cursor = document.querySelector("#paper svg .ABCJS-cursor");
			if (cursor) {
				cursor.setAttribute("x1", "0");
				cursor.setAttribute("x2", "0");
				cursor.setAttribute("y1", "0");
				cursor.setAttribute("y2", "0");
			}
		};

		const cursorControl = {
			onReady: function () {},
			onStart: function () {
				console.log("开始");
				data.playState = true;
				var svg = document.querySelector("#paper svg");
				var cursor = document.createElementNS("http://www.w3.org/2000/svg", "line");
				cursor.setAttribute("class", "ABCJS-cursor");
				cursor.setAttributeNS(null, "x1", "0");
				cursor.setAttributeNS(null, "y1", "0");
				cursor.setAttributeNS(null, "x2", "0");
				cursor.setAttributeNS(null, "y2", "0");
				svg?.appendChild(cursor);
			},
			onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {
				if (!data.playState) return;
				// console.log("🚀 ~ beatNumber:", (beatNumber / totalBeats) * totalTime);
				const time = (beatNumber / totalBeats) * totalTime;
				metronomeData.metro.sound(time);
			},
			onEvent: (ev: any) => {
				if (!data.playState) return;
				if (ev.measureStart && ev.left === null) return; // this was the second part of a tie across a measure line. Just ignore it.
				if (popup.selectMearesShow) {
					const startTime = data.selectMeasures.startNote?.currentTrackMilliseconds || 0;
					const endNote: any = data.selectMeasures.endNote ? data.selectMeasures.endNote : null;
					// console.log("🚀 ~ endNote:", ev.milliseconds , endNote.currentTrackMilliseconds)
					if (
						ev.milliseconds < startTime ||
						(endNote && ev.milliseconds > endNote.currentTrackMilliseconds)
					) {
						const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
						if (totalTime) {
							const progress = startTime / 1000 / totalTime;
							nextTick(() => {
								(abcData.synthControl as any).seek(progress);
							});
						}
					}
				}
				data.activePlayNote = { ...ev };
				var cursor = document.querySelector("#paper svg .ABCJS-cursor");
				if (cursor) {
					cursor.setAttribute("x1", ev.left + ev.width / 2);
					cursor.setAttribute("x2", ev.left + ev.width / 2);
					cursor.setAttribute("y1", ev.top);
					cursor.setAttribute("y2", ev.top + ev.height);
				}
			},
			onFinished: function () {
				console.log("finished");
				data.playState = false;
				var els = document.querySelectorAll("svg .highlight");
				for (var i = 0; i < els.length; i++) {
					els[i].classList.remove("highlight");
				}
				hideCursor();
			},
		};

		const staffNotes = ALL_NOTES();
		const loadMiniMp3 = async () => {
			data.loadingAudioSrouce = true;
			const midiBuffer = new ABCJS.synth.CreateSynth();
			const str = `X: 1\nM:4/4\nL:1/4\n${staffNotes}`;
			const visualObj = ABCJS.parseOnly(str);
			await midiBuffer.init({
				visualObj: visualObj[0],
				options: { ...abcData.synthOptions },
			});
		};

		const resetMidi = (useActive = false) => {
			const midiBuffer = new ABCJS.synth.CreateSynth();
			midiBuffer
				.init({
					visualObj: abcData.visualObj,
					options: { ...abcData.synthOptions },
				})
				.then(() => {
					abcData.synthControl
						.setTune(abcData.visualObj, useActive, {
							midiTranspose: abcData.abc.visualTranspose,
							program: abcData.synthOptions.program,
						})
						.then(function (response) {
							data.loadingAudioSrouce = false;
							// console.log("Audio successfully loaded.", {...abcData.synthControl});
							// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
						})
						.catch((err) => {
							console.log(err);
						});
				});
		};

		const togglePlay = (type: "play" | "pause" | "reset") => {
			if (type === "play") {
				abcData.synthControl.play();
				data.playState = true;
			} else if (type === "pause") {
				abcData.synthControl.play();
				// abcData.synthControl.pause();
				data.playState = false;
				hideCursor();
				const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
				if (totalTime && data.activePlayNote?.milliseconds !== undefined) {
					const progress = data.activePlayNote.milliseconds / 1000 / totalTime;
					nextTick(() => {
						(abcData.synthControl as any).seek(progress);
					});
				}
			} else {
				abcData.synthControl.restart();
				nextTick(() => {
					if (!data.playState) {
						abcData.synthControl.play();
					}
				});
			}
			// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl.timer.noteTimings);
		};

		const renderSvg = () => {
			abcData.visualObj = ABCJS.renderAbc("paper", data.music, {
				...abcData.abcOptions,
				visualTranspose: abcData.abc.visualTranspose,
			})[0];
			if (data.drawCount < 3) {
				console.log("🚀 ~ visualObj:", abcData.visualObj);
			}
		};

		const renderBoxRect = () => {
			const svg = document.querySelector("#paper svg");
			const padding = 4;
			let measureNumber = 0;
			for (let i = 0; i < abcData.visualObj.lines.length; i++) {
				const line = abcData.visualObj.lines[i];
				// console.log("🚀 ~ line:", line);
				for (let j = 0; j < line.staff.length; j++) {
					const staff = line.staff[j];
					const voices = [...staff.voices].flat();
					for (let l = 0; l < voices.length; l++) {
						const item = voices[l];
						if (item.el_type === "bar") {
							measureNumber++;
						}
						// console.log(item.el_type);
						if (["note", "keySignature", "clef", "timeSignature"].includes(item.el_type)) {
							const box = item.abselem.elemset?.[0]?.getBBox?.() || null;
							// console.log("🚀 ~ box:", item.abselem, box);
							if (box) {
								const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
								rect.setAttributeNS(null, "x", box.x - padding + "");
								rect.setAttributeNS(null, "y", box.y - padding + "");
								rect.setAttributeNS(null, "width", box.width + padding * 2 + "");
								rect.setAttributeNS(null, "height", box.height + padding * 2 + "");
								rect.setAttributeNS(null, "fill", "rgba(0,0,0,0)");
								rect.setAttributeNS(null, "stroke", "rgba(0,0,0,0)");
								rect.setAttributeNS(null, "rx", "2");
								rect.classList.add("abcjs-note-hover");
								svg?.appendChild(rect);
							}
						}
					}
				}
			}
			console.log(measureNumber);
			data.selectMeasures.max = measureNumber;
		};

		let saveTimer: any = null;
		const autoSave = () => {
			saveTimer && clearTimeout(saveTimer);
			saveTimer = setTimeout(() => {
				handleSaveMusic(false);
			}, 5000);
		};

		/**
		 * @param isProduct 是否是生成曲谱
		 */
		const handleResetRender = () => {
			if (data.drawCount > 0) {
				abcData.synthControl.disable(true);
				if (data.playState) {
					data.playState = false;
				}
			}
			data.isSave = false;
			return new Promise((resolve) => {
				nextTick(() => {
					data.music = renderMeasures(abcData.abc);
					renderSvg();
					resetMidi(data.drawCount > 0 ? true : false);
					renderBoxRect();
					try {
						productMetronomeData();
					} catch (error) {
						console.log("🚀 ~ error:", error);
					}

					resolve(1);
					if (data.drawCount > 0) {
						const host = location.host;
						if (host.includes("localhost") || host.includes("192")) return;
						autoSave();
					}

					textAreaRef.value && (textAreaRef.value.value = data.music);
					data.drawCount++;
				});
			});
		};

		/** 生成曲谱节拍器数据 */
		const productMetronomeData = () => {
			const times = new ABCJS.TimingCallbacks(abcData.visualObj);
			const list: any[] = [];
			let meter = abcData.abc.meter || "";
			// length - 1是为了去除最后一个空的结束事件
			for (let i = 0; i < times.noteTimings.length - 1; i++) {
				const timeNote = times.noteTimings[i];
				const abcNote = getNextNote(timeNote.startChar as number);
				let indexStr: any = abcNote.chord?.find((n) => n.position === "left")?.name || "";
				indexStr = indexStr.split(".").map((n: string) => Number(n));
				if (indexStr.length === 2) {
					const measure = abcData.abc.measures[indexStr[0]];
					// 如果小节里面有拍号，后面一直延用这个拍号,以此类推, ps: 下个版本
					// if (measure.meter){
					// 	meter = measure.meter;
					// }
					const reg = new RegExp(/M:(\d+)\/\d+/);
					const numerator = Number(meter.match(reg)?.[1]);
					// console.log("🚀 ~ reg:", meter.match(reg)?.[1], abcData.abc.meter)
					// console.log("🚀 ~ measure:", measure)
					const note = measure.notes[indexStr[1]];
					list.push({
						...note,
						timeNote,
						abcNote,
						measure: {
							numerator,
						},
					});
				}
			}
			// console.log("abcData.abc.measures", list);
			if (!metronomeData.metro) {
				metronomeData.metro = new Metronome();
			}
			try {
				metronomeData.activeIndex = -1;
				metronomeData.metro.init(list);
			} catch (error) {
				console.log("🚀 ~ 生成节拍器数据错误:", error);
			}
		};

		// 高亮选中的音符
		const rangeHighlight = (startChar: number) => {
			// console.log(data.active.endChar, abcData.visualObj.getElementFromChar(data.active.startChar));
			const abcElem: AbcElem = abcData.visualObj.getElementFromChar(startChar);
			if (abcElem) {
				abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
			}
			return abcElem;
		};

		const useIndexGetNote = (indexStr: string) => {
			const index = data.music.indexOf(indexStr);
			const note = abcData.visualObj.getElementFromChar(index);
			return note;
		};

		const getMeasureNotes = (measureIndex: number) => {
			const measure = abcData.abc.measures[measureIndex];
			const notes = [];
			for (let k = 0; k < measure.notes.length; k++) {
				const indexStr = `${measureIndex}.${k}`;
				const note = useIndexGetNote(indexStr);
				notes.push(note);
			}
			return notes;
		};

		const handleClose = () => {
			// 判断是否在应用中
			if (window.matchMedia("(display-mode: standalone)").matches) {
				window.onbeforeunload = null;
				window.parent.postMessage(
					{
						api: "notation_exit",
					},
					"*"
				);
			} else {
				window.close();
			}
		};

		/**
		 *
		 * @param key
		 * @param type  note 音符 accidental 临时升降记号
		 * @returns
		 */
		const handleChange = async (params: { type: string; value: any }) => {
			abcData.synthControl.disable(true);
			if (data.playState) {
				data.playState = false;
			}
			const type = params.type;
			const value = params.value;
			const activeNote =
				abcData.abc.measures[data.active?.measureIndex]?.notes[data.active?.noteIndex] || null;

			if (type === "exit") {
				if (!data.isSave) {
					showConfirmDialog({
						title: "温馨提示",
						message: "还没保存,是否保存?",
					})
						.then(async () => {
							const msg = message.loading("保存中...");
							await handleSaveMusic(false);
							setTimeout(() => {
								msg.type = "success";
								msg.content = "保存成功";
								setTimeout(() => {
									msg.destroy();
									handleClose();
								}, 500);
							}, 300);
						})
						.catch(() => {
							handleClose();
						});
					return;
				}
				handleClose();
			}
			// console.log(params, activeNote);
			if (type === "type") {
				// 设置音符类型
				data.noteType = value;
				if (activeNote) {
					activeNote.noteType = value;
					await handleResetRender();
					const abcElem: AbcElem = rangeHighlight(data.active.startChar);
					const active: any = abcElem
						? {
								...cloneDeep(abcElem),
								measureIndex: data.active.measureIndex,
								noteIndex: data.active.noteIndex,
								isFirstChecked: true,
						  }
						: null;
					data.active = active;
				}
				return;
			}

			// 分割
			if (type === "segno") {
				if (!data.active) {
					showToast("请先选择音符");
					return;
				}
				if (!activeNote) return;
				activeNote.segno = activeNote.segno ? "" : value;
				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}

			// 修改音符,或者添加音符
			if (type === "note") {
				if (data.active && data.active.el_type == "note") {
					const activeNote =
						abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
					const _values = value.split("-");
					if (data.active.isFirstChecked) {
						activeNote.content = _values[0];
						activeNote.noteType = data.noteType;
						if (_values[1]) {
							activeNote.accidental = _values[1] || "";
						}
						data.active.isFirstChecked = false;
					}
					await handleResetRender();
					const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
					if (oldNote?.abselem?.beam?.elems?.length) {
						// 判断是否需要分割beam
						const elems: AbcElem[] = oldNote.abselem.beam.elems;
						const beatDuration = abcData.visualObj.getBeatLength();
						const beamLength = elems.map((n) => n.duration).reduce((a, b) => a + b);
						if (beamLength >= beatDuration) {
							abcData.abc.measures[data.active.measureIndex].notes[data.active.noteIndex].segno = " ";
							await handleResetRender();
						}
					}
					if (oldNote?.midiPitches) {
						ABCJS.synth.playEvent(oldNote.midiPitches, oldNote.midiGraceNotePitches, 1000);
					}
					const nextNote =
						abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex + 1];
					if (nextNote) {
						const abcNextElem: AbcElem = useIndexGetNote(
							`${data.active.measureIndex}.${data.active.noteIndex + 1}`
						);
						rangeHighlight(abcNextElem.startChar);
						data.active = {
							...abcNextElem,
							measureIndex: data.active.measureIndex,
							noteIndex: data.active.noteIndex + 1,
							isFirstChecked: true,
						};
					} else {
						const notes = getMeasureNotes(data.active.measureIndex);
						const duration = notes.map((n) => n.duration).reduce((a, b) => a + b);
						if (duration >= 1) {
							// 小节内音符总时值过长,自动跳转到下一小节
							const nextMeasureNote = abcData.abc.measures[data.active.measureIndex + 1]?.notes[0];
							if (nextMeasureNote) {
								const abcNextElem: AbcElem = useIndexGetNote(`${data.active.measureIndex + 1}.${0}`);
								rangeHighlight(abcNextElem.startChar);
								data.active = {
									...abcNextElem,
									measureIndex: data.active.measureIndex + 1,
									noteIndex: 0,
									isFirstChecked: true,
								};
							} else {
								// 到最后一个小节的最后一个音符了
								rangeHighlight(data.active.startChar);
								data.active.isFirstChecked = true;
							}
						} else {
							handleCreateNote(
								data.active.measureIndex,
								data.active.noteIndex,
								createNote({
									content: "z",
									noteType: data.noteType,
								})
							);
							await handleResetRender();
							const newNote = useIndexGetNote(
								`${data.active.measureIndex}.${data.active.noteIndex + 1}`
							);
							rangeHighlight(newNote.startChar);
							data.active = {
								...newNote,
								measureIndex: data.active.measureIndex,
								noteIndex: data.active.noteIndex + 1,
								isFirstChecked: true,
							};
						}
					}
				}
			}

			// 临时升降记号
			if (type === "accidentals") {
				if (!data.active) {
					message.warning("请先选择音符");
					return;
				}
				if (activeNote.content === "z") {
					message.warning("休止符无法添加临时升降记号");
					return;
				}
				activeNote.accidental = activeNote.accidental == value ? "" : value;
				await handleResetRender();
				const abcElem: AbcElem = rangeHighlight(data.active.startChar);
				const active: any = abcElem
					? {
							...cloneDeep(abcElem),
							measureIndex: data.active.measureIndex,
							noteIndex: data.active.noteIndex,
							isFirstChecked: true,
					  }
					: null;
				data.active = active;
			}

			// 谱号
			if (type === "clef") {
				if (data.active) {
					if (!activeNote) return;
					activeNote.clef = `[${value}]`;
					await handleResetRender();
				} else {
					abcData.abc.celf = value;
					handleResetRender();
				}
			}

			// 调号
			if (type === "key") {
				if (data.active) {
					if (!activeNote) return;
					activeNote.key = `[${value}]`;
					await handleResetRender();
				} else {
					abcData.abc.key = value;
					await handleResetRender();
				}
			}

			// 拍号
			if (type === "meter") {
				if (data.active && data.active.measureIndex !== 0) {
					if (!activeNote) return;
					const measure = abcData.abc.measures[data.active.measureIndex];
					measure.meter = `[${value}]`;
					await handleResetRender();
					const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
					rangeHighlight(oldNote.startChar);
				} else {
					abcData.abc.meter = value;
					await handleResetRender();
				}
			}

			//  演奏技法
			if (type === "play") {
				if (!data.active) {
					message.warning("请先选择音符");
					return;
				}
				if (!activeNote) return;
				if (activeNote.play.includes(value)) {
					activeNote.play = activeNote.play.filter((item) => item !== value);
				} else {
					activeNote.play.push(value);
				}
				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}
			// 力度标记,渐强渐弱
			if (type === "dynamics") {
				if (!data.active) {
					message.info("请先选择音符");
					return;
				}
				if (!activeNote) return;
				if (Array.isArray(value)) {
					if (activeNote?.dynamics) {
						activeNote.dynamics = "";
						for (let i = 0; i < abcData.abc.measures.length; i++) {
							const measure = abcData.abc.measures[i];
							for (let j = 0; j < measure.notes.length; j++) {
								const note = measure.notes[j];
								if (note.dCode === activeNote.dCode) {
									note.dynamics = "";
								}
							}
						}

						await handleResetRender();
					} else {
						data.select.list = [cloneDeep(data.active)];
						data.select.state = true;
						data.select.parmas = params;
						message.info("请选择结束音符");
					}
					return;
				}
				if (activeNote.dynamics === value) {
					activeNote.dynamics = "";
				} else {
					activeNote.dynamics = value;
				}
				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}

			// 延音
			if (type === "tie") {
				if (!data.active) {
					message.info("请先选择音符");
					return;
				}
				if (!activeNote) return;
				if (Array.isArray(value)) {
					if (activeNote?.tie) {
						activeNote.tie = "";
						for (let i = 0; i < abcData.abc.measures.length; i++) {
							const measure = abcData.abc.measures[i];
							for (let j = 0; j < measure.notes.length; j++) {
								const note = measure.notes[j];
								if (note.tCode === activeNote.tCode) {
									note.tie = "";
								}
							}
						}

						await handleResetRender();
						return;
					} else {
						data.select.list = [cloneDeep(data.active)];
						data.select.state = true;
						data.select.parmas = params;
						message.info("请选择结束音符");
						return;
					}
				}

				const abcElem: any = getNextNote(data.active.endChar);
				if (activeNote.tieline) {
					activeNote.tieline = "";
				} else {
					if (data.active.averagepitch != abcElem.averagepitch) {
						message.warning("必须同音高才能添加延音线");
						return;
					}
					activeNote.tieline = value;
				}

				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}

			// 反复
			if (type === "repeat") {
				if (!data.active) {
					return;
				}
				const activeMeasure = abcData.abc.measures[data.active.measureIndex] || null;
				if (!activeMeasure) return;
				if (activeMeasure.repeat === value) {
					activeMeasure.repeat = "";
				} else {
					activeMeasure.repeat = value;
				}
				await handleResetRender();
				rangeHighlight(data.active.startChar + value.length);
			}

			// 小节线
			if (type === "barline") {
				if (!data.active) {
					return;
				}
				const activeMeasure = abcData.abc.measures[data.active.measureIndex] || null;
				if (!activeMeasure) return;
				// 如果是开始重复,就修改前一个小节的barline
				if (value === "|:") {
					const prevMeasure = abcData.abc.measures[data.active.measureIndex - 1] || null;
					if (!prevMeasure) return;
					prevMeasure.barline = value;
				} else {
					activeMeasure.barline = value;
				}
				await handleResetRender();
			}

			// 速度
			if (type === "speeds") {
				if (data.active) {
					if (data.active.measureIndex === 0 && data.active.noteIndex === 0) {
						abcData.abc.speed = value;
						await handleResetRender();
					} else {
						const activeNote =
							abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
						if (!activeNote) return;
						activeNote.speed = `[${value}]`;
						await handleResetRender();
					}
					rangeHighlight(data.active.startChar);
				} else {
					abcData.abc.speed = value;
					await handleResetRender();
				}
			}

			//  附点
			if (type === "dot") {
				if (!data.active) {
					showToast("请先选择音符");
					return;
				}
				if (!activeNote) return;
				activeNote.dot = activeNote.dot ? "" : value;
				// activeNote.dot = activeNote.dot ? "" : NOTE_DOT[activeNote.noteType];
				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}

			// 3连音
			if (type === "slus") {
				const activeNote =
					abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
				if (!activeNote) return;
				activeNote.slus = activeNote.slus === value ? "" : value;
				await handleResetRender();
				rangeHighlight(data.active.startChar);
			}

			// 移动音符
			if (type === "move") {
				const step = value._step ? value._step : value.action === "up" ? -1 : 1;
				if (!activeNote) return;
				activeNote.content = moveNote(activeNote.content, step);
				// arr now contains elements that are either a chord, a decoration, a note name, or anything else. It can be put back to its original string with .join("").

				await handleResetRender();
				const _abcElem = rangeHighlight(data.active.startChar);
				if (!_abcElem?.midiPitches) return;
				ABCJS.synth.playEvent(_abcElem.midiPitches, _abcElem.midiGraceNotePitches, 1000);
			}

			// 删除音符
			if (type === "delete") {
				if (!data.active) return;
				if (data.active.startChar === 0) return;
				abcData.abc.measures[data.active.measureIndex].notes.splice(data.active.noteIndex, 1);
				if (abcData.abc.measures[data.active.measureIndex].notes.length === 0) {
					abcData.abc.measures.splice(data.active.measureIndex, 1);
				}
				handleResetRender();
				data.active = null as unknown as INoteActive;
			}
		};

		const getNextNote = (index: number): AbcElem => {
			const abcElem = abcData.visualObj.getElementFromChar(index);
			if (abcElem.el_type === "note") {
				return abcElem;
			} else {
				return getNextNote(abcElem.endChar);
			}
		};

		const handleDeleteNote = () => {
			if (!data.active) return;
			if (data.active.startChar === 0) return;
			abcData.abc.measures[data.active.measureIndex].notes.splice(data.active.noteIndex, 1);
			if (abcData.abc.measures[data.active.measureIndex].notes.length === 0) {
				abcData.abc.measures.splice(data.active.measureIndex, 1);
			}
			handleResetRender();
			data.active = null as unknown as INoteActive;
		};

		const clearSelectNote = () => {
			data.active = null as unknown as INoteActive;
			const notes = document.querySelectorAll(".abcjs-note_selected");
			notes.forEach((item) => {
				item.classList.remove("abcjs-note_selected");
				item.setAttribute("fill", "currentColor");
			});
		};

		const handleSelectNote = async () => {
			const type = data.select.parmas?.type;
			const value = data.select.parmas?.value;
			const startItem = data.select.list[0];
			const endItem = data.select.list[1];
			// 力度标记,渐强渐弱
			if (type === "dynamics") {
				if (
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dynamics ||
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dynamics
				) {
					message.warning("已经添加了力度标记");
				} else {
					const crescendo = Date.now() + "";
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dynamics = value[0];
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].dCode = crescendo;
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dynamics = value[1];
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].dCode = crescendo;
					await handleResetRender();
				}
			}

			// 连音
			if (type === "tie") {
				const crescendo = Date.now() + "";
				if (abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie) {
					const tie = abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie;
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie = value[0] + tie;
				} else {
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tie = value[0];
					abcData.abc.measures[startItem.measureIndex].notes[startItem.noteIndex].tCode = crescendo;
				}
				if (abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie) {
					const tie = abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie;
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie = tie + value[1];
				} else {
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tie = value[1];
					abcData.abc.measures[endItem.measureIndex].notes[endItem.noteIndex].tCode = crescendo;
				}
				await handleResetRender();
			}
			data.select.state = false;
			data.select.list = [];
			data.select.parmas = null;
			clearSelectNote();
			message.destroyAll();
		};

		/** 移调 */
		const handleMoveKey = async (item: (typeof ABC_DATA.key)[0]) => {
			// const moveData = getKeyStep(item.value, abcData.abc.key, data.moveKeyType);
			// console.log("🚀 ~ item:", abcData.abc.key, "=>", item.value, moveData);
			// 将所有的音符移调
			// for (let i = 0; i < abcData.abc.measures.length; i++) {
			// 	const measure = abcData.abc.measures[i];
			// 	for (let j = 0; j < measure.notes.length; j++) {
			// 		const note = measure.notes[j];
			// 		if (note.content == "z") continue;
			// 		const content = moveNoteKey(note.content, moveData);
			// 		const _a = content.substring(0, 1);
			// 		if (_a === "^" || _a === "_") {
			// 			note.content = content.substring(1);
			// 		} else {
			// 			note.content = content;
			// 		}
			// 		console.log("🚀 ~ note.content:", note.content);
			// 	}
			// }
			// // console.log(abcData.abc.visualTranspose, item.step, item.value)
			// abcData.abc.key = item.value;
			// const step =
			// 	data.moveKeyType === "up"
			// 		? item.step > 0
			// 			? item.step
			// 			: item.step + 12
			// 		: data.moveKeyType === "down"
			// 		? item.step < 0
			// 			? item.step
			// 			: item.step + 12
			// 		: item.step;
			abcData.abc.visualTranspose = item.step;
			popup.moveKeyShow = false;
			if (data.playState) {
				abcData.synthControl.disable(true);
				data.playState = false;
			}
			await handleResetRender();
		};

		const handleKeyUp = (e: KeyboardEvent) => {
			if (!data.active) return false;
			console.log(e.key);
			if (e.key === "Backspace") {
				handleChange({ type: "delete", value: "" });
			}
			if (/^[A-Ga-g]$/.test(e.key)) {
				handleChange({ type: "note", value: e.key.toLocaleUpperCase() });
			}
			if (["ArrowUp", "ArrowDown"].includes(e.key)) {
				e.preventDefault();
				e.stopPropagation();
				handleChange({ type: "move", value: { action: e.key === "ArrowUp" ? "up" : "donw" } });
				return false;
			}
		};

		/** 重置曲谱 */
		const handleCreateSvg = () => {
			abcData.abc.measures = initMusic(30);
			handleResetRender();
		};

		const instruments = computed(() => {
			return ABCJS.synth.instrumentIndexToName.map((name, index) => ({
				label: instrumentsNames[name as keyof typeof instrumentsNames],
				value: index,
			}));
		});
		const instrumentName = computed(() => {
			const code = ABCJS.synth.instrumentIndexToName[abcData.synthOptions.program];
			return instrumentsNames[code as keyof typeof instrumentsNames];
		});
		const getDetailData = async () => {
			data.loading = true;
			const query: any = getQuery();
			const res = await api_musicSheetCreationDetail(query.id);
			if (res?.code == 200) {
				data.musicId = res.data.id || "";
				data.musicName = res.data.name || "";
				data.creator = res.data.creator || "";
				let abc = "" as any;
				try {
					abc = JSON.parse(res.data.creationData);
				} catch (error) {
					console.log(error);
				}
				if (abc) {
					console.log("🚀 ~ abc:", abc);

					abcData.abc.celf = abc.celf || "K:treble";
					abcData.abc.key = abc.key.value || abc.key || "K:C";
					abcData.abc.meter = abc.meter.value || abc.meter || "M:4/4";
					abcData.abc.speed = abc.speed || "Q:1/4=60";
					abcData.abc.visualTranspose = abc.visualTranspose || 0;
					abcData.abc.subjectCode = abc.subjectCode || "acoustic_grand_piano";
					const _instruments = ABCJS.synth.instrumentIndexToName.indexOf(abcData.abc.subjectCode as any);
					abcData.synthOptions.program = _instruments > -1 ? _instruments : 0;
					abcData.abc.measures = abc.measures || initMusic(30);
					// console.log("🚀 ~ abcData.abc:", abcData.abc);
				}
			}
			data.loading = false;
		};
		const handleSaveMusic = async (tips = true) => {
			await api_musicSheetCreationUpdate({
				name: data.musicName,
				creator: data.creator,
				creationConfig: renderMeasures(abcData.abc, { hiddenIndex: true }),
				creationData: JSON.stringify(cleanDeep(abcData.abc)),
				id: data.musicId,
				subjectId: 3,
			});
			if (tips) {
				message.success("保存成功");
			}
			data.isSave = true;
		};
		onMounted(async () => {
			await getDetailData();
			loadMiniMp3();
			if (ABCJS.synth.supportsAudio()) {
				abcData.synthControl = new ABCJS.synth.SynthController();
				// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl)
				abcData.synthControl.load("#audio", cursorControl, {
					displayLoop: true,
					displayRestart: true,
					displayPlay: true,
					displayProgress: true,
				});
			}

			console.log(ABCJS);
			await handleResetRender();
			document.addEventListener("keyup", handleKeyUp);
			window.onbeforeunload = (e) => {
				if (!data.isSave) {
					e.preventDefault();
					e.returnValue = "还有没保存的";
				}
			};
			abcData.synthControl.restart();
			const selectMearesBtn = document.querySelector("#selectMearesBtn");
			if (selectMearesBtn) {
				const rect = selectMearesBtn.getBoundingClientRect();
				data.selectMeasures.x = document.body.clientWidth - 320;
				data.selectMeasures.y = rect.top + 70;
				data.selectMeasures.state = true;
			}
		});
		onUnmounted(() => {
			document.removeEventListener("keyup", handleKeyUp);
		});

		const measureComputed = computed(() => {
			if (!data.active) return {} as unknown as IMeasure;
			const activeMeasure = abcData.abc.measures[data.active.measureIndex] || {};
			return activeMeasure;
		});

		const noteComputed = computed(() => {
			if (!data.active) return {} as unknown as INote;
			const activeNote =
				abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || {};
			return activeNote;
		});

		/** 新建曲谱 */
		const handleCreateMusic = () => {
			showConfirmDialog({
				title: "温馨提示",
				message: "是否覆盖当前乐谱?",
			}).then(() => {
				handleCreateSvg();
				data.active = null as unknown as INoteActive;
			});
		};

		/** 添加小节 */
		const handleAddMearse = () => {
			for (let i = 0; i < data.addMearseNumber; i++) {
				if (["pre", "next"].includes(data.addMearseType)) {
					if (!data.active) {
						message.warning("请选择小节");
						return;
					}
					if (data.addMearseType === "pre") {
						abcData.abc.measures.splice(data.active.measureIndex, 0, createMeasure());
					} else if (data.addMearseType === "next") {
						abcData.abc.measures.splice(data.active.measureIndex + 1, 0, createMeasure());
					}
				} else {
					abcData.abc.measures.push(createMeasure());
				}
			}
			popup.barShow = false;
			handleResetRender();
		};

		const handleDeleteMearse = () => {
			if (data.deleteMearseType === "ing") {
				if (!data.active) {
					message.warning("请选择小节");
					return;
				}
				abcData.abc.measures.splice(data.active.measureIndex, 1);
			} else if (data.deleteMearseType === "finish") {
				let len = abcData.abc.measures.length;
				for (let i = len; i > 0; i--) {
					if (
						abcData.abc.measures[i - 1].notes.length === 1 &&
						abcData.abc.measures[i - 1].notes[0].content === "z"
					) {
						if (abcData.abc.measures.length === 1) {
							break;
						}
						abcData.abc.measures.splice(i - 1, 1);
					} else {
						break;
					}
				}
			}
			popup.mearseDeleteShow = false;
			handleResetRender();
		};

		const downPng = async () => {
			abcData.abc.title = `T:${data.musicName}`;
			abcData.abc.creator = `R:${data.creator}`;
			const paper = document.getElementById("exportPng");
			if (!paper) return;
			const abc = renderMeasures(abcData.abc, { hiddenIndex: true, showTitle: true, showCreator: true });
			ABCJS.renderAbc(paper, abc, abcData.abcOptions);
			const svg: any = paper.children[0]?.cloneNode(true);
			const svgBox = paper.getBoundingClientRect();
			svg.setAttribute("width", `${svgBox.width * 3}`);
			svg.setAttribute("height", `${svgBox.height * 3}`);
			const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
			rect.setAttribute("x", "0");
			rect.setAttribute("y", "0");
			rect.setAttribute("width", `${svgBox.width * 10}`);
			rect.setAttribute("height", `${svgBox.height * 10}`);
			rect.setAttribute("fill", "#fff");
			svg.prepend(rect);
			if (svg) {
				const _canvas = svg2canvas(svg.outerHTML);
				// document.body.appendChild(_canvas);
				let el: any = document.createElement("a");
				// 设置 href 为图片经过 base64 编码后的字符串，默认为 png 格式
				el.href = _canvas.toDataURL();
				el.download = data.musicName + ".png";

				// 创建一个点击事件并对 a 标签进行触发
				const event = new MouseEvent("click");
				el.dispatchEvent(event);
			}
		};

		const downRef = ref();
		const downMidi = () => {
			const midi = ABCJS.synth.getMidiFile(abcData.visualObj, {
				chordsOff: true,
				midiOutputType: "link",
				fileName: "曲谱",
			});
			// console.log("🚀 ~ midi:", midi)
			downRef.value.innerHTML = midi;
			downRef.value.querySelector("a").click();
		};
		const downWav = () => {
			try {
				if (abcData.synthControl) {
					abcData.synthControl.download("曲谱.wav");
				}
			} catch (error) {
				const midiBuffer = new ABCJS.synth.CreateSynth();
				midiBuffer
					.init({
						visualObj: abcData.visualObj,
						options: abcData.synthOptions,
					})
					.then(() => {
						midiBuffer.prime().then(() => {
							// console.log(midiBuffer.download());
							downloadFile(midiBuffer.download(), "曲谱.wav");
						});
					});
			}
		};

		const handleDownFile = (type: IFileBtnType) => {
			if (type === "png") {
				downPng();
			} else if (type === "midi") {
				downMidi();
			} else if (type === "wav") {
				downWav();
			}
		};

		const handleExport = () => {
			const input = document.createElement("input");
			input.type = "file";
			input.accept = ".xml,.musicxml";
			input.onchange = (e: any) => {
				const file = e.target.files[0];
				const reader = new FileReader();
				reader.onload = (e: any) => {
					let abc = e.target.result;
					// console.log("🚀 ~ abc:", abc);
					abc = new DOMParser().parseFromString(abc, "text/xml");
					// console.log("🚀 ~ abc:", abc);
					abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
					// console.log(abc);
					const parseData = ABCJS.renderAbc("importRef", abc[0], { responsive: "resize" });
					console.log("🚀 ~ parseData:", parseData);
					abcData.abc = formateAbc(parseData[0], { subjectCode: abcData.abc.subjectCode });
					handleResetRender();
				};
				reader.readAsText(file);
			};
			input.click();
		};

		/** 设置选段小节 */
		const handleSetSelectMeares = (index: number | null, type: "start" | "end") => {
			console.log("🚀 ~ index:", index);
			if (type === "start") {
				const note = index ? useIndexGetNote(`${index - 1}.0`) : null;
				// console.log("🚀 ~ note:", note);
				data.selectMeasures.start = index ? index - 1 : 0;
				data.selectMeasures.startNote = note;
				if (
					data.selectMeasures.start &&
					data.selectMeasures.end &&
					data.selectMeasures.end < data.selectMeasures.start
				) {
					data.selectMeasures.end = 0;
					data.selectMeasures.endNote = null;
				}
			} else {
				const note = index
					? useIndexGetNote(`${index - 1}.${abcData.abc.measures[index - 1]?.notes.length - 1}`)
					: null;
				// console.log("🚀 ~ note:", note);
				data.selectMeasures.end = index ? index - 1 : 0;
				data.selectMeasures.endNote = note;
				if (
					data.selectMeasures.start &&
					data.selectMeasures.end &&
					data.selectMeasures.start > data.selectMeasures.end
				) {
					// console.log(data.selectMeasures.start, data.selectMeasures.end);
					data.selectMeasures.start = 0;
					data.selectMeasures.startNote = null;
				}
			}
		};

		return () => (
			<>
				<div class={styles.container}>
					<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
						<div class={styles.topWrap}>
							<div class={styles.topBtn} onClick={() => handleChange({ type: "exit", value: "exit" })}>
								<div class={[styles.btnImg]}>
									<img class={styles.topBtnIcon} src={getImage("icon_-1.png")} />
								</div>
								<div>退出</div>
							</div>
							<div class={styles.topBtn}>
								<FileBtn
									onSelect={(val: IFileBtnType) => {
										if (val === "newMusic") {
											handleCreateMusic();
										} else if (val === "save") {
											handleSaveMusic();
										} else if (["xml"].includes(val)) {
											handleExport();
										} else if (val === "upload") {
										} else if (["png", "midi", "wav"].includes(val)) {
											handleDownFile(val);
										} else if (val === "print") {
										}
										//  else if (val === "exit") {

										// }
									}}
								/>
								<div>文件</div>
							</div>

							<div class={styles.topLine}></div>

							<div class={styles.topBtn} onClick={() => handleChange({ type: "dot", value: ">" })}>
								<div class={[styles.btnImg, noteComputed.value.dot === ">" && styles.btnImgActive]}>
									<img class={styles.topBtnIcon} src={getImage("icon_1.png")} />
								</div>
								<div>附点</div>
							</div>

							<div class={styles.topLine}></div>

							{ABC_DATA.accidentals.map((item) => (
								<div
									class={styles.topBtn}
									onClick={() => handleChange({ type: "accidentals", value: item.value })}
								>
									<div
										class={[
											styles.btnImg,
											noteComputed.value.accidental === item.value && styles.btnImgActive,
										]}
									>
										<img class={styles.topBtnIcon} src={item.icon} />
									</div>
									<div class={styles.btnName}>{item.name}</div>
								</div>
							))}

							<div class={styles.topLine}></div>

							<div
								class={styles.topBtn}
								onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[0].value })}
							>
								<div
									class={[
										styles.btnImg,
										noteComputed.value.tieline === ABC_DATA.tie[0].value && styles.btnImgActive,
									]}
								>
									<img class={styles.topBtnIcon} src={ABC_DATA.tie[0].icon} />
								</div>
								<div>{ABC_DATA.tie[0].name}</div>
							</div>

							<div
								class={styles.topBtn}
								onClick={() => handleChange({ type: "tie", value: ABC_DATA.tie[1].value })}
							>
								<div
									class={[
										styles.btnImg,
										ABC_DATA.tie[1].value.includes(noteComputed.value.tie) && styles.btnImgActive,
									]}
								>
									<img class={styles.topBtnIcon} src={ABC_DATA.tie[1].icon} />
								</div>
								<div>{ABC_DATA.tie[1].name}</div>
							</div>

							<div class={styles.topLine}></div>

							{ABC_DATA.play.slice(0, 4).map((item) => (
								<div
									class={[styles.topBtn]}
									onClick={() => handleChange({ type: "play", value: item.value })}
								>
									<div
										class={[
											styles.btnImg,
											noteComputed.value.play?.includes(item.value) && styles.btnImgActive,
										]}
									>
										<img class={styles.topBtnIcon} src={item.icon} />
									</div>
									<div>{item.name}</div>
								</div>
							))}

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								trigger="click"
								contentStyle={{ width: "400px" }}
							>
								{{
									trigger: () => (
										<div class={styles.topDownArrow}>
											<img src={getImage("icon_arrow.png")} />
										</div>
									),
									default: () => (
										<NGrid cols={4} yGap={8}>
											{ABC_DATA.play.slice(4).map((item) => (
												<NGi>
													<div
														class={[
															styles.btnItem,
															noteComputed.value.play?.includes(item.value) && styles.btnItemActive,
														]}
														onClick={() => {
															data.morePlay = false;
															handleChange({ type: "play", value: item.value });
														}}
													>
														<div class={styles.btnItemIcon}>
															<TheIcon iconClassName={item.icon} />
														</div>
														<div>{item.name}</div>
													</div>
												</NGi>
											))}
										</NGrid>
									),
								}}
							</NPopover>

							<div class={styles.topLine}></div>

							<NDropdown
								trigger="click"
								options={ABC_DATA.slus as any}
								labelField="name"
								keyField="value"
								onSelect={(val) => {
									console.log(val);
									handleChange({ type: "slus", value: val });
								}}
							>
								<div class={[styles.topBtn]}>
									<div class={styles.btnImg}>
										<img class={styles.topBtnIcon} src={getImage("icon_13.png")} />
									</div>
									<div>连音</div>
								</div>
							</NDropdown>

							<div class={[styles.topBtn, styles.btnDisabled]}>
								<div class={styles.btnImg}>
									<img class={styles.topBtnIcon} src={getImage("icon_14.png")} />
								</div>
								<div>翻转</div>
							</div>

							<div class={styles.topLine}></div>

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								v-model:show={popup.selectSubjectShow}
								trigger="click"
								contentStyle={{ width: "320px" }}
							>
								{{
									trigger: () => (
										<div class={styles.topBtn}>
											<div class={styles.btnImg} onClick={() => (popup.instrument = true)}>
												<img class={styles.topBtnIcon} src={getImage("icon_25.png")} />
											</div>
											<div
												style={{
													overflow: "hidden",
													textOverflow: "ellipsis",
													whiteSpace: "nowrap",
													maxWidth: "60px",
												}}
											>
												{instrumentName.value}
											</div>
										</div>
									),
									default: () => (
										<>
											<div class={styles.btnLineTitle}>选择声部</div>

											<NSelect
												filterable
												options={instruments.value}
												v-model:value={abcData.synthOptions.program}
												onUpdate:value={async () => {
													abcData.synthControl.disable(true);
													data.playState = false;
													await loadMiniMp3();
													resetMidi(true);
													popup.selectSubjectShow = false;
												}}
											></NSelect>
										</>
									),
								}}
							</NPopover>

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								v-model:show={popup.moveKeyShow}
								trigger="click"
								contentStyle={{ width: "320px" }}
							>
								{{
									trigger: () => (
										<div class={styles.topBtn}>
											<div class={styles.btnImg}>
												<img class={styles.topBtnIcon} src={getImage("icon_15.png")} />
											</div>
											<div>移调</div>
										</div>
									),
									default: () => (
										<>
											<div class={styles.btnLineTitle}>移调方式</div>

											<NSpace>
												<NButton
													secondary
													type={data.moveKeyType === "inset" ? "primary" : "default"}
													onClick={() => (data.moveKeyType = "inset")}
												>
													<NIcon component={GripLinesVertical} />
													最靠近
												</NButton>
												<NButton
													secondary
													type={data.moveKeyType === "down" ? "primary" : "default"}
													onClick={() => (data.moveKeyType = "down")}
												>
													<NIcon component={LongArrowAltDown} />
													向下移调
												</NButton>
												<NButton
													secondary
													type={data.moveKeyType === "up" ? "primary" : "default"}
													onClick={() => (data.moveKeyType = "up")}
												>
													<NIcon component={LongArrowAltUp} />
													向上移调
												</NButton>
											</NSpace>

											<div class={styles.btnLineTitle}>目标音调</div>

											<NGrid cols={5} yGap={8}>
												{ABC_DATA.key
													.sort((a, b) => b.step - a.step)
													.map((item) => (
														<NGi>
															<div
																class={[
																	styles.btnItem,
																	abcData.abc.key === item.value && styles.btnItemActive,
																]}
																onClick={() => handleMoveKey(item)}
															>
																<div class={[styles.btnItemIcon]}>
																	<TheIcon iconClassName={item.icon} />
																</div>
																<div class={styles.btnItemName}>{item.name}</div>
															</div>
														</NGi>
													))}
											</NGrid>
										</>
									),
								}}
							</NPopover>

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								v-model:value={popup.speedShow}
								trigger="click"
								placement="bottom"
								displayDirective="show"
							>
								{{
									trigger: () => (
										<div class={[styles.topBtn]}>
											<div class={styles.btnImg}>
												<img class={styles.topBtnIcon} src={getImage("icon_16.png")} />
											</div>
											<div>速度调整</div>
										</div>
									),
									default: () => <TheSpeed onChange={(val) => handleChange(val)} />,
								}}
							</NPopover>

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								v-model:show={popup.staffShow}
								trigger="click"
								placement="bottom"
								contentStyle={{ width: "320px" }}
							>
								{{
									trigger: () => (
										<div class={[styles.topBtn]}>
											<div class={styles.btnImg}>
												<img class={styles.topBtnIcon} src={getImage("icon_17.png")} />
											</div>
											<div>谱面显示</div>
										</div>
									),
									default: () => (
										<>
											<div class={styles.btnLineTitle}>乐谱大小</div>

											<NSpace>
												<NButton
													type={abcData.abcOptions.staffwidth === 1200 ? "primary" : "default"}
													secondary
													onClick={() => {
														abcData.abcOptions.staffwidth = 1200;
														handleResetRender();
													}}
												>
													小
												</NButton>
												<NButton
													type={abcData.abcOptions.staffwidth === 800 ? "primary" : "default"}
													secondary
													onClick={() => {
														abcData.abcOptions.staffwidth = 800;
														handleResetRender();
													}}
												>
													中
												</NButton>
												<NButton
													type={abcData.abcOptions.staffwidth === 400 ? "primary" : "default"}
													secondary
													onClick={() => {
														abcData.abcOptions.staffwidth = 400;
														handleResetRender();
													}}
												>
													大
												</NButton>
											</NSpace>

											<div class={styles.btnLineTitle}>小节数</div>

											<NSpace vertical>
												<NInputNumber
													min={1}
													v-model:value={(abcData.abcOptions.wrap as any).preferredMeasuresPerLine}
													placeholder="请输入小节数"
													onUpdate:value={() => {
														handleResetRender();
													}}
												/>

												{/* <NSpace style={{ marginTop: "20px" }} align="center" wrap={false} wrapItem={false}>
												<NButton style={{ width: "48%" }} round onClick={() => (popup.staffShow = false)}>
													取消
												</NButton>
												<NButton
													style={{ width: "48%" }}
													round
													type="primary"
												>
													确定
												</NButton>
											</NSpace> */}
											</NSpace>
										</>
									),
								}}
							</NPopover>

							<NPopover
								class={styles.popupWrap}
								showArrow={false}
								v-model:show={popup.barShow}
								trigger="click"
								placement="bottom"
								contentStyle={{ width: "320px" }}
							>
								{{
									trigger: () => (
										<div class={[styles.topBtn]}>
											<div class={styles.btnImg}>
												<img class={styles.topBtnIcon} src={getImage("icon_18.png")} />
											</div>
											<div>添加小节</div>
										</div>
									),
									default: () => (
										<>
											<div class={styles.btnLineTitle}>添加方式</div>

											<NSpace>
												<NButton
													type={data.addMearseType === "pre" ? "primary" : "default"}
													secondary
													onClick={() => (data.addMearseType = "pre")}
												>
													当前小节前
												</NButton>
												<NButton
													type={data.addMearseType === "next" ? "primary" : "default"}
													secondary
													onClick={() => (data.addMearseType = "next")}
												>
													当前小节后
												</NButton>
												<NButton
													type={data.addMearseType === "finish" ? "primary" : "default"}
													secondary
													onClick={() => (data.addMearseType = "finish")}
												>
													曲谱末尾
												</NButton>
											</NSpace>

											<div class={styles.btnLineTitle}>小节数</div>

											<NSpace vertical>
												<NInputNumber
													min={1}
													v-model:value={data.addMearseNumber}
													placeholder="请输入小节数"
												/>

												<NSpace
													style={{ marginTop: "20px" }}
													align="center"
													wrap={false}
													wrapItem={false}
												>
													<NButton
														style={{ width: "48%" }}
														round
														onClick={() => (popup.barShow = false)}
													>
														取消
													</NButton>
													<NButton
														style={{ width: "48%" }}
														round
														type="primary"
														onClick={() => handleAddMearse()}
													>
														确定
													</NButton>
												</NSpace>
											</NSpace>
										</>
									),
								}}
							</NPopover>

							<NPopover
								class={styles.popupWrap}
								v-model:show={popup.mearseDeleteShow}
								trigger="click"
								placement="bottom"
							>
								{{
									trigger: () => (
										<div class={[styles.topBtn]}>
											<div class={styles.btnImg}>
												<img class={styles.topBtnIcon} src={getImage("icon_19.png")} />
											</div>
											<div>删除小节</div>
										</div>
									),
									default: () => (
										<>
											<div class={styles.btnLineTitle}>删除方式</div>

											<NSpace vertical>
												<NSpace>
													<NButton
														type={data.deleteMearseType === "ing" ? "primary" : "default"}
														secondary
														onClick={() => (data.deleteMearseType = "ing")}
													>
														当前选中小节
													</NButton>
													<NButton
														type={data.deleteMearseType === "finish" ? "primary" : "default"}
														secondary
														onClick={() => (data.deleteMearseType = "finish")}
													>
														末尾空白小节
													</NButton>
												</NSpace>

												<NSpace
													style={{ marginTop: "20px" }}
													align="center"
													wrap={false}
													wrapItem={false}
												>
													<NButton
														style={{ width: "48%" }}
														round
														onClick={() => (popup.mearseDeleteShow = false)}
													>
														取消
													</NButton>
													<NButton
														style={{ width: "48%" }}
														round
														type="primary"
														onClick={() => handleDeleteMearse()}
													>
														确定
													</NButton>
												</NSpace>
											</NSpace>
										</>
									),
								}}
							</NPopover>

							<div class={styles.topLine}></div>

							<div
								style={{ marginLeft: "auto" }}
								class={styles.topBtn}
								onClick={() => togglePlay("reset")}
							>
								<div class={styles.btnImg}>
									<img class={styles.topBtnIcon} src={getImage("icon_20.png")} />
								</div>
								<div>重播</div>
							</div>
							<div class={styles.topBtn}>
								<NSpin show={data.loadingAudioSrouce} size="small">
									<div
										class={styles.btnImg}
										onClick={() => togglePlay(data.playState ? "pause" : "play")}
									>
										<img
											style={{ display: data.playState ? "" : "none" }}
											class={styles.topBtnIcon}
											src={getImage("icon_21_1.png")}
										/>
										<img
											style={{ display: data.playState ? "none" : "" }}
											class={styles.topBtnIcon}
											src={getImage("icon_21.png")}
										/>
									</div>
								</NSpin>
								<div>{data.playState ? "暂停" : "播放"}</div>
							</div>
							<div
								id="selectMearesBtn"
								class={[styles.topBtn]}
								onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
							>
								<div class={[styles.btnImg, popup.selectMearesShow && styles.btnImgActive]}>
									<img class={styles.topBtnIcon} src={getImage("icon_22.png")} />
								</div>
								<div>选段</div>
							</div>

							<div
								class={[styles.topBtn]}
								onClick={() => {
									metronomeData.disable = !metronomeData.disable;
									metronomeData.metro?.initPlayer();
								}}
							>
								<div class={[styles.btnImg, !metronomeData.disable && styles.btnImgActive]}>
									<img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
								</div>
								<div>节拍器</div>
							</div>
							<div class={[styles.topBtn]} onClick={() => (popup.settingShow = true)}>
								<div class={styles.btnImg}>
									<img class={styles.topBtnIcon} src={getImage("icon_24.png")} />
								</div>
								<div>设置</div>
							</div>
						</div>
					</div>
					<div class={styles.content}>
						<div class={styles.slide}>
							<Collapse v-model={data.slide} elevation={false} divider={false}>
								<CollapseItem title="音符时值" name="note">
									<div class={styles.wrapBox}>
										{ABC_DATA.types.map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => handleChange({ type: "type", value: item.value })}
											>
												<div
													class={[styles.btnImg, data.noteType === item.value && styles.btnImgActive]}
												>
													<TheIcon iconClassName={item.icon} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
										<div
											class={styles.topBtn}
											onClick={() => handleChange({ type: "note", value: "z" })}
										>
											<div
												class={[
													styles.btnImg,
													noteComputed.value.content === "z" && styles.btnImgActive,
												]}
											>
												<img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} />
											</div>
											<div>休止符</div>
										</div>

										<div
											class={styles.topBtn}
											onClick={() => handleChange({ type: "segno", value: " " })}
										>
											<div
												class={[styles.btnImg, noteComputed.value.segno === " " && styles.btnImgActive]}
											>
												{/* <img style={{ width: "20px", height: "20px" }} src={getImage("icon_rest.png")} /> */}
											</div>
											<div>分割</div>
										</div>
									</div>
								</CollapseItem>

								<CollapseItem title="拍号" name="meter">
									<div class={styles.wrapBox}>
										{ABC_DATA.meter.map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => handleChange({ type: "meter", value: item.value })}
											>
												<div class={[styles.btnImg]}>
													<TheIcon iconClassName={item.icon} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
									</div>
								</CollapseItem>

								<CollapseItem title="力度记号" name="dynamics">
									<div class={styles.wrapBox}>
										{ABC_DATA.dynamics.slice(0, 8).map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => handleChange({ type: "dynamics", value: item.value })}
											>
												<div
													class={[
														styles.btnImg,
														noteComputed.value.dynamics === item.value && styles.btnImgActive,
													]}
												>
													<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
										<div
											class={styles.topBtn}
											onClick={() =>
												handleChange({ type: "dynamics", value: ABC_DATA.dynamics[8].value })
											}
										>
											<div
												class={[
													styles.btnImg,
													ABC_DATA.dynamics[8].value.includes(noteComputed.value.dynamics) &&
														styles.btnImgActive,
												]}
											>
												<TheIcon iconClassName={ABC_DATA.dynamics[8].icon} size={["2em", "2em"]} />
											</div>
											<div>{ABC_DATA.dynamics[8].name}</div>
										</div>
										<div
											class={styles.topBtn}
											onClick={() =>
												handleChange({ type: "dynamics", value: ABC_DATA.dynamics[9].value })
											}
										>
											<div
												class={[
													styles.btnImg,
													ABC_DATA.dynamics[9].value.includes(noteComputed.value.dynamics) &&
														styles.btnImgActive,
												]}
											>
												<TheIcon iconClassName={ABC_DATA.dynamics[9].icon} size={["2em", "2em"]} />
											</div>
											<div>{ABC_DATA.dynamics[9].name}</div>
										</div>
									</div>
								</CollapseItem>
								<CollapseItem title="反复与跳跃" name="repeat">
									<div class={styles.wrapBox}>
										{ABC_DATA.repeat.map((item) => (
											<div
												class={[styles.topBtn, styles.longTopBtn]}
												onClick={() => handleChange({ type: "repeat", value: item.value })}
											>
												<div
													class={[
														styles.btnImg,
														measureComputed.value.repeat === item.value && styles.btnImgActive,
													]}
												>
													<TheIcon iconClassName={item.icon} size={["5em", "1em"]} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
									</div>
								</CollapseItem>
								<CollapseItem title="小节线" name="line">
									<div class={styles.wrapBox}>
										{ABC_DATA.bar.map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => {
													data.morePlay = false;
													handleChange({ type: "barline", value: item.value });
												}}
											>
												<div
													class={[
														styles.btnImg,
														measureComputed.value.barline === item.value && styles.btnImgActive,
													]}
												>
													<TheIcon iconClassName={item.icon} size={["2em", "2em"]} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
									</div>
								</CollapseItem>

								<CollapseItem title="谱号" name="clef">
									<div class={styles.wrapBox}>
										{ABC_DATA.clef.map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => handleChange({ type: "clef", value: item.value })}
											>
												<div class={[styles.btnImg]}>
													<TheIcon iconClassName={item.icon} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
									</div>
								</CollapseItem>
								<CollapseItem title="调号" name="key">
									<div class={styles.wrapBox}>
										{ABC_DATA.key.map((item) => (
											<div
												class={styles.topBtn}
												onClick={() => handleChange({ type: "key", value: item.value })}
											>
												<div class={[styles.btnImg]}>
													<TheIcon iconClassName={item.icon} />
												</div>
												<div>{item.name}</div>
											</div>
										))}
									</div>
								</CollapseItem>
							</Collapse>
						</div>

						<div class={styles.box}>
							<div class={styles.titleBox}>
								<div class={styles.titleName} style={{ width: "50%", margin: "0 auto" }}>
									<NInput
										onKeyup={(e: Event) => e.stopPropagation()}
										v-model:value={data.musicName}
										placeholder="曲谱名称"
									/>
								</div>
								<div style={{ width: "30%", margin: "10px 0 0 auto" }}>
									<NInput
										onKeyup={(e: Event) => e.stopPropagation()}
										v-model:value={data.creator}
										placeholder="曲谱作者"
									/>
								</div>
							</div>
							<div id="paper"></div>
							{!data.loading && (
								<Keys
									show={data.active ? true : false}
									instrumentCode={abcData.abc.subjectCode}
									onClick={(val) => handleChange(val)}
								/>
							)}

							{/* <textarea ref={textAreaRef} class={styles.value} id="abc"></textarea> */}
							<div id="importRef"></div>
							<div id="audio" style={{ display: "none" }}></div>
							{data.loadingAudioSrouce && (
								<div class={styles.loading}>
									<NSpin></NSpin>
								</div>
							)}
						</div>
					</div>
					<div ref={downRef}></div>

					<TheSetting v-model:show={popup.settingShow} />

					{data.selectMeasures.state && (
						<UseDraggable
							initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
							class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
						>
							<div onKeyup={(e: Event) => e.stopPropagation()}>
								<NSpace justify="space-between">
									<div class={styles.btnLineTitle}>输入小节范围</div>
									<NButton
										circle
										quaternary
										size="small"
										onClick={() => (popup.selectMearesShow = false)}
									>
										<NIcon size={16} component={<Close />} />
									</NButton>
								</NSpace>
								<NSpace align="center" wrap={false} wrapItem={false}>
									<div class={styles.mearesInput}>
										<NInputNumber
											min={1}
											max={data.selectMeasures.max}
											bordered={false}
											placeholder="开始小节"
											showButton={false}
											onUpdate:value={(val) => handleSetSelectMeares(val, "start")}
										></NInputNumber>
										-
										<NInputNumber
											min={data.selectMeasures.start}
											max={data.selectMeasures.max}
											bordered={false}
											placeholder="结束小节"
											showButton={false}
											onUpdate:value={(val) => handleSetSelectMeares(val, "end")}
										></NInputNumber>
									</div>
									<div class={styles.topBtn}>
										<NSpin show={data.loadingAudioSrouce} size="small">
											<div
												class={styles.btnImg}
												onClick={() => togglePlay(data.playState ? "pause" : "play")}
											>
												<img
													style={{ display: data.playState ? "" : "none" }}
													class={styles.topBtnIcon}
													src={getImage("icon_21_1.png")}
												/>
												<img
													style={{ display: data.playState ? "none" : "" }}
													class={styles.topBtnIcon}
													src={getImage("icon_21.png")}
												/>
											</div>
										</NSpin>
									</div>
								</NSpace>
							</div>
						</UseDraggable>
					)}
				</div>
				<div class={styles.exportPng}>
					<div id="exportPng"></div>
				</div>
			</>
		);
	},
});
