import { Point, ClipperclipStart, Rectangle } from './type';
const sysinfo = uni.getWindowInfo();


/**
 * 将给定的值限制在指定的最小值和最大值之间
 * @param {number} value - 要限制的值。
 * @param {number} min - 允许的最小值。
 * @param {number} max - 允许的最大值。
 * @returns {number} 返回限制后的值。
 */
export function clamp(value : number, min : number, max : number) : number {
	return Math.min(Math.max(value, min), max)
};

/**
 * 计算图像尺寸
 * @param {number} width - 图像原始宽度
 * @param {number} height - 图像原始高度
 * @param {number} [clipWidth] - 图像剪辑宽度
 * @param {number} [clipHeight] - 图像剪辑高度
 * @param {number} [originWidth] - 图像原始宽度（备用）
 * @param {number} [originHeight] - 图像原始高度（备用）
 * @returns {{imageWidth: number, imageHeight: number}} - 计算后的图像尺寸
 */
export function calcImageSize(
	width : number,
	height : number,
	originWidth : number,
	originHeight : number,
	clipWidth ?: number,
	clipHeight ?: number) : number[] {

	// 如果原始宽度和高度都为0，则将图像宽度设置为系统窗口宽度，高度设置为0
	if (width == 0 && height == 0) {
		return [sysinfo.windowWidth, 0]
	}

	// 使用剪辑宽度和高度，如果未提供则使用原始宽度和高度
	const finalWidth = clipWidth ?? originWidth;
	const finalHeight = clipHeight ?? originHeight;

	// 根据宽高比计算最终的图像尺寸
	if (width / height > finalWidth / finalHeight) {
		return [
			(width / height) * finalHeight,
			finalHeight
		]
	} else {
		return [
			finalWidth,
			(height / width) * finalWidth
		]
	}
}

/**
 * 计算图片缩放比例
 * @param {number} imageWidth 图片原始宽度
 * @param {number} imageHeight 图片原始高度
 * @param {number} clipWidth 裁剪区域宽度
 * @param {number} clipHeight 裁剪区域高度
 * @param {number} angle 图片旋转角度
 * @param {number=} scale 图片缩放比例（可选，如果未提供则使用originScale）
 * @returns {number} 计算后的图片缩放比例
 */
export function calcImageScale(
	imageWidth : number,
	imageHeight : number,
	clipWidth : number,
	clipHeight : number,
	angle : number,
	scale : number
	) : number {
	let _scale = scale
	let _imageWidth = imageWidth
	let _imageHeight = imageHeight
	// 旋转角度导致宽高互换的情况
	if ((angle / 90) % 2 != 0) {
		_imageWidth = imageHeight
		_imageHeight = imageWidth
	}

	// 计算缩放比例
	const scaleX = clipWidth / _imageWidth;
	const scaleY = clipHeight / _imageHeight;

	// 如果旋转角度不为0且当前缩放比例等于最大缩放比例，则调整缩放比例
	if (angle != 0 && scale == Math.max(clipWidth / _imageHeight, clipHeight / _imageWidth)) {
		return Math.max(scaleX, scaleY);
	}

	// 根据裁剪区域调整缩放比例
	if (_imageWidth * scale < clipWidth) {
		_scale = scaleX;
	}
	if (_imageHeight * scale < clipHeight) {
		_scale = Math.max(scale, scaleY);
	}
	return _scale
}



/**
 * 计算图片在裁剪区域内的偏移量
 * @param {number} imageLeft 图片左边缘的初始偏移量。
 * @param {number} imageTop 图片顶边缘的初始偏移量。
 * @param {number} imageWidth 图片的原始宽度。
 * @param {number} imageHeight 图片的原始高度。
 * @param {number} clipX 裁剪区域的左边缘位置。
 * @param {number} clipY 裁剪区域的顶边缘位置。
 * @param {number} clipWidth 裁剪区域的宽度。
 * @param {number} clipHeight 裁剪区域的高度。
 * @param {number} angle 图片的旋转角度。
 * @param {number} scale 图片的缩放比例。
 * @returns {OffsetAndScale} 包含计算后的偏移量left、top和缩放比例scale的对象。
 */
export function calcImageOffset(
	imageLeft : number,
	imageTop : number,
	imageWidth : number,
	imageHeight : number,
	clipX : number,
	clipY : number,
	clipWidth : number,
	clipHeight : number,
	angle : number,
	scale : number) : number[] {

	let left = imageLeft;
	let top = imageTop;


	let _imageWidth = imageWidth
	let _imageHeight = imageHeight
	// 旋转角度导致宽高互换的情况
	if ((angle / 90) % 2 != 0) {
		_imageWidth = imageHeight
		_imageHeight = imageWidth
	}


	// 计算当前图片尺寸
	const currentImageWidth = _imageWidth * scale / 2;
	const currentImageHeight = _imageHeight * scale / 2;

	// 限制图片偏移量，确保图片在裁剪内
	left = Math.min(left, clipX + currentImageWidth);
	left = Math.max(left, clipX + clipWidth - currentImageWidth);
	top = Math.min(top, clipY + currentImageHeight);
	top = Math.max(top, clipY + clipHeight - currentImageHeight);


	// return {
	// 	left,
	// 	top,
	// 	scale
	// }

	return [left, top, scale]
}

/**
 * 根据给定的宽度和高度计算直角三角形的斜边长度（勾股定理）
 * @param {number} width - 直角三角形的宽度。
 * @param {number} height - 直角三角形的高度。
 * @returns {number} 返回直角三角形的斜边长度。
 */
export function calcPythagoreanTheorem(width : number, height : number) {
	return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
}


/**
 * 计算触摸点相对于给定点的偏移量
 * @param {Point} point - 给定的点对象，包含 x 和 y 坐标。
 * @param {number} clientX - 触摸点的客户端 x 坐标。
 * @param {number} clientY - 触摸点的客户端 y 坐制。
 * @returns {number[]} 返回一个包含两个数字的数组，分别表示触摸点相对于给定点的 x 和 y 偏移量。
 */
export function imageTouchMoveOfCalcOffset(point : Point, clientX : number, clientY : number) : number[] {
	return [
		clientX - point.x,
		clientY - point.y,
	]
}


/**
 * 根据触摸点的位置确定点击的是裁剪框的哪个角
 * @param {number} clipX - 裁剪框的 x 坐标。
 * @param {number} clipY - 裁剪框的 y 坐标。
 * @param {number} clipWidth - 裁剪框的宽度。
 * @param {number} clipHeight - 裁剪框的高度。
 * @param {number} currentX - 触摸点的 x 坐标。
 * @param {number} currentY - 触摸点的 y 坐标。
 * @returns {number} 返回点击的角，1 表示右下角，2 表示右上角，3 表示左上角，4 表示左下角；如果没有点击在角上，则返回 -1。
 */
export function determineDirection(clipX : number, clipY : number, clipWidth : number, clipHeight : number, currentX : number, currentY : number) : number {
	/*
	 * (右下>>1 右上>>2 左上>>3 左下>>4)
	 */
	let corner : number = -1;
	/**
	 * 思路：（利用直角坐标系）
	 *  1.找出裁剪框中心点
	 *  2.如点击坐标在上方点与左方点区域内，则点击为左上角
	 *  3.如点击坐标在下方点与右方点区域内，则点击为右下角
	 *  4.其他角同理
	 */
	const mainPoint = [clipX + clipWidth / 2, clipY + clipHeight / 2]; // 中心点
	const currentPoint = [currentX, currentY]; // 触摸点

	if (currentPoint[0] <= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
		corner = 3; // 左上
	} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
		corner = 2; // 右上
	} else if (currentPoint[0] <= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
		corner = 4; // 左下
	} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
		corner = 1; // 右下
	}
	return corner;
}

/**
 * 根据触摸事件更新裁剪框的尺寸和位置，并保持比例（如果需要）
 * @param {number} clipWidth - 当前裁剪框的宽度。
 * @param {number} clipHeight - 当前裁剪框的高度。
 * @param {number} oldClipX - 当前裁剪框的 x 坐标。
 * @param {number} oldClipY - 当前裁剪框的 y 坐标。
 * @param {number} minWidth - 裁剪框允许的最小宽度。
 * @param {number} maxWidth - 裁剪框允许的最大宽度。
 * @param {number} minHeight - 裁剪框允许的最小高度。
 * @param {number} maxHeight - 裁剪框允许的最大高度。
 * @param {ClipperclipStart} clipStart - 包含裁剪框起始位置和拖动角的信息。
 * @param {boolean} isLockRatio - 是否保持裁剪框的宽高比。
 * @param {UniTouch} touch - 触摸事件对象，包含 clientX 和 clientY 属性。
 * @returns {number[] | null} 如果裁剪框尺寸或位置发生变化，则返回一个包含新宽度、新高度、新 x 坐标和新 y 坐标的数组；否则返回 null。
 */
export function clipTouchMoveOfCalculate(
	clipWidth : number,
	clipHeight : number,
	oldClipX : number,
	oldClipY : number,
	minWidth : number,
	maxWidth : number,
	minHeight : number,
	maxHeight : number,
	clipStart : ClipperclipStart,
	isLockRatio : boolean,
	touch : UniTouch) : number[] | null {
	const clientX = touch.clientX;
	// const clientY = touch.clientY;

	// 获取裁剪框新尺寸
	let width = clipWidth;
	let height = clipHeight;
	let clipX = oldClipX;
	let clipY = oldClipY;

	// 更新尺寸并保持比例
	const updateSizeWithRatio = (newWidth : number) => {
		width = newWidth;
		height = isLockRatio ? width / (clipWidth / clipHeight) : height;
	};
	// 检查并修正尺寸
	const checkAndCorrectSize = () : boolean => {
		width = clamp(width, minWidth, maxWidth);
		height = clamp(height, minHeight, maxHeight);
		return width != clipWidth || height != clipHeight;
	};
	// 根据拖动位置更新尺寸和位置
	switch (clipStart.corner) {
		case 1:
			updateSizeWithRatio(clipStart.width - clipStart.x + clientX);
			if (!checkAndCorrectSize()) return null;
			break;
		case 2:
			updateSizeWithRatio(clipStart.width - clipStart.x + clientX);
			if (!checkAndCorrectSize()) return null;
			clipY = clipStart.clipY - (height - clipStart.height);
			break;
		case 3:
			updateSizeWithRatio(clipStart.x - clientX + clipStart.width);
			if (!checkAndCorrectSize()) return null;
			clipY = clipStart.clipY - (height - clipStart.height);
			clipX = clipStart.clipX - (width - clipStart.width);
			break;
		case 4:
			updateSizeWithRatio(clipStart.width + clipStart.x - clientX);
			if (!checkAndCorrectSize()) return null;
			clipX = clipStart.clipX - (width - clipStart.width);
			break;
		default:
			return null;
	}

	return [
		width,
		height,
		clipX,
		clipY
	]
}



/**
 * 判断一个点是否在圆内（包括圆上）
 * @param {Point} point - 要检查的点对象，包含 x 和 y 坐标。
 * @param {Point} center - 圆心的点对象，包含 x 和 y 坐标。
 * @param {number} [radius=10] - 圆的半径，默认值为 10。
 * @returns {boolean} 如果点在圆内（包括圆上），则返回 true，否则返回 false。
 */
export function isPointInCircle(point : Point, center : Point, radius : number = 10) : boolean {
	const dx = point.x - center.x;
	const dy = point.y - center.y;
	return Math.sqrt(dx * dx + dy * dy) <= radius;
}

/**
 * 获取坐标在矩形的哪个顶点
 * @param point 点的坐标 Point
 * @param rectangle 多边形的
 * @param radius 顶点的半径
 * @returns 如果点在多边形顶点返回顶点下标
 */
export function getPointPositionInRectangle(
	point : Point,
	rectangle : Rectangle,
	radius : number = 30
	) : number | null {
	const topLeft : Point = { x: rectangle.x, y: rectangle.y };
	const topRight : Point = { x: rectangle.x + rectangle.width, y: rectangle.y };
	const bottomLeft : Point = { x: rectangle.x, y: rectangle.y + rectangle.height };
	const bottomRight : Point = { x: rectangle.x + rectangle.width, y: rectangle.y + rectangle.height };

	const corners = [
		bottomRight,
		topRight,
		topLeft,
		bottomLeft,
	];

	for (let i = 0; i < corners.length; i++) {
		if (isPointInCircle(point, corners[i], radius)) {
			return i + 1;
		}
	}

	return null;
}

/**
 * 判断点是否在多边形内（使用射线法）
 * @param point 点的坐标 Point
 * @param vs 多边形的顶点数组，每个顶点是一个 Point 数组
 * @param start 起始索引（可选，默认为0）
 * @param end 结束索引（可选，默认为vs.length）
 * @returns 如果点在多边形内返回 true，否则返回 false
 */
function pointInPolygonNested(point: Point, vs: Point[], start?: number, end?: number): boolean {
    const {x, y} = point;
    let inside = false;
	start = start ?? 0;
	end = end ?? vs.length
    const len = end - start;
    for (let i = 0, j = len - 1; i < len; j = i++) {
        const xi = vs[i + start].x;
		const yi = vs[i + start].y;
        const xj = vs[j + start].x; 
		const yj = vs[j + start].y;
        const intersect = ((yi > y) !== (yj > y))
            && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }
    return inside;
}

/**
 * 判断一个点是否在旋转后的矩形内（包括边界）
 * @param {Point} point - 要检查的点对象，包含 x 和 y 坐标。
 * @param {Rectangle} rect - 矩形对象，包含 x、y、width 和 height 属性。
 * @param {number} scale - 矩形的缩放比例。
 * @param {number} angle - 矩形旋转的角度（角度制）。
 * @returns {boolean} 如果点在旋转后的矩形内（包括边界），则返回 true，否则返回 false。
 */
export function isPointInRotatedRectangle(
	point : Point,
	rect : Rectangle,
	scale : number,
	angle : number // 角度制
	) : boolean {
	// 将角度转换为弧度
	const radians = (angle * Math.PI) / 180;

	// 计算矩形的中心点
	const rectCenterX = rect.x + rect.width / 2;
	const rectCenterY = rect.y + rect.height / 2;

	// 缩放矩形的宽高
	const scaledWidth = rect.width * scale;
	const scaledHeight = rect.height * scale;

	// 旋转点坐标，基于矩形的中心点
	const cosAngle = Math.cos(-radians);
	const sinAngle = Math.sin(-radians);
	// const rotatedPoint : Point = {
	// 	x:
	// 		cosAngle * (point.x - rectCenterX) -
	// 		sinAngle * (point.y - rectCenterY) +
	// 		rectCenterX,
	// 	y:
	// 		sinAngle * (point.x - rectCenterX) +
	// 		cosAngle * (point.y - rectCenterY) +
	// 		rectCenterY
	// };
	// 检查旋转后的点是否在未旋转的矩形内
	// 这里我们需要计算旋转后的矩形的四个顶点，然后判断点是否在这个四边形内
	const halfScaledWidth = scaledWidth / 2;
	const halfScaledHeight = scaledHeight / 2;

	// 计算旋转后的矩形的四个顶点
	const topLeft : Point = {
		x: rectCenterX - halfScaledWidth * cosAngle + halfScaledHeight * sinAngle,
		y: rectCenterY - halfScaledWidth * sinAngle - halfScaledHeight * cosAngle
	};
	const topRight : Point = {
		x: rectCenterX + halfScaledWidth * cosAngle + halfScaledHeight * sinAngle,
		y: rectCenterY + halfScaledWidth * sinAngle - halfScaledHeight * cosAngle
	};
	const bottomRight : Point = {
		x: rectCenterX + halfScaledWidth * cosAngle - halfScaledHeight * sinAngle,
		y: rectCenterY + halfScaledWidth * sinAngle + halfScaledHeight * cosAngle
	};
	const bottomLeft : Point = {
		x: rectCenterX - halfScaledWidth * cosAngle - halfScaledHeight * sinAngle,
		y: rectCenterY - halfScaledWidth * sinAngle + halfScaledHeight * cosAngle
	};
	// 计算向量叉乘
	function crossMul(v1 : Point, v2 : Point) : number {
		return v1.x * v2.y - v1.y * v2.x;
	}
	// 判断两条线段是否相交
	function checkCross(p1 : Point, p2 : Point, p3 : Point, p4 : Point) : boolean {
		const v1 : Point = { x: p1.x - p3.x, y: p1.y - p3.y };
		const v2 : Point = { x: p2.x - p3.x, y: p2.y - p3.y };

		const v3 : Point = { x: p4.x - p3.x, y: p4.y - p3.y };
		const v : number = crossMul(v1, v3) * crossMul(v2, v3);

		const v1_2 : Point = { x: p3.x - p1.x, y: p3.y - p1.y };
		const v2_2 : Point = { x: p4.x - p1.x, y: p4.y - p1.y };
		const v3_2 : Point = { x: p2.x - p1.x, y: p2.y - p1.y };
		return (v <= 0 && crossMul(v1_2, v3_2) * crossMul(v2_2, v3_2) <= 0);
	}
	
	// 使用射线法判断点是否在四边形内
	function isPointInPolygon(point : Point, polygon : Point[]) : boolean {
		// const p1 : Point = point;
		// const p2 : Point = { x: 1000000000000, y: point.y };
		// let count = 0;
		// // 对每条边都和射线作对比
		// for (let i = 0; i < polygon.length - 1; i++) {
		// 	const p3 : Point = polygon[i];
		// 	const p4 : Point = polygon[i + 1];
		// 	if (checkCross(p1, p2, p3, p4)) {
		// 		count++;
		// 	}
		// }
		// console.log('count', count)
		// const p3 : Point = polygon[polygon.length - 1];
		// const p4 : Point = polygon[0];
		// if (checkCross(p1, p2, p3, p4)) {
		// 	count++;
		// }
		// return count % 2 == 0 ? false : true;



		let isInside = false;
		for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
			if (
				(polygon[i].y > point.y) != (polygon[j].y > point.y) &&
				point.x <
				((polygon[j].x - polygon[i].x) * (point.y - polygon[i].y)) /
				(polygon[j].y - polygon[i].y) +
				polygon[i].x
			) {
				isInside = !isInside;
			}
		}
		return isInside;
	}
	
	return isPointInPolygon(point, [
		topLeft,
		topRight,
		bottomRight,
		bottomLeft
	]);
}


