<template>
	<view class="u-draggable" ref="uDraggable">
		<movable-area class="u-draggable__area" :style="[areaStyles]">
			<movable-view
				v-for="(item, index) in cloneList"
				direction="all"
				class="u-draggable__view"
				:key="index"
				:style="[{
					width: $u.addUnit(width),
					height: $u.addUnit(height)
				}]"
				:class="[{ 
					'u-draggable__active': index == activeIndex,
					'u-draggable__disabled': disabled || item.disabled,
				}]"
				:x="item.x"
				:y="item.y"
				:friction="friction"
				:damping="damping"
				:disabled="item.disabled || (longpress && !longPressStarted) || !isDrag"
				@change="handleTouchMove($event, index)"
				@longpress="handleLongPress($event, index)"
				@touchstart="handleTouchStart($event, index)"
				@touchend="handleTouchEnd"
				@touchcancel="handleTouchEnd"
			>
				<slot
					name="item"
					:index="index"
					:startIndex="item.startIndex"
					:item="item.item"
					:active="!item.disabled && index == activeIndex"
					:disabled="item.disabled"
				/>
				
				<view class="u-draggable__close" v-if="closeable && !item.disabled" @click.stop="closeItem(index)">
					<slot name="close">
						<view class="u-draggable__close-inner">
							<u-icon name="close" size="12" color="#fff"></u-icon>
						</view>
					</slot>
				</view>
			</movable-view>
		</movable-area>
	</view>
</template>

<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
import props from './props.js';
import mixin from '../../libs/mixin/mixin';
import mpMixin from '../../libs/mixin/mpMixin';

/**
 * u-draggable 拖拽排序
 * @description 拖拽排序组件，支持多列拖拽排序，可关闭，手柄拖拽，长按拖拽。
 * @tutorial https://uview.d3u.cn/components/draggable.html
 * @property {Array}			list			数据列表 （默认 [] ）
 * @property {Number}			column			列数 （默认 2 ）
 * @property {Number}			aspectRatio		宽高比，填写这项时gridHeight失效 （默认 null ）
 * @property {Number}			itemHeight		项目高度 （默认 60 ）
 * @property {Number}			damping			阻尼系数 （默认 50 ）
 * @property {Number}			friction		摩擦系数 （默认 2 ）
 * @property {Boolean}			handle			是否使用手柄拖拽 （默认 false ）
 * @property {Boolean}			disabled			是否禁用 （默认 false ）
 * @property {Boolean}			longpress			是否长按拖拽 （默认 false ）
 * @property {Boolean}			closeable			是否显示关闭按钮 （默认 false ）
 *
 * @event {Function}	change	拖拽排序组件被点击时触发
 * @event {Function}	close	拖拽排序组件被关闭时触发
 * @example <u-draggable :list="list" @change="change"></u-draggable>
 */

export default {
	name: 'u-draggable',
	mixins: [mpMixin, mixin, props],
	data() {
		return {
			isDrag: false,
			isInit: false,
			maxIndex: -1,
			activeIndex: -1,
			isDisabled: false,
			positions: [],
			cloneList: [],
			containerWidth: 0,
			positionMap: [],
			longPressStarted: false,
			dragState: {
				item: null,     // 当前拖拽的元素
				index: 0,       // 当前索引位置
				oldIndex: -1,   // 开始拖拽时的索引位置
				sortableIndex: -1 // 在可排序数组中的索引
			}
		};
	},
	computed: {
		rows() {
			return Math.ceil((this.isInit ? this.cloneList.length : this.list.length) / this.column);
		},
		height() {
			if (this.aspectRatio) {
				return this.width / this.aspectRatio;
			}
			return this.$u.getPx(this.itemHeight);
		},
		width() {
			return this.containerWidth / this.column;
		},
		areaStyles() {
			return {
				// #ifdef APP-NVUE
				width: this.$u.addUnit(this.containerWidth),
				// #endif
				height: this.$u.addUnit((this.rows) * this.height)
			};
		}
	},
	mounted() {
		this.init();
	},
	watch: {
		list: {
			handler(newVal) {
				this.init();
			},
			deep: true
		}
	},
	// #ifdef VUE3
	emits: ['change', 'close'],
	// #endif
	methods: {
		async init() {
			// 清除旧数据
			this.clear();
			await this.$nextTick();
			const res = await this.getContainerRect();
			
			if (res && res.width) {
				this.containerWidth = res.width;
				this.positions = [];
				this.positionMap = [];
				
				let sortableIndex = 0;
				this.cloneList = this.list.map((item, listIndex) => {
					this.maxIndex++;
					
					// 计算网格位置
					const row = Math.floor(listIndex / this.column);
					const col = listIndex % this.column;
					const x = col * this.width;
					const y = row * this.height;
					
					// 创建位置信息
					this.positions[listIndex] = {
						row,
						x,
						y,
						x1: x + this.width,
						y1: y + this.height,
						disabled: item.disabled,
						oldIndex: listIndex
					};
					
					// 处理禁用和可排序项的映射
					if (item.disabled) {
						this.positionMap.push({ index: listIndex, disabled: true, sortableIndex: -1 });
					} else {
						this.positionMap.push({ index: listIndex, disabled: false, sortableIndex: sortableIndex });
						sortableIndex++;
					}
					
					// 创建克隆项
					return {
						index: listIndex,
						item,
						x: x,
						y: y,
						startIndex: listIndex,
						disabled: item.disabled,
						oldIndex: listIndex
					};
				});

				this.isInit = true;
			}
		},

		getContainerRect() {
			return new Promise(resolve => {
				// #ifndef APP-NVUE
				this.$uGetRect('.u-draggable').then(res => {
					resolve(res);
				});
				// #endif

				// #ifdef APP-NVUE
				const ref = this.$refs['uDraggable'];
				if (ref) {
					dom.getComponentRect(ref, (res) => {
						resolve({
							height: res.size.height,
							width: res.size.width
						});
					});
				} else {
					resolve({ height: 0, width: 0 });
				}
				// #endif
			});
		},

		handleTouchStart(event, index) {

			const { handle } = event.target.dataset || {}
			if(this.handle && !handle) {
				return;
			}
	
			// 如果启用了长按模式且未长按，不允许拖拽
			if (this.longpress && !this.longPressStarted) {
				return;
			}

			// 获取目标元素并进行验证
			const targetItem = this.cloneList[index];
			if (!targetItem || targetItem.disabled || this.disabled) return;
			
			// 检查positionMap是否存在对应的数据
			const positionData = this.positionMap[targetItem.oldIndex];
			if (!positionData) return;
			
			// 设置拖拽状态
			this.isDrag = true;
			this.activeIndex = index;
			
			this.dragState.item = targetItem;
			this.dragState.index = targetItem.index;
			this.dragState.oldIndex = targetItem.index;
			this.dragState.sortableIndex = positionData.sortableIndex;
		},

		handleTouchMove(event, index) {
			
			// 如果不在拖拽状态，直接返回
			if (!this.isDrag) return;

			// 如果不是当前激活的元素，直接返回
			if (index !== this.activeIndex) return;

			// 获取当前位置
			const {x, y} = event.detail;
			
			// 计算中心点坐标
			const centerX = x + this.width / 2;
			const centerY = y + this.height / 2;
			
			this.detectCollision(centerX, centerY, index);
		},

		handleLongPress(event, index) {
			// 标记长按已开始
			this.longPressStarted = true;
			// 触发拖拽开始
			this.handleTouchStart(event, index);
			// 震动反馈（如果支持）
			if (uni.vibrateShort) {
				uni.vibrateShort();
			}
		},

		handleTouchEnd(event) {
			this.longPressStarted = false;
			// 如果没有激活的元素或全局禁用，直接返回
			if (this.activeIndex === -1 || this.disabled) return;

			// 结束拖拽状态
			this.isDrag = false;

			// 判断是否需要触发变更事件（只有当位置发生变化时）
			const hasPositionChanged = this.dragState.index !== this.dragState.oldIndex && this.dragState.oldIndex > -1;
			
			// 获取最后拖拽的元素和目标位置
			const lastDraggedItem = this.cloneList[this.activeIndex];
			const targetPosition = this.positions[this.dragState.index];

			// 验证元素和位置
			if (!lastDraggedItem || this.positionMap[this.dragState.index].disabled) {
				this.activeIndex = -1;
				return;
			}

			// 添加微小偏移以确保动画效果
			lastDraggedItem.x = targetPosition.x + 0.001;
			lastDraggedItem.y = targetPosition.y + 0.001;

			// 延迟设置最终位置并触发事件
			uni.$u.sleep(30).then(() => {
				// 设置最终位置
				lastDraggedItem.x = targetPosition.x;
				lastDraggedItem.y = targetPosition.y;
				
				// 如果位置发生变化，触发事件并重置状态
				if (hasPositionChanged) {
					this.dragState.oldIndex = -1;
					this.activeIndex = -1;
					this.triggerEmits();
				} else {
					// 即使位置没有变化，也需要重置激活状态
					this.activeIndex = -1;
				}
			});
		},
		
		detectCollision(centerX, centerY, activeIndex) {
			// 如果全局禁用，直接返回
			if (this.disabled) return;
			
			// 快速边界检查
			if (centerX < 0 || centerY < 0 || 
				centerX > this.containerWidth || 
				centerY > this.rows * this.height) {
				return;
			}
			
			// 获取当前行和列
			const currentRow = Math.floor(centerY / this.height);
			const currentCol = Math.floor(centerX / this.width);
			
			// 计算可能的目标索引
			const possibleIndex = currentRow * this.column + currentCol;
		
			// 验证索引是否有效且不是禁用位置
			if (possibleIndex >= 0 && 
				possibleIndex < this.positions.length && 
				possibleIndex !== this.dragState.index &&
				!this.positionMap[possibleIndex].disabled) {
				
				const targetPos = this.positions[possibleIndex];
				// 检查是否在目标区域内
				if (
					centerX >= targetPos.x && 
					centerX <= targetPos.x1 && 
					centerY >= targetPos.y && 
					centerY <= targetPos.y1
				) {
					// 执行交换（只在可排序项之间）
					this.swapSortableItems(activeIndex, possibleIndex);
				}
			}
		},
		
		swapSortableItems(fromIndex, toIndex) {
			// 快速边界检查
			if (toIndex < 0 || toIndex >= this.positions.length) return;
			
			// 如果全局禁用或目标位置是禁用位置，直接返回
			if (this.disabled || this.positionMap[toIndex].disabled) return;

			// 获取拖拽元素
			const draggedItem = this.cloneList[fromIndex];
			if (!draggedItem || draggedItem.disabled) return;
			
			// 查找目标位置的元素
			const targetItem = this.cloneList.find(item => item.index === toIndex);
			if (!targetItem || targetItem.disabled) return;

			// 获取目标元素在cloneList中的索引
			const targetCloneIndex = this.cloneList.findIndex(item => item.index === toIndex);
			if (targetCloneIndex === -1) return;

			// 交换位置信息
			const draggedPosition = this.positions[this.dragState.index];
			const targetPosition = this.positions[toIndex];

			// 更新目标元素位置
			targetItem.x = draggedPosition.x;
			targetItem.y = draggedPosition.y;
			targetItem.index = this.dragState.index;

			// 更新拖拽元素索引
			draggedItem.index = toIndex;
			
			// 如果是拖拽状态，更新dragState的索引
			if (this.isDrag) {
				this.dragState.index = toIndex;
			}
		},

		triggerEmits() {
			// 按位置索引排序所有剩余项目
			const sortedItems = [...this.cloneList].sort((a, b) => a.index - b.index);
			
			// 创建最终结果数组
			const result = sortedItems.map(item => item.item);
			
			// 触发变更事件
			this.$emit('change', result);
		},

		closeItem(index) {
			// 如果全局禁用或项目禁用，直接返回
			if (this.disabled || (this.cloneList[index] && this.cloneList[index].disabled)) return;
			
			// 重置拖拽状态
			this.activeIndex = -1;
			this.isDrag = false;

			// 获取要删除的项目并验证
			const itemToRemove = this.cloneList[index];
			if (!itemToRemove) return;

			// 获取被删除项目的位置索引
			const removedPosition = itemToRemove.index;
			
			// 从列表中移除项目
			this.cloneList.splice(index, 1);
			
			// 重新计算所有项目的位置
			this.cloneList.forEach((item, idx) => {
				// 如果项目的位置索引大于被删除项目的位置，需要前移
				if (item.index > removedPosition) {
					item.index--;
					
					// 重新计算网格位置
					const row = Math.floor(item.index / this.column);
					const col = item.index % this.column;
					const x = col * this.width;
					const y = row * this.height;
					
					// 更新位置
					item.x = x;
					item.y = y;
					
					// 更新positions数组
					this.positions[item.index] = {
						row,
						x,
						y,
						x1: x + this.width,
						y1: y + this.height,
						disabled: item.disabled,
						oldIndex: item.index
					};
				}
			});
			
			// 重建位置映射表
			this.rebuildPositionMap();
			
			// 删除多余的位置信息
			if (this.positions.length > this.cloneList.length) {
				this.positions.splice(this.cloneList.length);
			}
			
			this.$emit('close', index);
		},

		
		rebuildPositionMap() {
			this.positionMap = [];
			let sortableIndex = 0;
			
			this.cloneList.forEach((item) => {
				if (item.disabled) {
					this.positionMap.push({ 
						index: item.index, 
						disabled: true, 
						sortableIndex: -1 
					});
				} else {
					this.positionMap.push({ 
						index: item.index, 
						disabled: false, 
						sortableIndex: sortableIndex 
					});
					sortableIndex++;
				}
			});
		},

		clear() {
			this.isInit = false;
			this.isDrag = false;
			this.maxIndex = -1;
			this.activeIndex = -1;
			this.cloneList = [];
			this.positions = [];
			this.positionMap = [];
		}
	},
	// #ifdef VUE2
	beforeDestroy() {
		this.clear();
	}
	// #endif
	// #ifdef VUE3
	beforeUnmount() {
		this.clear();
	}
	// #endif
};
</script>

<style lang="scss" scoped>
.u-draggable {
	overflow: hidden;
	/* #ifdef APP-NVUE */
	flex: 1;
	/*  #endif */
	/* #ifndef APP-NVUE */
	width: 100%;
	/*  #endif */

	&__area {
		/* #ifdef APP-NVUE */
		flex: 1;
		/*  #endif */
		/* #ifndef APP-NVUE */
		width: 100%;
		/*  #endif */
	}

	&__handle {
		position: absolute;
		z-index: 9999;
		display: flex;
		align-items: center;
		justify-content: center;
		right: 0;
		top: 0;
		
		&-inner {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 60px;
			height: 60px;
			background-color: #000;
		
		}
	}

	&__close {
		position: absolute;
		z-index: 9999;
		display: flex;
		align-items: center;
		justify-content: center;
		right: 0;
		top: 0;
		&-inner {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 20px;
			height: 20px;
			background-color: #000;
			border-radius: 50%;
			opacity: 0.8;
		}
	}

	&__view {
		z-index: 2;
		transition: opacity 300ms ease;
		box-sizing: border-box;
		
	}

	&__active {
		z-index: 9999;
		transition: transform 100ms ease;
	}

		
	&__disabled {
		opacity: 0.5;
		cursor: not-allowed;
		/* #ifndef APP-NVUE */
		pointer-events: none;
		/*  #endif */
	}

}

</style>
