<template>
	<view class="l-slider" 
		:class="[
			'l-slider--' + (vertical ? 'vertical': 'horizontal'),{
			'l-slider--disabled': disabled}
		]" 
		:style="[styles]"
		ref="sliderRef" 
		@touchstart.stop="touchstart" 
		@touchmove.stop="touchmove"
		@touchend="touchend">
		<view ref="railRef" class="l-slider__rail" 
			:class="['l-slider__rail--' + (vertical ? 'vertical': 'horizontal')]" 
			:style="[railStyle]">
			<view class="l-slider__track" 
				:class="['l-slider__track--' + (vertical ? 'vertical': 'horizontal')]"  
				:style="[progressBarStyle]" ref="progressBarRef"></view>
		</view>
		<view class="l-slider__thumb-wrapper" 
			:class="['l-slider__thumb-wrapper--' + (vertical ? 'vertical': 'horizontal')]" 
			:style="[startThumbStyle]"
			ref="startThumbRef" 
			data-thumb="0">
			<slot name="start-thumb">
				<view class="l-slider__thumb" :style="[thumbStyle]"></view>
			</slot>
		</view>
		
		<view class="l-slider__thumb-wrapper" 
			:class="['l-slider__thumb-wrapper--' + (vertical ? 'vertical': 'horizontal')]" 
			:style="[endThumbStyle]"
			ref="endThumbRef" 
			data-thumb="1" 
			v-if="range">
			<slot name="end-thumb">
				<view class="l-slider__thumb" :style="[thumbStyle]"></view>
			</slot>
		</view>
	</view>
</template>
<script lang="ts">
	// @ts-nocheck
	import { computed, defineComponent, getCurrentInstance, nextTick, onMounted, onUnmounted ,ref, watch, reactive } from '@/uni_modules/lime-shared/vue';
	import sliderProps from './props'
	import { SliderValue } from './type';
	import { getRect } from '@/uni_modules/lime-shared/getRect';
	import { closest } from '@/uni_modules/lime-shared/closest';
	import { useTouch } from './touch'
	import { format } from './utils'
	
	export default defineComponent({
		name: 'l-slider', 
		props: sliderProps,
		emits: ['change', 'update:modelValue', 'change-position'],
		setup(props, {emit}) {
			const instance = getCurrentInstance().proxy!
			const sliderRect = reactive({
				width: 0,
				height: 0,
				'min-height': '',
				'min-width': '',
				left: 0,
				top: 0
			})
			const railRect = reactive({
				width: 0,
				height: 0,
				left: 0,
				top: 0
			})
			const progressBarRect = reactive({
				width: null,
				height: null,
				left: null,
				opacity: 1,
				top: null
			})
			const thumbRect = reactive({
				width: 0,
				height: 0,
				left: 0,
				top: 0
			})
			const startThumbStyle = reactive({
				left: null,
				top: null
			})
			const endThumbStyle = reactive({
				left: null,
				top: null
			})
			
			const tempValue = ref<number[]>([0, 0]);
			const innerValue = computed({
				set(value : SliderValue) {
					emit('change', value);
					emit('update:modelValue', value);
					// #ifdef VUE2
					emit('input', value);
					// #endif
				},
				get() : SliderValue {
					return props.value ?? props.modelValue ?? props.min
				}
			})
			const isRange = computed(() : boolean => props.range && Array.isArray(innerValue.value) && innerValue.value.length == 2);
			const direction = computed(() : Map<string, string> => {
				const map = new Map<string, string>();
				map.set('left', props.vertical ? 'top' : 'left');
				map.set('right', props.vertical ? 'bottom' : 'right');
				map.set('width', props.vertical ? 'height' : 'width');
				map.set('min-height', props.vertical ? 'min-width' : 'min-height');
				map.set('vertical', props.vertical ? 'vertical' : 'horizontal');
				// map.set('margin-top', props.vertical ? 'margin-left' : 'margin-top');
				// map.set('margin-bottom', props.vertical ? 'margin-right' : 'margin-bottom');
				return map
			})
			const getDirection = (key : string) : string => {
				return direction.value.get(key) ?? key
			}
			
			const styles = computed(()=>{
				const style:Record<string, any> = {};
				const minHeight = getDirection('min-height')
				style[minHeight] = sliderRect[minHeight]
				return style
			})
			const railStyle = computed(() => {
				const style:Record<string, any> = {};
				if (props.railColor) {
					style['background'] =  props.railColor!
				}
				if (props.railSize) {
					style[props.vertical ? 'width': 'height'] = props.railSize!
				}
				return style
			})
			
			const thumbStyle = computed(() => {
				const style:Record<string, any> = {};
				if (props.thumbColor) {
					style['background'] = props.thumbColor!
				}
				if(props.thumbBorderColor) {
					style['border-color'] =  props.thumbBorderColor!
				}
				if (props.thumbSize) {
					style['width'] =  props.thumbSize!
					style['height']=  props.thumbSize!
				}
				return style
			})
			
			const progressBarStyle = computed(() : Map<string, any> => {
				const style:Record<string, any> = {};
				if (props.trackColor) {
					style['background'] = props.trackColor!
				}
				const left = getDirection('left')
				const width = getDirection('width')
				style[left] = progressBarRect[left]
				style[width] = progressBarRect[width]
				style['opacity'] = progressBarRect.opacity
				
				return style
			})
			
			const scope = computed(() : number => props.max - props.min);
			
			// 计算选中条的长度占轨道总长度的比例
			const calculateSelectedBarRatio = () : number => {
				const { min } = props;
				if (isRange.value) {
					// 对于范围选择，计算两个值之间的差值相对于总范围的比值
					return Math.abs(tempValue.value[1] - tempValue.value[0]) / scope.value;
				}
				// 对于单个值选择，计算该值相对于总范围的比值
				return (tempValue.value[0] - min) / scope.value;
			};
			// 计算选中条起始位置的偏移量占轨道总长度的比例
			const calculateStartOffsetRatio = () : number => {
				const { min } = props;
				if (isRange.value) {
					// 对于范围选择，计算起始值相对于总范围的偏移量比值
					const _startValue = Math.min(tempValue.value[0], tempValue.value[1]);
					return (_startValue - min) / scope.value;
					
				}
				// 对于单个值选择，偏移量为0
				return 0;
			};
			// 计算轨道的最大尺寸（宽或高）
			const calculateMaxTrackDimension = () : number[] => {
				const railDimension = props.vertical ? railRect.width : railRect.height;
				const thumbDimension = props.vertical ? thumbRect.width : thumbRect.height;
				return [Math.max(railDimension, thumbRect.width, thumbRect.height), Math.max(railDimension, thumbDimension)];
			};
			// 计算轨道的总长度
			const calculateTrackLength = () : number => {
				return props.vertical ? railRect.height : railRect.width
			};
			// 更新进度条和滑块的位置
			const updateProgressBarAndThumb = () => {
				let trackLength = calculateTrackLength();
				const selectedBarRatio = calculateSelectedBarRatio();
				const startOffsetRatio = calculateStartOffsetRatio();
				const [start, end] = tempValue.value;
				const [maxDimension, thumbDimension] = calculateMaxTrackDimension();
				const thumbHalf = maxDimension / 2;
				const usableTrackLength = trackLength - maxDimension;
				
				progressBarRect[getDirection('left')] =  `${startOffsetRatio * usableTrackLength}px`
				progressBarRect[getDirection('width')] =  `${(selectedBarRatio * usableTrackLength + maxDimension)}px`
				progressBarRect[getDirection('opacity')] =  selectedBarRatio == 0 && start == props.min ? 0 : 1
				sliderRect[getDirection('min-height')] = `${thumbDimension}px`
				
				// 更新滑块位置
				const startThumbPosition = (start - props.min) / scope.value * usableTrackLength + thumbHalf;
				startThumbStyle[getDirection('left')] =  `${startThumbPosition}px`
				if (!isRange.value) return;
				const endThumbPosition = (end - props.min) / scope.value * usableTrackLength + thumbHalf;
				endThumbStyle[getDirection('left')] = `${endThumbPosition}px`
			}
			
			const touch = useTouch()
			const getDelta = (startX : number, startY : number) : number => {
				// #ifdef WEB
				const topHeight = document.querySelector('.uni-page-head').offsetHeight
				// #endif
				// #ifndef WEB
				const topHeight = 0
				// #endif
				return props.vertical ? startY - sliderRect.top - topHeight : startX - sliderRect.left;
			}
			let startValue = 0
			const currentKey = ref(0)
			const dragStatus = ref<DragStatus>('')
			const touchstart = (event : UniTouchEvent) => {
				if (props.disabled || dragStatus.value != '') return;
				// event.preventDefault()
				dragStatus.value = 'start'
				touch.start(event);
			
				// #ifdef VUE3
				const { startX, startY } = touch;
				const delta = getDelta(startX.value, startY.value)
				// #endif
				// #ifdef VUE2
				const delta = getDelta(event.touches[0].pageX, event.touches[0].pageY)
				// #endif
				const total = calculateTrackLength()
				let value = format(delta / total * scope.value + props.min, props.min, props.max, props.step)
				if (isRange.value) {
					currentKey.value = tempValue.value.findIndex((it) : boolean => it == closest(tempValue.value, value))
					tempValue.value[currentKey.value] = value
					innerValue.value = [...tempValue.value]
				} else {
					currentKey.value = 0;
					innerValue.value = value
				}
				startValue = value;
			}
			const touchmove = (event : UniTouchEvent) => {
				if (props.disabled) return;
				// event.preventDefault()
				dragStatus.value = 'dragging'
				touch.move(event);
				const key = currentKey.value
				const delta = props.vertical ? touch.deltaY : touch.deltaX;
				const total = calculateTrackLength()
				const diff = delta.value / total * scope.value;
				const value = format(startValue + diff, props.min, props.max, props.step)
				tempValue.value[key] = value
				if (isRange.value) {
					innerValue.value = [...tempValue.value]
				} else {
					innerValue.value = value
				}
			}
			const touchend = (_event : UniTouchEvent) => {
				if (props.disabled) return;
				dragStatus.value = ''
				if (!isRange.value) {
					currentKey.value = 0
				} else {
					currentKey.value = -1
				}
			}
			
			
			const setValue = (v: SliderValue) => {
				// if(dragStatus.value == '') {
				if (isRange.value) {
					tempValue.value = v.map((value) : number => format(value, props.min, props.max, props.step))
				} else {
					tempValue.value[0] = format(innerValue.value as number, props.min, props.max, props.step)
				}
				// }
				nextTick(updateProgressBarAndThumb)
			}
			
			const stopWatch = watch(innerValue, setValue, { deep: true })
			
			const init = () => {
				Promise.all([
					getRect('.l-slider', instance),
					getRect('.l-slider__rail', instance),
					getRect('.l-slider__thumb-wrapper', instance),
				]).then(([slider, rail, thumb])=>{
					sliderRect.width = slider.width;
					sliderRect.height = slider.height;
					sliderRect.left = slider.left;
					sliderRect.top = slider.top;
					
					railRect.width = rail.width;
					railRect.height = rail.height;
					railRect.left = rail.left;
					railRect.top = rail.top;
					
					thumbRect.width = thumb.width;
					thumbRect.height = thumb.height;
					thumbRect.left = thumb.left;
					thumbRect.top = thumb.top;
					
					setValue(innerValue.value)
				})
				
			}
			
			
			onMounted(()=>{
				nextTick(init)
			})
			onUnmounted(stopWatch)
			
			return {
				styles,
				railStyle,
				thumbStyle,
				progressBarStyle,
				startThumbStyle,
				endThumbStyle,
				touchstart,
				touchmove,
				touchend
			}
			
		}
	})
</script>
<style lang="scss">
	@import './index-u';
</style>
