import { defineComponent, reactive, onMounted, computed } from "vue";
import tockAndTick from "/src/constant/tockAndTick.json";
import { Howl } from "howler";
import { Popup } from "vant";
import styles from "./index.module.less";
import state from "/src/state";
import { browser } from "/src/utils/index";
import tickWav from "/src/assets/tick.mp3";
import tockWav from "/src/assets/tock.mp3";

const tickData = reactive({
	len: 0,
	denominator: undefined as undefined | number,
	reduceLen: 0,
	tickEnd: false,
	/** 节拍器时间 */
	beatLengthInMilliseconds: 0,
	index: 0,
	show: false
});

// 是否使用系统节拍器
const isUseSystemBeat = computed(()=>{
	return (state.playType === "play"&& !state.isOpenMetronome)||(state.playType === "sing" && !state.isSingOpenMetronome)
})
// 使用哪个节拍器个数
const useLen = computed(()=>{
	return isUseSystemBeat.value ? tickData.reduceLen : tickData.len
})

let _time: NodeJS.Timeout 
// 关闭节拍器
export function closeTick(){
	if (tickData.show) {
		_time && clearTimeout(_time)
		tickData.tickEnd = true
		tickData.show = false
	}
}
const tickPlayCb = (i: any, resolve: any, source: any) => {
	if (tickData.tickEnd) {
		resolve(i)
		return
	};
	// 第一个点，延迟100ms再消失
	if (i === 0) {
		setTimeout(() => {
			tickData.index++;
		}, 100);
	} else {
		tickData.index++;
	}
	// 当系统节拍器才播放声音，跟练模式需要播放系统节拍器的声音，评测模式，如果没有伴奏，也需要播放系统节拍器的声音
	if (source && (isUseSystemBeat.value || state.modeType === 'follow' || (state.modeType === 'evaluating' && !state.accompany)) ) {
		const beatVolume = state.setting.beatVolume / 100
		source.volume = beatVolume;
		if (source.volume <= 0) {
			source.muted = true
		} else {
			source.muted = false
		}
		source.play();
	}
	resolve(i);
}
const handlePlay = (i: number, source: any | null) => {
	return new Promise((resolve) => {
		if (i === 0 ) {
			tickPlayCb(i, resolve, source);
		} else {
			_time=setTimeout(() => {
				tickPlayCb(i, resolve, source);
			}, tickData.beatLengthInMilliseconds);
		}
	});
};

// HTMLAudioElement 音频
const audioData = reactive({
	tick: null as unknown as HTMLAudioElement,
	tock: null as unknown as HTMLAudioElement,
});

const createAudio = (src: string): Promise<HTMLAudioElement | null> => {
	return new Promise((resolve) => {
		const a = new Audio(src);
		a.load();
		a.onloadedmetadata = () => {
			resolve(a);
		};
		a.onerror = () => {
			resolve(null);
		};
	});
};

/** 设置节拍器
 * @param beat 节拍数
 * @param denominator 节拍器分母
 */
export const handleInitTick = (beat: number, denominator?: number) => {
	tickData.len = beat;
	tickData.denominator = denominator;
	// 节拍器的个数除以2 直到小于等于4为止 
	while (beat > 4 && beat % 2 === 0) {
        beat = beat / 2;
    }
	tickData.reduceLen = beat
};

/** 开始节拍器 */
// 评测和练习模式，根据是否播放系统节拍器和mp3节拍器来控制是否发声，跟练模式百分之播
export const handleStartTick = async () => {
	tickData.show = true;
	tickData.tickEnd = false;
	tickData.index = 0;
	tickData.beatLengthInMilliseconds = tickData.denominator ? 4 / tickData.denominator * (60 / state.speed) * 1000 : (60 / state.speed) * 1000;
	for(let i = 0; i <= useLen.value; i++){
		// 提前结束, 直接放回false
		if (tickData.tickEnd) return false;
		// Audio 标签播放音频
		const source = i === 0 ? audioData.tick : i === useLen.value ? null : audioData.tock;
		await handlePlay(i, source)
	}
	tickData.show = false;
	return true
};

export default defineComponent({
	name: "metronome",
	setup() {
		const posObj = {
			top: "0px",
			left: "0px"
		}
		function initPos() {
			const musicAndSelectionDom = document.querySelector("#musicAndSelection")
			const osmdSvgPage1Dom = musicAndSelectionDom?.querySelector("#osmdSvgPage1")
			const stafflineDom = osmdSvgPage1Dom?.querySelector(".staffline")
			const musicAndSelectionDomPos = musicAndSelectionDom?.getBoundingClientRect()
			const osmdSvgPage1DomPos = osmdSvgPage1Dom?.getBoundingClientRect()
			const stafflineDomPos = stafflineDom?.getBoundingClientRect()
			Object.assign(posObj,{
				top: (osmdSvgPage1DomPos?.top||0)-(musicAndSelectionDomPos?.top||0)-18+"px",
				left: (stafflineDomPos?.left||0)-(osmdSvgPage1DomPos?.left||0)+"px"
			})
		}
		onMounted(() => {
			initPos()
			Promise.all([createAudio(tickWav), createAudio(tockWav)]).then(
				([tick, tock]) => {
					if (tick) {
						audioData.tick = tick;
					}
					if (tock) {
						audioData.tock = tock;
					}
				}
			);
		});		
		return () => (
				tickData.show && 
				<div class={styles.dots} style={posObj}>
					{
						Array.from({ length: useLen.value }).map((item,index)=>{
							return <div class={[styles.dot,((useLen.value - tickData.index)<=index)&&styles.hide]}></div>
						})
					}
				</div>
		);
	},
});
