<template>
	<view>
		<slot />
	</view>
</template>

<script lang="uts">
import { UIView } from 'UIKit'
import { 
	initCameraView, setCameraCallback,
	applyPermission, openAppSettings, vibrate, shutterSound, recordSound, keyDestroyer, keyListener,
	open, reopen, close, destroyCamera, takePhoto, takePhotoSnapshot, takeVideo, takeVideoSnapshot, stopVideo,
	setZoom, setExposure, setWhiteBalance, setHdr, setFacing, setGrid, setFlash, setAudio, setSuffix, setGallery, setOrientation, setMode, setSizeSelectors,
	setPreviewCorner, setPreviewRotation, isTakingVideo, isTakingPicture, isOpened
} from './index.uts'

export default {
	name: "ima-camera-view",
	props: {
		mode: {
			type: String,
			default: 'picture' // video
		},
		widthRatio: {
			type: Number,
			default: 0
		},
		heightRatio: {
			type: Number,
			default: 0
		},
		tolerance: {
			type: Number,
			default: 0.1
		},
		previewCornerRadius: {
			type: Number,
			default: 0
		},
		previewCornerRadiusRate: {
			type: Number,
			default: 0
		},
		previewRotation: {
			type: Number,
			default: 0
		},
		whiteBalance: {
			type: String,
			default: 'auto'
		},
		hdr: {
			type: String,
			default: 'off'
		},
		facing: {
			type: String,
			default: 'back'
		},
		flash: {
			type: String,
			default: 'off'
		},
		audio: {
			type: String,
			default: 'on'
		},
		photoSuffix: {
			type: String,
			default: 'jpeg'
		},
		orientation: {
			type: String,
			default: 'auto'
		},
		grid: {
			type: String,
			default: 'off'
		},
		gridColor: {
			type: String,
			default: '#808080'
		},
		shutter: {
			type: Boolean,
			default: true
		},
		sound: {
			type: String,
			default: ''
		},
		recorder: {
			type: Boolean,
			default: true
		},
		sound2: {
			type: String,
			default: ''
		},
		vibrate: {
			type: Boolean,
			default: false
		},
		duration: {
			type: Number,
			default: 300
		},
		gallery: {
			type: Boolean,
			default: false
		},
		shortcut: {
			type: Boolean,
			default: false
		},
	},
	data() {
		return {
			cameraZoom: 0,
			cameraFacing: 'back',
			cameraViewActivity: null,
			cameraViewContext: null,
		}
	},
	watch: {
		mode: {
			handler(newValue : String) {
				this.changeMode(newValue)
			},
			immediate: false
		},
		whiteBalance: {
			handler(newValue : String) {
				this.changeWhiteBalance(newValue)
			},
			immediate: true
		},
		hdr: {
			handler(newValue : String) {
				this.changeHdr(newValue)
			},
			immediate: true
		},
		facing: {
			handler(newValue : String) {
				this.cameraFacing = newValue;
				this.changeFacing(newValue)
			},
			immediate: false
		},
		flash: {
			handler(newValue : String) {
				this.changeFlash(newValue)
			},
			immediate: false
		},
		audio: {
			handler(newValue : String) {
				this.changeAudio(newValue)
			},
			immediate: false
		},
		photoSuffix: {
			handler(newValue : String) {
				this.changeSuffix(newValue)
			},
			immediate: true
		},
		orientation: {
			handler(newValue : String) {
				this.changeOrientation(newValue)
			},
			immediate: true
		},
		grid: {
			handler(newValue : String) {
				this.changeGrid(newValue, '#808080')
			},
			immediate: true
		},
		gallery: {
			handler(newValue : Boolean) {
				this.changeGallery(newValue)
			},
			immediate: true
		},
		previewCornerRadius: {
			handler(newValue ?: Number) {
				this.changePreviewCorner(newValue, this.previewCornerRadiusRate)
			},
			immediate: true
		},
		previewCornerRadiusRate: {
			handler(newValue ?: Number) {
				this.changePreviewCorner(this.previewCornerRadius, newValue)
			},
			immediate: true
		},
		previewRotation: {
			handler(newValue : Number) {
				this.changePreviewRotation(newValue)
			},
			immediate: true
		},
		shortcut: {
			handler(newValue : Boolean) {
				this.shortcutListener()
			},
			immediate: true
		}
	},
	/**
	 * 组件涉及的事件声明，只有声明过的事件，才能被正常发送
	 */
	emits: [
		'onCameraOpened',
		'onCameraClosed',
		'onPictureTaken',
		'onVideoTakenStart',
		'onVideoTakenProgress',
		'onVideoTakenEnd',
		'onFocusStart',
		'onFocusEnd',
		'onZoomChanged',
		'onCameraChange',
		'onOrientationChange',
		'onCameraTakenError',
		'onCameraError',
	],
	/**
	 * 规则：如果没有配置expose，则methods中的方法均对外暴露，如果配置了expose，则以expose的配置为准向外暴露
	 * ['publicMethod'] 含义为：只有 `publicMethod` 在实例上可用
	 */
	expose: [
		'open',
		'reopen',
		'close',
		'destroyCamera',
		'takePhoto',
		'takePhotoSnapshot',
		'takeVideo',
		'takeVideoSnapshot',
		'stopVideo',
		'changeZoom',
		'changeExposure',
		'changeWhiteBalance',
		'changeHdr',
		'changeFacing',
		'changeMode',
		'changeOrientation',
		'changeGrid',
		'changeFlash',
		'changeAudio',
		'changeSuffix',
		'changeSizeSelectors',
		'changePreviewCorner',
		'changePreviewRotation',
		'changeGallery',
		'openAppSettings'
	],
	methods: {
		// 加载相机视图界面
		initCameraView(): UIView{
			return initCameraView((success : boolean, message : string) => {
				if (success) {
					this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
					this.initCamera();
				} else {
					console.warn(`相机初始失败或者当前设备不支持:${message}`);
				}
			})
		},
		// 加载相机权限
		initCameraPermission() {
			applyPermission((allRight : boolean, grantedList : Array<String>) => {
				// 用户同意了全部权限
				if (allRight) {
					this.initCameraView();
					console.log(`用户同意了全部权限: ${grantedList}`);
				}
				// 用户只同意了 grantedList 中的部分权限，或者有权限被拒绝
				else {
					console.warn(`部分权限被拒绝: ${grantedList}`);
				}
			}, (doNotAskAgain : boolean, grantedList : Array<String>) => {
				// 用户拒绝某些权限并勾选“不再询问”
				if (doNotAskAgain) {
					// 跳转到当前 App 的系统设置页
					openAppSettings()
					console.warn(`权限被永久拒绝: ${grantedList}`);
				}
			})
		},
		openAppSettings(){
			openAppSettings()
		},
		// 加载相机
		initCamera() {
			setCameraCallback((event : string, data : any) => {
				switch (event) {
					case "opened":
						console.log("相机已打开", data)
						this.$emit('onCameraOpened', data)
						break
					case "closed":
						console.log("相机已关闭", data)
						this.$emit('onCameraClosed', data)
						break
					case "picture":
						console.log("照片", data)
						this.$emit('onPictureTaken', data)
						break
					case "video-start":
						console.log("开始录制", data)
						this.$emit('onVideoTakenStart', data)
						break
					case "video-progress":
						console.log("录制中", data)
						this.$emit('onVideoTakenProgress', data)
						break
					case "video-end":
						console.log("结束视频", data)
						// this.$emit('onVideoTakenEnd', data)
						break
					case "video":
						console.log("视频资源", data)
						this.$emit('onVideoTakenEnd', data)
						this.$emit('onVideoTaken', data)
						break
					case "focus-start":
						console.log("对焦开始", data)
						this.$emit('onFocusStart', data)
						break
					case "focus-end":
						console.log("对焦结束", data)
						this.$emit('onFocusEnd', data)
						break
					case "zoom-change":
						console.log("缩放级别", data)
						this.$emit('onZoomChanged', data)
						break
					case "camera-change":
						console.log("相机设置", data)
						this.$emit('onCameraChange', data)
						break
					case "orientation-change":
						console.log("相机角度转换", data)
						this.$emit('onOrientationChange', data)
						break
					case "take-error":
						console.log("相机拍摄监测", data)
						this.$emit('onCameraTakenError', data)
						break
					case "error":
						console.log("相机出错", data)
						this.$emit('onCameraError', data)
						break
				}
			})
			open();
		},
		// 打开摄像头预览
		open() {
			open();
		},
		// 重新打开摄像头预览（用于自己手动重启）
		reopen() {
			reopen();
		},
		// 关闭摄像头预览
		close() {
			close();
		},
		// 销毁相机
		destroyCamera() {
			if (this.$el != null) {
				destroyCamera()
			}
		},
		// 拍照（标准拍照流程）
		takePhoto() {
			this.photoSound()
			takePhoto()
		},
		// 快照拍照（适用于快速拍照场景）
		takePhotoSnapshot() {
			this.photoSound()
			takePhotoSnapshot()
		},
		// 开始录制视频，默认保存在缓存目录中，文件名为当前时间戳
		takeVideo(duration ?: Number) {
			const videoDuration : Number = duration ?? 0;
			this.videoSound(true)
			takeVideo(videoDuration)
		},
		// 快照方式录制视频
		takeVideoSnapshot(duration ?: Number) {
			const videoDuration : Number = duration ?? 0;
			this.videoSound(true)
			takeVideoSnapshot(videoDuration)
		},
		// 停止视频录制
		stopVideo() {
			stopVideo();
		},
		// 设置摄像头缩放级别，参数 zoom 是缩放倍数（浮点数）
		changeZoom(zoom : Number) {
			setZoom(zoom)
		},
		// 设置曝光模式，参数 exposure 是曝光数值（浮点数）
		changeExposure(exposure : Number) {
			setExposure(exposure)
		},
		// 设置相机白平衡
		changeWhiteBalance(whiteBalance : String) {
			setWhiteBalance(whiteBalance)
		},
		// 设置相机HDR
		changeHdr(hdr : String) {
			setHdr(hdr)
		},
		// 设置摄像头方向
		changeFacing(facing : String) {
			setFacing(facing)
		},
		// 设置相机模式
		changeMode(mode : String) {
			setMode(mode)
		},
		// 设置相机网格及颜色
		changeGrid(grid : String, color : String) {
			setGrid(grid, color)
		},
		// 设置闪光灯模式
		changeFlash(flash : String) {
			setFlash(flash)
		},
		// 设置音频
		changeAudio(audio : String) {
			setAudio(audio)
		},
		// 设置照片输出格式
		changeSuffix(suffix : String) {
			setSuffix(suffix)
		},
		// 是否将拍摄文件保存到本地可见媒体（即相册）
		changeGallery(gallery : Boolean) {
			setGallery(gallery)
		},
		// 设置相机使用设备方向
		changeOrientation(orientation : String) {
			setOrientation(orientation)
		},
		// 手动设置特定比例的方法
		changeSizeSelectors(width : Number, height : Number, tolerance : Number) {
			setSizeSelectors(width, height, tolerance)
		},
		// 设置预览圆角。radiusRate=0.5 且组件宽高相等时为圆形预览
		changePreviewCorner(radius ?: Number, radiusRate ?: Number) {
			let safeRadius : Number = 0
			let safeRadiusRate : Number = 0
			if (radius != null) {
				safeRadius = radius
			}
			if (radiusRate != null) {
				safeRadiusRate = radiusRate
			}
			setPreviewCorner(safeRadius, safeRadiusRate)
		},
		// 设置预览旋转角度（用于外接摄像头方向修正，支持 0/90/180/270）
		changePreviewRotation(degrees : Number) {
			setPreviewRotation(degrees)
		},
		// 判断是否正在录制视频
		isTakingVideo() : Boolean {
			return isTakingVideo();
		},
		// 判断是否正在拍照
		isTakingPicture() : Boolean {
			return isTakingPicture();
		},
		// 判断摄像头是否已打开
		isOpened() : Boolean {
			return isOpened();
		},
		// 拍照提示音
		photoSound() {
			if (this.vibrate) {
				vibrate(this.duration ?? 300)
			}
			if (this.shutter) {
				shutterSound(this.sound ?? '')
			}
		},
		// 录像提示音
		videoSound(isStart : Boolean) {
			if (this.recorder) {
				recordSound(isStart, this.sound2 ?? '')
			}
		},
		// 自拍杆、快捷键监听【只针对拍照，自己可自定义成录像】
		shortcutListener() {
			// 如果没有启用，则无效
			if (!this.shortcut) {
				return
			}
			// DONE:按行业标准
			// 基础版（action只有：down(按下)、up(松开)）
			const baseOptions = {
				type: "base",
				intercept: true
			}
			keyListener(baseOptions, (action : String, code : Number, name : String) => {
				console.log(`按键事件: ${action} -> ${name} (${code})`)
				// 拍照：快捷键（音量键上下）、蓝牙自拍杆（拍照按钮）
				if ((code == 24 || code == 25) && action == 'up') {
					this.takePhoto();
				}
				// 聚焦(加)：蓝牙自拍杆（加键）
				else if (code == 168 && action == 'down') {
					this.cameraZoom = this.cameraZoom + 0.01;
					if (this.cameraZoom >= 1) {
						this.cameraZoom = 1
					}
					console.log('聚焦(加)', this.cameraZoom)
					this.changeZoom(this.cameraZoom)
				}
				// 聚焦(减)：蓝牙自拍杆（减键）
				else if (code == 169 && action == 'down') {
					this.cameraZoom = this.cameraZoom - 0.01;
					if (this.cameraZoom <= 0) {
						this.cameraZoom = 0
					}
					console.log('聚焦(减)', this.cameraZoom)
					this.changeZoom(this.cameraZoom)
				}
				// 设置摄像头方向 ：蓝牙自拍杆（开机键）
				else if (code == 119 && action == 'up') {
					this.cameraFacing = this.cameraFacing == 'front' ? 'back' : 'front';
					console.log('设置摄像头方向', this.cameraFacing)
					this.changeFacing(this.cameraFacing)
				}
			});

			// TODO:这个需要自己实现
			// 高阶版（action有：down(按下)、up(松开)、single(单击)、double(双击)、long(长按)）
			// const fullOptions: ListenerOptions = {
			// 	type: "full",   			// 使用高阶版
			// 	intercept: true 			// 是否拦截系统事件
			// 	doubleInterval: 400,  // 双击间隔 ms（默认 400）
			// 	longPressTime; 500,   // 长按时间阈值 ms（默认 500）
			// }
			// keyListener(fullOptions, (action : string, code : number, name : string) => {
			// 	console.log(`按键事件: ${action} -> ${name} (${code})`)
			// });
		}
	},
	/**
	 * [可选实现] 组件被创建，组件第一个生命周期，
	 * 在内存中被占用的时候被调用，开发者可以在这里执行一些需要提前执行的初始化逻辑
	 */
	created() {
	  // 申请权限
	  this.initCameraPermission()
	}, 
	NVBeforeLoad() {
		
    },
	/**
	 * [必须实现] 创建原生View，必须定义返回值类型
	 * 开发者需要重点实现这个函数，声明原生组件被创建出来的过程，以及最终生成的原生组件类型
	 * （iOS 返回 UIView，与 Android FrameLayout 对应）
	 */
	NVLoad() : UIView {
		// 必须在 initCameraView 之前设置，确保原生层在预览层创建时已记录旋转角度，
		// 避免后续再设置时触发不必要的预览重建
		setPreviewRotation(this.previewRotation)
		return this.initCameraView()
	},
	/**
	 * [可选实现] 原生View已创建
	 */
	NVLoaded() {
		// iOS：前后台自动关开相机可接入 UIApplication 通知；与 Android UTSAndroid 生命周期对齐占位
	},
	/**
	 * [可选实现] 原生View布局完成
	 */
	NVLayouted() {
		this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
		// 布局完成后再次确保旋转已应用（此时 props 确定已生效）
		this.changePreviewRotation(this.previewRotation);
	},
	/**
	 * [可选实现] 原生View将释放
	 */
	NVBeforeUnload() {
	},
	/**
	 * [可选实现] 原生View已释放，这里可以做释放View之后的操作
	 */
	NVUnloaded() {
		// 如果组件绑定了视图则需要在组件销毁时释放视图相关资源
		if (this.$el != null) {
			destroyCamera();
		}
		// 销毁自拍杆、快捷键监听
		if (this.shortcut) {
			keyDestroyer();
		}
	},
	/**
	 * [可选实现] 组件销毁
	 */
	unmounted() {
		// 销毁相机视图
		if (this.$el != null) {
			destroyCamera();
		}
		// 销毁自拍杆、快捷键监听
		if (this.shortcut) {
			keyDestroyer();
		}
	},
	/**
	 * [可选实现] 自定组件布局尺寸，用于告诉排版系统，组件自身需要的宽高
	 * 一般情况下，组件的宽高应该是由终端系统的排版引擎决定，组件开发者不需要实现此函数
	 * 但是部分场景下，组件开发者需要自己维护宽高，则需要开发者重写此函数
	 */
	NVMeasure(size : UTSSize) : UTSSize {
		return size;
	}
}
</script>
