<template>
	<view>
		<view ref="uni-rate" class="uni-rate">
			<view class="uni-rate__icon" :class="{'uni-cursor-not-allowed': disabled}"
				:style="{ 'margin-right': marginNumber + 'px' }" v-for="(star, index) in stars" :key="index"
				@touchstart.stop="touchstart" @touchmove.stop="touchmove" @mousedown.stop="mousedown"
				@mousemove.stop="mousemove" @mouseleave="mouseleave">
				<uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" />
				<!-- #ifdef APP-NVUE -->
				<view :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" class="uni-rate__icon-on">
					<uni-icons style="text-align: left;" :color="disabled?'#ccc':activeColor" :size="size"
						type="star-filled" />
				</view>
				<!-- #endif -->
				<!-- #ifndef APP-NVUE -->
				<view :style="{ width: star.activeWitch}" class="uni-rate__icon-on">
					<uni-icons :color="disabled?disabledColor:activeColor" :size="size" type="star-filled" />
				</view>
				<!-- #endif -->
			</view>
		</view>
	</view>
</template>

<script>
	// #ifdef APP-NVUE
	const dom = uni.requireNativePlugin('dom');
	// #endif
	/**
	 * Rate 评分
	 * @description 评分组件
	 * @tutorial https://ext.dcloud.net.cn/plugin?id=33
	 * @property {Boolean} 	isFill = [true|false] 		星星的类型，是否为实心类型, 默认为实心
	 * @property {String} 	color 						未选中状态的星星颜色，默认为 "#ececec"
	 * @property {String} 	activeColor 				选中状态的星星颜色，默认为 "#ffca3e"
	 * @property {String} 	disabledColor 				禁用状态的星星颜色，默认为 "#c0c0c0"
	 * @property {Number} 	size 						星星的大小
	 * @property {Number} 	value/v-model 				当前评分
	 * @property {Number} 	max 						最大评分评分数量，目前一分一颗星
	 * @property {Number} 	margin 						星星的间距，单位 px
	 * @property {Boolean} 	disabled = [true|false] 	是否为禁用状态，默认为 false
	 * @property {Boolean} 	readonly = [true|false] 	是否为只读状态，默认为 false
	 * @property {Boolean} 	allowHalf = [true|false] 	是否实现半星，默认为 false
	 * @property {Boolean} 	touchable = [true|false] 	是否支持滑动手势，默认为 true
	 * @event {Function} change 						uniRate 的 value 改变时触发事件，e={value:Number}
	 */

	export default {
		name: "UniRate",
		props: {
			isFill: {
				// 星星的类型，是否镂空
				type: [Boolean, String],
				default: true
			},
			color: {
				// 星星未选中的颜色
				type: String,
				default: "#ececec"
			},
			activeColor: {
				// 星星选中状态颜色
				type: String,
				default: "#ffca3e"
			},
			disabledColor: {
				// 星星禁用状态颜色
				type: String,
				default: "#c0c0c0"
			},
			size: {
				// 星星的大小
				type: [Number, String],
				default: 24
			},
			value: {
				// 当前评分
				type: [Number, String],
				default: 0
			},
			modelValue: {
				// 当前评分
				type: [Number, String],
				default: 0
			},
			max: {
				// 最大评分
				type: [Number, String],
				default: 5
			},
			margin: {
				// 星星的间距
				type: [Number, String],
				default: 0
			},
			disabled: {
				// 是否可点击
				type: [Boolean, String],
				default: false
			},
			readonly: {
				// 是否只读
				type: [Boolean, String],
				default: false
			},
			allowHalf: {
				// 是否显示半星
				type: [Boolean, String],
				default: false
			},
			touchable: {
				// 是否支持滑动手势
				type: [Boolean, String],
				default: true
			}
		},
		data() {
			return {
				valueSync: "",
				userMouseFristMove: true,
				userRated: false,
				userLastRate: 1
			};
		},
		watch: {
			value(newVal) {
				this.valueSync = Number(newVal);
			},
			modelValue(newVal) {
				this.valueSync = Number(newVal);
			},
		},
		computed: {
			stars() {
				const value = this.valueSync ? this.valueSync : 0;
				const starList = [];
				const floorValue = Math.floor(value);
				const ceilValue = Math.ceil(value);
				for (let i = 0; i < this.max; i++) {
					if (floorValue > i) {
						starList.push({
							activeWitch: "100%"
						});
					} else if (ceilValue - 1 === i) {
						starList.push({
							activeWitch: (value - floorValue) * 100 + "%"
						});
					} else {
						starList.push({
							activeWitch: "0"
						});
					}
				}
				return starList;
			},

			marginNumber() {
				return Number(this.margin)
			}
		},
		created() {
			this.valueSync = Number(this.value || this.modelValue);
			this._rateBoxLeft = 0
			this._oldValue = null
		},
		mounted() {
			setTimeout(() => {
				this._getSize()
			}, 100)
			// #ifdef H5
			this.PC = this.IsPC()
			// #endif
		},
		methods: {
			touchstart(e) {
				// #ifdef H5
				if (this.IsPC()) return
				// #endif
				if (this.readonly || this.disabled) return
				const {
					clientX,
					screenX
				} = e.changedTouches[0]
				// TODO 做一下兼容，只有 Nvue 下才有 screenX，其他平台式 clientX
				this._getRateCount(clientX || screenX)
			},
			touchmove(e) {
				// #ifdef H5
				if (this.IsPC()) return
				// #endif
				if (this.readonly || this.disabled || !this.touchable) return
				const {
					clientX,
					screenX
				} = e.changedTouches[0]
				this._getRateCount(clientX || screenX)
			},

			/**
			 * 兼容 PC @tian
			 */

			mousedown(e) {
				// #ifdef H5
				if (!this.IsPC()) return
				if (this.readonly || this.disabled) return
				const {
					clientX,
				} = e
				this.userLastRate = this.valueSync
				this._getRateCount(clientX)
				this.userRated = true
				// #endif
			},
			mousemove(e) {
				// #ifdef H5
				if (!this.IsPC()) return
				if (this.userRated) return
				if (this.userMouseFristMove) {
					console.log('---mousemove----', this.valueSync);
					this.userLastRate = this.valueSync
					this.userMouseFristMove = false
				}
				if (this.readonly || this.disabled || !this.touchable) return
				const {
					clientX,
				} = e
				this._getRateCount(clientX)
				// #endif
			},
			mouseleave(e) {
				// #ifdef H5
				if (!this.IsPC()) return
				if (this.readonly || this.disabled || !this.touchable) return
				if (this.userRated) {
					this.userRated = false
					return
				}
				this.valueSync = this.userLastRate
				// #endif
			},
			// #ifdef H5
			IsPC() {
				var userAgentInfo = navigator.userAgent;
				var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
				var flag = true;
				for (let v = 0; v < Agents.length - 1; v++) {
					if (userAgentInfo.indexOf(Agents[v]) > 0) {
						flag = false;
						break;
					}
				}
				return flag;
			},
			// #endif

			/**
			 * 获取星星个数
			 */
			_getRateCount(clientX) {
				this._getSize()
				const size = Number(this.size)
				if (isNaN(size)) {
					return new Error('size 属性只能设置为数字')
				}
				const rateMoveRange = clientX - this._rateBoxLeft
				let index = parseInt(rateMoveRange / (size + this.marginNumber))
				index = index < 0 ? 0 : index;
				index = index > this.max ? this.max : index;
				const range = parseInt(rateMoveRange - (size + this.marginNumber) * index);
				let value = 0;
				if (this._oldValue === index && !this.PC) return;
				this._oldValue = index;
				if (this.allowHalf) {
					if (range > (size / 2)) {
						value = index + 1
					} else {
						value = index + 0.5
					}
				} else {
					value = index + 1
				}

				value = Math.max(0.5, Math.min(value, this.max))
				this.valueSync = value
				this._onChange()
			},

			/**
			 * 触发动态修改
			 */
			_onChange() {

				this.$emit("input", this.valueSync);
				this.$emit("update:modelValue", this.valueSync);
				this.$emit("change", {
					value: this.valueSync
				});
			},
			/**
			 * 获取星星距离屏幕左侧距离
			 */
			_getSize() {
				// #ifndef APP-NVUE
				uni.createSelectorQuery()
					.in(this)
					.select('.uni-rate')
					.boundingClientRect()
					.exec(ret => {
						if (ret) {
							this._rateBoxLeft = ret[0].left
						}
					})
				// #endif
				// #ifdef APP-NVUE
				dom.getComponentRect(this.$refs['uni-rate'], (ret) => {
					const size = ret.size
					if (size) {
						this._rateBoxLeft = size.left
					}
				})
				// #endif
			}
		}
	};
</script>

<style lang="scss">
	.uni-rate {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		line-height: 1;
		font-size: 0;
		flex-direction: row;
		/* #ifdef H5 */
		cursor: pointer;
		/* #endif */
		flex-wrap: wrap;
	}

	.uni-rate__icon {
		position: relative;
		line-height: 1;
		font-size: 0;
	}

	.uni-rate__icon-on {
		overflow: hidden;
		position: absolute;
		top: 0;
		left: 0;
		line-height: 1;
		text-align: left;
	}

	.uni-cursor-not-allowed {
		/* #ifdef H5 */
		cursor: not-allowed !important;
		/* #endif */
	}
</style>
