/// <reference types="Cypress" />

context('tool tests', () => {
	// @TODO: make rounded tools render consistently across platforms
	const roundedToolsCompareOptions = {
		failureThreshold: 13,
		failureThresholdType: 'pixel'
	};

	// beforeAll isn't a thing, and beforeEach applies also to tests declared above it,
	// so do this fake test + flag hack in order to execute some steps before the first test
	let before_first_real_test = true;
	it(`(fake test for setup)`, () => {
		cy.visit('/')
		cy.setResolution([800, 500]);
		cy.window().should('have.property', 'selected_colors'); // wait for app to be loaded
		before_first_real_test = false;
	});
	beforeEach(() => {
		if (before_first_real_test) return;
		cy.window().then({ timeout: 60000 }, async (win) => {
			win.selected_colors.foreground = "#000";
			win.selected_colors.background = "#fff";
			win.brush_shape = win.default_brush_shape;
			win.brush_size = win.default_brush_size
			win.eraser_size = win.default_eraser_size;
			win.airbrush_size = win.default_airbrush_size;
			win.pencil_size = win.default_pencil_size;
			win.stroke_size = win.default_stroke_size;
			win.clear();
		});
	});

	const simulateGesture = (win, { start, end, shift, shiftToggleChance = 0.01, secondary, secondaryToggleChance, target }) => {
		target = target || win.$(".main-canvas")[0];
		let startWithinRect = target.getBoundingClientRect();
		let canvasAreaRect = win.$(".canvas-area")[0].getBoundingClientRect();

		let startMinX = Math.max(startWithinRect.left, canvasAreaRect.left);
		let startMaxX = Math.min(startWithinRect.right, canvasAreaRect.right);
		let startMinY = Math.max(startWithinRect.top, canvasAreaRect.top);
		let startMaxY = Math.min(startWithinRect.bottom, canvasAreaRect.bottom);
		let startPointX = startMinX + start.x * (startMaxX - startMinX);
		let startPointY = startMinY + start.y * (startMaxY - startMinY);
		let endPointX = startMinX + end.x * (startMaxX - startMinX);
		let endPointY = startMinY + end.y * (startMaxY - startMinY);

		const $cursor = win.$(`<img src="images/cursors/default.png" class="user-cursor"/>`);
		$cursor.css({
			position: "absolute",
			left: 0,
			top: 0,
			opacity: 0,
			zIndex: 5, // @#: z-index
			pointerEvents: "none",
			transition: "opacity 0.5s",
		});
		$cursor.appendTo(".jspaint");
		let triggerMouseEvent = (type, point) => {

			const clientX = point.x;
			const clientY = point.y;
			// const el_over = win.document.elementFromPoint(clientX, clientY);
			const do_nothing = false;//!type.match(/move/) && (!el_over || !el_over.closest(".canvas-area"));
			$cursor.css({
				display: "block",
				position: "absolute",
				left: clientX,
				top: clientY,
				opacity: do_nothing ? 0.5 : 1,
			});
			if (do_nothing) {
				return;
			}

			let event = new win.$.Event(type, {
				view: window,
				bubbles: true,
				cancelable: true,
				clientX,
				clientY,
				screenX: clientX,
				screenY: clientY,
				offsetX: point.x,
				offsetY: point.y,
				button: secondary ? 2 : 0,
				buttons: secondary ? 2 : 1,
				shiftKey: shift,
			});
			win.$(target).trigger(event);
		};

		let t = 0;
		const stepsInGesture = 3;
		let pointForTime = (t) => {
			return {
				x: startPointX + (endPointX - startPointX) * t,
				y: startPointY + (endPointY - startPointY) * Math.pow(t, 0.3),
			};
		};

		return new Promise((resolve) => {
			triggerMouseEvent("pointerenter", pointForTime(t)); // so dynamic cursors follow the simulation cursor
			triggerMouseEvent("pointerdown", pointForTime(t));
			let move = () => {
				t += 1 / stepsInGesture;
				// if (seededRandom() < shiftToggleChance) {
				// 	shift = !shift;
				// }
				// if (seededRandom() < secondaryToggleChance) {
				// 	secondary = !secondary;
				// }
				if (t > 1) {
					triggerMouseEvent("pointerup", pointForTime(t));

					$cursor.remove();

					resolve();
				} else {
					triggerMouseEvent("pointermove", pointForTime(t));
					/*gestureTimeoutID =*/ setTimeout(move, 10);
				}
			};
			triggerMouseEvent("pointerleave", pointForTime(t));
			move();
		});
	};

	// const gesture = (points) => {
	// 	const options = { secondary: false, shift: false };
	// 	// @TODO: while loop
	// 	trigger("pointerenter", points[0].x, points[0].y, options);
	// 	trigger("pointerdown", points[0].x, points[0].y, options);
	// 	let i = 0;
	// 	for (; i < points.length; i++) {
	// 		trigger("pointermove", points[i].x, points[i].y, options);
	// 	}
	// 	i--;
	// 	trigger("pointerup", points[i].x, points[i].y, options);
	// };

	// it('brush tool', () => {
	// 	cy.get(".tool[title='Brush']").click();
	// 	// gesture([{ x: 50, y: 50 }, { x: 100, y: 100 }]);
	// 	cy.get(".swatch:nth-child(21)").rightclick();
	// 	cy.window().then({ timeout: 8000 }, async (win) => {
	// 		for (let secondary = 0; secondary <= 1; secondary++) {
	// 			for (let b = 0; b < 12; b++) {
	// 				win.$(`.chooser > :nth-child(${b + 1})`).click();
	// 				const start = { x: 0.05 + b * 0.05, y: 0.1 + 0.1 * secondary };
	// 				const end = { x: start.x + 0.04, y: start.y + 0.04 };
	// 				await simulateGesture(win, { shift: false, secondary: !!secondary, start, end });
	// 			}
	// 		}
	// 	});
	// 	cy.matchImageSnapshot();
	// });

	// @TODO: test transparent document mode
	it(`eraser tool`, () => {
		cy.get(`.tool[title='Eraser/Color Eraser']`).click();
		// gesture([{ x: 50, y: 50 }, { x: 100, y: 100 }]);
		cy.window().then({ timeout: 60000 }, async (win) => {
			for (let row = 0; row < 4; row++) {
				const secondary = !!(row % 2);
				const increaseSize = row >= 2;
				let $options = win.$(`.chooser > *`);
				for (let o = 0; o < $options.length; o++) {
					$options[o].click();
					if (increaseSize) {
						for (let i = 0; i < 5; i++) {
							win.$('body').trigger(new win.$.Event("keydown", { key: "NumpadPlus", keyCode: 107, which: 107 }));
						}
					}
					win.selected_colors.background = "#f0f";
					const start = { x: 0.05 + o * 0.05, y: 0.1 + 0.1 * row };
					const end = { x: start.x + 0.04, y: start.y + 0.04 };
					await simulateGesture(win, { shift: false, secondary: false, start, end });
					if (secondary) {
						// eslint-disable-next-line require-atomic-updates
						win.selected_colors.background = "#ff0";
						// eslint-disable-next-line require-atomic-updates
						win.selected_colors.foreground = "#f0f";
						const start = { x: 0.04 + o * 0.05, y: 0.11 + 0.1 * row };
						const end = { x: start.x + 0.03, y: start.y + 0.02 };
						await simulateGesture(win, { shift: false, secondary: true, start, end });
					}
				}
			}
		});
		cy.get(".main-canvas").matchImageSnapshot();
	});

	["Brush", "Pencil", "Rectangle", "Rounded Rectangle", "Ellipse", "Line"].forEach((toolName) => {
		it(`${toolName.toLowerCase()} tool`, () => {
			cy.get(`.tool[title='${toolName}']`).click();
			// gesture([{ x: 50, y: 50 }, { x: 100, y: 100 }]);
			cy.get(".swatch:nth-child(22)").rightclick();
			cy.window().then({ timeout: 60000 }, async (win) => {
				for (let row = 0; row < 4; row++) {
					const secondary = !!(row % 2);
					const increaseSize = row >= 2;
					let $options = win.$(`.chooser > *`);
					// Pencil has no options
					if ($options.length === 0) {
						$options = win.$("<dummy>");
					}
					for (let o = 0; o < $options.length; o++) {
						$options[o].click();
						if (increaseSize && (o === 0 || toolName === "Brush" || toolName === "Line")) {
							for (let i = 0; i < 5; i++) {
								win.$('body').trigger(new win.$.Event("keydown", { key: "NumpadPlus", keyCode: 107, which: 107 }));
							}
						}
						const start = { x: 0.05 + o * 0.05, y: 0.1 + 0.1 * row };
						const end = { x: start.x + 0.04, y: start.y + 0.04 };
						await simulateGesture(win, { shift: false, secondary: !!secondary, start, end });
					}
				}
			});
			cy.get(".main-canvas").matchImageSnapshot(toolName.match(/Rounded Rectangle|Ellipse/) ? roundedToolsCompareOptions : undefined);
		});
	});
});
