/**
 * 点方法。
 * 此库来自konva
 * 移植：https://jx2d.cn
 * tmzdy
 */
export function parsePathData(data) {
	if (!data) {
		return [];
	}
	var cs = data;
	var cc = [
		'm',
		'M',
		'l',
		'L',
		'v',
		'V',
		'h',
		'H',
		'z',
		'Z',
		'c',
		'C',
		'q',
		'Q',
		't',
		'T',
		's',
		'S',
		'a',
		'A',
	];
	cs = cs.replace(new RegExp(' ', 'g'), ',');
	for (var n = 0; n < cc.length; n++) {
		cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
	}
	var arr = cs.split('|');
	var ca = [];
	var coords = [];
	var cpx = 0;
	var cpy = 0;
	var re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
	var match;
	for (n = 1; n < arr.length; n++) {
		var str = arr[n];
		var c = str.charAt(0);
		str = str.slice(1);
		coords.length = 0;
		while ((match = re.exec(str))) {
			coords.push(match[0]);
		}
		var p = [];
		for (var j = 0, jlen = coords.length; j < jlen; j++) {
			if (coords[j] === '00') {
				p.push(0, 0);
				continue;
			}
			var parsed = parseFloat(coords[j]);
			if (!isNaN(parsed)) {
				p.push(parsed);
			} else {
				p.push(0);
			}
		}
		while (p.length > 0) {
			if (isNaN(p[0])) {
				break;
			}
			var cmd = null;
			var points = [];
			var startX = cpx,
				startY = cpy;
			var prevCmd, ctlPtx, ctlPty;
			var rx, ry, psi, fa, fs, x1, y1;
			switch (c) {
				case 'l':
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'L';
					points.push(cpx, cpy);
					break;
				case 'L':
					cpx = p.shift();
					cpy = p.shift();
					points.push(cpx, cpy);
					break;
				case 'm':
					var dx = p.shift();
					var dy = p.shift();
					cpx += dx;
					cpy += dy;
					cmd = 'M';
					if (ca.length > 2 && ca[ca.length - 1].command === 'z') {
						for (var idx = ca.length - 2; idx >= 0; idx--) {
							if (ca[idx].command === 'M') {
								cpx = ca[idx].points[0] + dx;
								cpy = ca[idx].points[1] + dy;
								break;
							}
						}
					}
					points.push(cpx, cpy);
					c = 'l';
					break;
				case 'M':
					cpx = p.shift();
					cpy = p.shift();
					cmd = 'M';
					points.push(cpx, cpy);
					c = 'L';
					break;
				case 'h':
					cpx += p.shift();
					cmd = 'L';
					points.push(cpx, cpy);
					break;
				case 'H':
					cpx = p.shift();
					cmd = 'L';
					points.push(cpx, cpy);
					break;
				case 'v':
					cpy += p.shift();
					cmd = 'L';
					points.push(cpx, cpy);
					break;
				case 'V':
					cpy = p.shift();
					cmd = 'L';
					points.push(cpx, cpy);
					break;
				case 'C':
					points.push(p.shift(), p.shift(), p.shift(), p.shift());
					cpx = p.shift();
					cpy = p.shift();
					points.push(cpx, cpy);
					break;
				case 'c':
					points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'C';
					points.push(cpx, cpy);
					break;
				case 'S':
					ctlPtx = cpx;
					ctlPty = cpy;
					prevCmd = ca[ca.length - 1];
					if (prevCmd.command === 'C') {
						ctlPtx = cpx + (cpx - prevCmd.points[2]);
						ctlPty = cpy + (cpy - prevCmd.points[3]);
					}
					points.push(ctlPtx, ctlPty, p.shift(), p.shift());
					cpx = p.shift();
					cpy = p.shift();
					cmd = 'C';
					points.push(cpx, cpy);
					break;
				case 's':
					ctlPtx = cpx;
					ctlPty = cpy;
					prevCmd = ca[ca.length - 1];
					if (prevCmd.command === 'C') {
						ctlPtx = cpx + (cpx - prevCmd.points[2]);
						ctlPty = cpy + (cpy - prevCmd.points[3]);
					}
					points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'C';
					points.push(cpx, cpy);
					break;
				case 'Q':
					points.push(p.shift(), p.shift());
					cpx = p.shift();
					cpy = p.shift();
					points.push(cpx, cpy);
					break;
				case 'q':
					points.push(cpx + p.shift(), cpy + p.shift());
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'Q';
					points.push(cpx, cpy);
					break;
				case 'T':
					ctlPtx = cpx;
					ctlPty = cpy;
					prevCmd = ca[ca.length - 1];
					if (prevCmd.command === 'Q') {
						ctlPtx = cpx + (cpx - prevCmd.points[0]);
						ctlPty = cpy + (cpy - prevCmd.points[1]);
					}
					cpx = p.shift();
					cpy = p.shift();
					cmd = 'Q';
					points.push(ctlPtx, ctlPty, cpx, cpy);
					break;
				case 't':
					ctlPtx = cpx;
					ctlPty = cpy;
					prevCmd = ca[ca.length - 1];
					if (prevCmd.command === 'Q') {
						ctlPtx = cpx + (cpx - prevCmd.points[0]);
						ctlPty = cpy + (cpy - prevCmd.points[1]);
					}
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'Q';
					points.push(ctlPtx, ctlPty, cpx, cpy);
					break;
				case 'A':
					rx = p.shift();
					ry = p.shift();
					psi = p.shift();
					fa = p.shift();
					fs = p.shift();
					x1 = cpx;
					y1 = cpy;
					cpx = p.shift();
					cpy = p.shift();
					cmd = 'A';
					points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
					break;
				case 'a':
					rx = p.shift();
					ry = p.shift();
					psi = p.shift();
					fa = p.shift();
					fs = p.shift();
					x1 = cpx;
					y1 = cpy;
					cpx += p.shift();
					cpy += p.shift();
					cmd = 'A';
					points = convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
					break;
			}
			ca.push({
				command: cmd || c,
				points: points,
				start: {
					x: startX,
					y: startY,
				},
				pathLength: calcLength(startX, startY, cmd || c, points),
			});
		}
		if (c === 'z' || c === 'Z') {
			ca.push({
				command: 'z',
				points: [],
				start: undefined,
				pathLength: 0,
			});
		}
	}
	return ca;
}

export function calcLength(x, y, cmd, points) {
	var len, p1, p2, t;
	
	switch (cmd) {
		case 'L':
			return getLineLength(x, y, points[0], points[1]);
		case 'C':
			len = 0.0;
			p1 = getPointOnCubicBezier(0, x, y, points[0], points[1], points[2], points[3], points[4], points[5]);
			for (t = 0.01; t <= 1; t += 0.01) {
				p2 = getPointOnCubicBezier(t, x, y, points[0], points[1], points[2], points[3], points[4], points[
					5]);
				len += getLineLength(p1.x, p1.y, p2.x, p2.y);
				p1 = p2;
			}
			return len;
		case 'Q':
			len = 0.0;
			p1 = getPointOnQuadraticBezier(0, x, y, points[0], points[1], points[2], points[3]);
			for (t = 0.01; t <= 1; t += 0.01) {
				p2 = getPointOnQuadraticBezier(t, x, y, points[0], points[1], points[2], points[3]);
				len += getLineLength(p1.x, p1.y, p2.x, p2.y);
				p1 = p2;
			}
			return len;
		case 'A':
			len = 0.0;
			var start = points[4];
			var dTheta = points[5];
			var end = points[4] + dTheta;
			var inc = Math.PI / 180.0;
			if (Math.abs(start - end) < inc) {
				inc = Math.abs(start - end);
			}
			p1 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
			if (dTheta < 0) {
				for (t = start - inc; t > end; t -= inc) {
					p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
					len += getLineLength(p1.x, p1.y, p2.x, p2.y);
					p1 = p2;
				}
			} else {
				for (t = start + inc; t < end; t += inc) {
					p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
					len += getLineLength(p1.x, p1.y, p2.x, p2.y);
					p1 = p2;
				}
			}
			p2 = getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
			len += getLineLength(p1.x, p1.y, p2.x, p2.y);
			return len;
	}
	return 0;
}

export function convertEndpointToCenterParameterization(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
	var psi = psiDeg * (Math.PI / 180.0);
	var xp = (Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
	var yp = (-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
		(Math.cos(psi) * (y1 - y2)) / 2.0;
	var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
	if (lambda > 1) {
		rx *= Math.sqrt(lambda);
		ry *= Math.sqrt(lambda);
	}
	var f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) /
		(rx * rx * (yp * yp) + ry * ry * (xp * xp)));
	if (fa === fs) {
		f *= -1;
	}
	if (isNaN(f)) {
		f = 0;
	}
	var cxp = (f * rx * yp) / ry;
	var cyp = (f * -ry * xp) / rx;
	var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
	var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
	var vMag = function(v) {
		return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
	};
	var vRatio = function(u, v) {
		return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
	};
	var vAngle = function(u, v) {
		return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
	};
	var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
	var u = [(xp - cxp) / rx, (yp - cyp) / ry];
	var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
	var dTheta = vAngle(u, v);
	if (vRatio(u, v) <= -1) {
		dTheta = Math.PI;
	}
	if (vRatio(u, v) >= 1) {
		dTheta = 0;
	}
	if (fs === 0 && dTheta > 0) {
		dTheta = dTheta - 2 * Math.PI;
	}
	if (fs === 1 && dTheta < 0) {
		dTheta = dTheta + 2 * Math.PI;
	}
	return [cx, cy, rx, ry, theta, dTheta, psi, fs];
}

export function getSelfRect() {
	var points = [];
	this.dataArray.forEach(function(data) {
		if (data.command === 'A') {
			var start = data.points[4];
			var dTheta = data.points[5];
			var end = data.points[4] + dTheta;
			var inc = Math.PI / 180.0;
			if (Math.abs(start - end) < inc) {
				inc = Math.abs(start - end);
			}
			if (dTheta < 0) {
				for (let t = start - inc; t > end; t -= inc) {
					const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
						data.points[3], t, 0);
					points.push(point.x, point.y);
				}
			} else {
				for (let t = start + inc; t < end; t += inc) {
					const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2],
						data.points[3], t, 0);
					points.push(point.x, point.y);
				}
			}
		} else if (data.command === 'C') {
			for (let t = 0.0; t <= 1; t += 0.01) {
				const point = Path.getPointOnCubicBezier(t, data.start.x, data.start.y, data.points[0], data
					.points[1], data.points[2], data.points[3], data.points[4], data.points[5]);
				points.push(point.x, point.y);
			}
		} else {
			points = points.concat(data.points);
		}
	});
	var minX = points[0];
	var maxX = points[0];
	var minY = points[1];
	var maxY = points[1];
	var x, y;
	for (var i = 0; i < points.length / 2; i++) {
		x = points[i * 2];
		y = points[i * 2 + 1];
		if (!isNaN(x)) {
			minX = Math.min(minX, x);
			maxX = Math.max(maxX, x);
		}
		if (!isNaN(y)) {
			minY = Math.min(minY, y);
			maxY = Math.max(maxY, y);
		}
	}
	return {
		x: Math.round(minX),
		y: Math.round(minY),
		width: Math.round(maxX - minX),
		height: Math.round(maxY - minY),
	};
}

export function expandPoints(p, tension) {
    var len = p.length, allPoints = [], n, cp;
    for (n = 2; n < len - 2; n += 2) {
        cp = getControlPoints(p[n - 2], p[n - 1], p[n], p[n + 1], p[n + 2], p[n + 3], tension);
        if (isNaN(cp[0])) {
            continue;
        }
        allPoints.push(cp[0]);
        allPoints.push(cp[1]);
        allPoints.push(p[n]);
        allPoints.push(p[n + 1]);
        allPoints.push(cp[2]);
        allPoints.push(cp[3]);
    }
    return allPoints;
}

export function getControlPoints(x0, y0, x1, y1, x2, y2, t) {
    var d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)), d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)), fa = (t * d01) / (d01 + d12), fb = (t * d12) / (d01 + d12), p1x = x1 - fa * (x2 - x0), p1y = y1 - fa * (y2 - y0), p2x = x1 + fb * (x2 - x0), p2y = y1 + fb * (y2 - y0);
    return [p1x, p1y, p2x, p2y];
}


export function getTensionPointsClosed(points,tension) {
        var p = points ,len = p.length, firstControlPoints = getControlPoints(p[len - 2], p[len - 1], p[0], p[1], p[2], p[3], tension), lastControlPoints = getControlPoints(p[len - 4], p[len - 3], p[len - 2], p[len - 1], p[0], p[1], tension), middle = expandPoints(p, tension), tp = [firstControlPoints[2], firstControlPoints[3]]
            .concat(middle)
            .concat([
            lastControlPoints[0],
            lastControlPoints[1],
            p[len - 2],
            p[len - 1],
            lastControlPoints[2],
            lastControlPoints[3],
            firstControlPoints[0],
            firstControlPoints[1],
            p[0],
            p[1],
        ]);
        return tp;
    }