<template>
	<div class="hand-track">
		<video
			id="gum-local"
			class="video"
			width="100%"
			height="100%"
			autoplay
			playsinline
			webkit-playsinline
			x5-playsinline
			ref="myVideo"
		></video>

		<!-- 获取权限 -->
		<button
			id="showVideo"
			class="btn"
			ref="showVideoBtn"
			@click="initVideo($event)"
		>
			开启摄像头
		</button>

		<!-- info -->
		<div class="info-dialog">
			<div class="progress" ref="progress">
				当前进度: {{ progressText }}..
			</div>
			<div class="status">
				当前手势: <span>{{ handStatus }}</span>
			</div>
		</div>

		<!-- unsuit-dialog -->
		<div class="unsuit-dialog" ref="unsuit">请在浏览器中打开</div>
		<div class="unsuit-dialog" ref="unsuit2">您的系统版本过低</div>
	</div>
</template>
<script>
// @ is an alias to /src
import gsap from "gsap";
import {
	IEngine,
	Load,
	IHandTrackingApi,
	MediaStreamErrorEnum,
	EventEnum,
} from "@handtracking.io/yoha";

export default {
	name: "handTrack",
	components: {},
	data() {
		return {
			progressText: "0%", //进度文字
			BORDER_PADDING_FACTOR: 0.01, //边界阀值
			VIDEO_WIDTH_FACTOR: 0.8, //宽度阀值
			videoRealSize: {},
			handStatus: "未检测到手",
		};
	},
	mounted() {
		this.video = this.$refs.myVideo;

		this.checkUserDeviceSuitable();
	},
	methods: {
		// 检测用户手机是否兼容
		checkUserDeviceSuitable() {
			if (
				window.deviceInfo.system === "IOS" &&
				window.deviceInfo.app === "WX"
			) {
				let iosV = this.getIOSVersion();
				// console.log(
				// 	!this.versionStringCompare(iosV, "14.5"),
				// 	this.versionStringCompare(iosV, "11.0")
				// );
				if (
					!this.versionStringCompare(iosV, "14.5") &&
					this.versionStringCompare(iosV, "11.0")
				) {
					gsap.to(this.$refs.unsuit, { autoAlpha: 1 });
				} else if (!this.versionStringCompare(iosV, 11.0)) {
					gsap.to(this.$refs.unsuit2, { autoAlpha: 1 });
				}
			}
		},
		// 版本号比较
		versionStringCompare(preVersion = "", lastVersion = "") {
			var sources = preVersion.split(".");
			var dests = lastVersion.split(".");
			var maxL = Math.max(sources.length, dests.length);
			var result = 0;
			for (let i = 0; i < maxL; i++) {
				let preValue = sources.length > i ? sources[i] : 0;
				let preNum = isNaN(Number(preValue))
					? preValue.charCodeAt()
					: Number(preValue);
				let lastValue = dests.length > i ? dests[i] : 0;
				let lastNum = isNaN(Number(lastValue))
					? lastValue.charCodeAt()
					: Number(lastValue);
				if (preNum < lastNum) {
					result = false;
					break;
				} else if (preNum > lastNum) {
					result = true;
					break;
				}
			}
			return result;
		},
		// 获取设备权限
		getDevicePermission(constraints) {
			if (navigator.mediaDevices.getUserMedia === undefined) {
				navigator.mediaDevices.getUserMedia = function (constraints) {
					var getUserMedia =
						navigator.getUserMedia ||
						navigator.webkitGetUserMedia ||
						navigator.mozGetUserMedia ||
						navigator.msGetUserMedia ||
						navigator.oGetUserMedia;
					if (!getUserMedia) {
						return Promise.reject(
							new Error(
								"getUserMedia is not implemented in this browser"
							)
						);
					}
					return new Promise(function (resolve, reject) {
						getUserMedia.call(
							navigator,
							constraints,
							resolve,
							reject
						);
					});
				};
			}

			return global.navigator.mediaDevices
				.getUserMedia(constraints)
				.then((stream) => {
					if (stream) {
						stopStreamTracks(stream);
						return true;
					}
					return Promise.reject(new Error("EmptyStreamError"));
				})
				.catch((errMsg) => {
					if (errMsg && "NotAllowedError" === errMsg.name) {
						return false;
					}
					return Promise.reject(errMsg);
				});
		},
		// 开始获取权限
		requestPermission() {
			return this.getDevicePermission({ video: true, audio: true })
				.catch(() =>
					this.getDevicePermission({ video: false, audio: true })
				)
				.catch(() =>
					this.getDevicePermission({ video: true, audio: false })
				)
				.catch(() => true);
		},
		// 获取摄像头设备id
		async getBackCameraDeviceId() {
			let source = new Array();
			let devices = await navigator.mediaDevices.enumerateDevices();
			if (devices.length > 0) {
				devices.forEach(function (device) {
					if (device.kind == "videoinput") {
						source.push(device);
					}
				});
			}
			return source[source.length - 1].deviceId;
		},
		// 初始化 video
		async initVideo(e) {
			const constraints = {
				video: {
					deviceId: { exact: await this.getBackCameraDeviceId() },
					facingMode: "environment",
				},
				// video: {
				// 	facingMode: { exact: "environment" },
				// 	width: { min: 375, ideal: 375, max: 750 },
				// 	height: { min: 590, ideal: 590, max: 1624 },
				// },
			};

			this.video.play();

			try {
				this.stream = await navigator.mediaDevices.getUserMedia(
					constraints
				);

				gsap.to(e.target, { autoAlpha: 0 });
				e.target.disabled = true;

				// 处理成功回调
				this.handleSuccess(this.stream, constraints);
			} catch (e) {
				this.handleError(e);
			}
		},
		// 处理stream获取成功
		async handleSuccess(stream, constraints) {
			const videoTracks = stream.getVideoTracks();
			this.stream = stream; // make variable available to browser console
			let streamSize = this.GetStreamDimensions(stream);
			let handledSize = this.ScaleResolutionToWidth(
				streamSize,
				window.innerWidth
			);

			this.videoRealSize = handledSize;

			console.log("Got stream with constraints:", constraints);
			console.log(`Using video device: ${videoTracks[0].label}`);
			console.log("Got stream size:", streamSize);
			console.log(
				"Got streamHandled size:",
				window.innerWidth,
				handledSize
			);

			this.responseVideo(handledSize);
			this.video.srcObject = stream;

			// 开始识别
			this.startDraw();
		},
		// 处理stream获取错误
		handleError(error) {
			console.error(error);
		},
		// 视频设置
		responseVideo(size) {
			this.video.width = size.width;
			this.video.height = size.height;
		},
		// 获得stream大小
		GetStreamDimensions(stream) {
			return {
				width: stream.getVideoTracks()[0].getSettings().width,
				height: stream.getVideoTracks()[0].getSettings().height,
			};
		},
		// 等比缩放视频
		ScaleResolutionToWidth(resolution, width) {
			const cw = resolution.width;
			const ch = resolution.height;
			const tw = width;

			return {
				width: tw,
				height: ch / (cw / tw),
			};
		},
		// 创建手势识别引擎
		async CreateEngine() {
			let API = IHandTrackingApi;
			if (API) {
				return API.CreateEngine();
			}
			API = await Load("./yoha");
			return API.CreateEngine();
		},
		// 开始识别
		async startDraw() {
			this.engine = await this.CreateEngine();
			await this.engine.DownloadModel((progress) => {
				this.progressText = `${Math.round(progress * 100)}%`;
			});

			this.progressText = "引擎准备中…";
			this.engine.Configure({
				// Webcam video is usually flipped so we want the coordinates to be flipped as well.
				flipX: false,
				// Crop away a small area at the border to prevent the user to move out of view
				// when reaching for border areas on the canvas.
				padding: this.BORDER_PADDING_FACTOR,
			});

			this.engine.SetUpCustomTrackSource(this.video);
			// await this.engine.SetUpCameraTrackSource();

			// ios需要预热
			if (window.deviceInfo.system === "IOS") {
				await this.engine.Warmup();
			}

			this.progressText = "引擎已就绪";
			gsap.to(this.$refs.progress, {
				autoAlpha: 0,
				delay: 0.5,
				onComplete: () => {
					this.$refs.progress.style.display = "none";
				},
			});

			this.engine.Start((e) => {
				if (e.type === EventEnum.RESULT) {
					if (e.poses.fist) {
						this.handStatus = "握拳";
						// document.getElementById("status").innerText = "握拳";
					} else if (e.poses.pinch) {
						this.handStatus = "捏合";
						// document.getElementById("status").innerText = "捏合";
					} else {
						this.handStatus = "张开";
						// document.getElementById("status").innerText = "张开";
					}
				} else if (e.type === EventEnum.LOST) {
					this.handStatus = "未检测到手";
					// document.getElementById("status").innerText = "未检测到手";
				}
			});
		},
		// 获取ios版本号
		getIOSVersion() {
			let str = navigator.userAgent.toLowerCase();
			let ver = str.match(/cpu iphone os (.*?) like mac os/);
			if (!ver) {
				console.log("请在Ios系统中打开");
			} else {
				console.log(
					"你当前的Ios系统版本为：" + ver[1].replace(/_/g, ".")
				);
				return ver[1].replace(/_/g, ".");
			}
		},
	},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.hand-track {
	.prLayout(100%,100vh);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-content: center;
	align-items: center;
	.video {
		position: relative;
		z-index: 0;
		// transform: scaleX(-1);
		width: 750px;
		height: auto;
		// height: 700px;
	}
	.info-dialog {
		.paLayout(0,0, 50%,130px,2);
		background-color: rgba(255, 255, 255, 0.25);
		border-radius: 0 0 30px 0;
		padding: 20px;
		font-size: 28px;
		display: flex;
		flex-direction: column;
		align-content: center;
		align-items: center;
		text-align: left;
		text-indent: 10px;
		justify-content: center;
		div {
			width: 100%;
			line-height: 40px;
			color: #fff;
			span {
				font-weight: bold;
				color: #f00;
			}
		}
		.progress {
		}
	}
	.btn {
		.paCenterBottom(200px,280px,80px,2);
		background-color: #fff;
		line-height: 40px;
		text-align: center;
		border-radius: 8px;
		font-size: 25px;
	}
	.unsuit-dialog {
		visibility: hidden;
		.paCenterBottom(0, 100%,80px,10);
		position: fixed;
		background-color: rgba(255, 255, 255, 0.35);
		color: #fff;
		line-height: 80px;
		text-align: center;
		font-size: 40px;
	}
}
</style>