<script>
	import { onMount } from "svelte";

	onMount(async () => {
		
		// webgl.js
		const canvas = document.querySelector(".myCanvas");
		const image = document.querySelector(".myPreview__image");
		const gl = canvas.getContext("webgl", {
			preserveDrawingBuffer: true
		});

		const plane = createPlane(gl);
		const texture = createTexture(gl);
		const shader = createImageShader(gl);
		let matrix = [];

		function update() {
			gl.clearColor(0.0, 0.0, 0.0, 0.0);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

			shader.use().setProjection(matrix).setTexture(texture);
			plane.draw(shader);
		}

		function loadTexture() {
			drawToTexure(gl, texture, image);
			update();
		}

		function toMatrix(t) {
			// prettier-ignore
			return [
				t[0], t[1], 0, t[2],
				t[3], t[4], 0, t[5],
				0,    0,    1,    0,
				t[6], t[7], 0, t[8]
			];
		}

		if (image.naturalWidth && image.naturalHeight) {
			loadTexture();
		} else {
			image.addEventListener("load", () => {
				loadTexture();
			});
		}

		/**
		 * Event handlers
		 */

		document.addEventListener("myPointsChanged", (event) => {
			const points = event.detail.reduce(
				(result, { x, y }) => [...result, x / 960 - 1, 1 - y / 640],
				[]
			);

			const transform = new PerspT([1, -1, -1, -1, -1, 1, 1, 1], points);
			matrix = toMatrix(transform.coeffs);

			update();
		});

		/**
		 * Primitives
		 */

		function createPlane(gl) {
			const positionBuffer = createBuffer(
				gl,
				[1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]
			);

			return {
			draw(shader) {
				shader.bindPositionBuffer(positionBuffer);
				gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
			},
			};
		}

		function createImageShader(gl) {
			const vertexSource = `
			attribute vec4 aPosition;

			uniform mat4 uProjection;

			varying highp vec2 vTextureCoord;

			void main() {
				vTextureCoord = vec2((aPosition.x - 1.0) * 0.5, (aPosition.y - 1.0) * 0.5);

				gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0) * uProjection;
			}
			`;

			const fragmentSource = `
			varying highp vec2 vTextureCoord;

			uniform sampler2D uSampler;

			void main() {
				gl_FragColor = texture2D(uSampler, vTextureCoord);
			}
			`;

			const program = createProgram(
			gl,
			createShader(gl, vertexSource, gl.VERTEX_SHADER),
			createShader(gl, fragmentSource, gl.FRAGMENT_SHADER)
			);

			const positionAttribute = gl.getAttribLocation(program, "aPosition");
			const projectionLocation = gl.getUniformLocation(program, "uProjection");
			const samplerLocation = gl.getUniformLocation(program, "uSampler");

			const instance = {
			use() {
				gl.useProgram(program);
				gl.enableVertexAttribArray(positionAttribute);
				return instance;
			},
			bindPositionBuffer(buffer) {
				gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
				gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);
			},
			setProjection(value) {
				gl.uniformMatrix4fv(projectionLocation, false, value);
				return instance;
			},
			setTexture(value) {
				gl.activeTexture(gl.TEXTURE0);
				gl.bindTexture(gl.TEXTURE_2D, value);
				gl.uniform1i(samplerLocation, 0);
				return instance;
			},
			};

			return instance;
		}

		/**
		 * WebGL helpers
		 * https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial
		 */

		function createBuffer(gl, positions) {
			const positionBuffer = gl.createBuffer();
			gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

			return positionBuffer;
		}

		function createProgram(gl, vertexShader, fragmentShader) {
			const program = gl.createProgram();
			gl.attachShader(program, vertexShader);
			gl.attachShader(program, fragmentShader);
			gl.linkProgram(program);

			if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
			const info = gl.getProgramInfoLog(program);
			throw `Could not compile WebGL program. \n\n${info}`;
			}

			return program;
		}

		function createShader(gl, source, type) {
			const shader = gl.createShader(type);
			gl.shaderSource(shader, source);
			gl.compileShader(shader);

			if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
			const info = gl.getShaderInfoLog(shader);
			throw `Could not compile WebGL program. \n\n${info}`;
			}

			return shader;
		}

		function createTexture(gl) {
			const texture = gl.createTexture();
			const pixel = new Uint8Array([0, 0, 255, 255]);

			gl.bindTexture(gl.TEXTURE_2D, texture);
			gl.texImage2D(
				gl.TEXTURE_2D,
				0,
				gl.RGBA,
				1,
				1,
				0,
				gl.RGBA,
				gl.UNSIGNED_BYTE,
				pixel
			);

			return texture;
		}

		function drawToTexure(gl, texture, source) {
			gl.bindTexture(gl.TEXTURE_2D, texture);
			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);

			if (isPowerOf2(source.naturalWidth) && isPowerOf2(source.naturalHeight)) {
				gl.generateMipmap(gl.TEXTURE_2D);
			} else {
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
			}
		}

		function isPowerOf2(value) {
			return (value & (value - 1)) === 0;
		}



		// var download = function(){
		//   var link = document.createElement('a');
		//   link.download = 'filename.png';
		//   link.href = document.getElementById("resultCanvas").toDataURL();
		//   link.click();
		// }


		// 7360 x 4912
		// 1712, 1408
		// 3049, 428
		// 5712, 2983
		// 4354, 4209

		// 1920 x 1281
		// 447, 367
		// 795, 112
		// 1490, 778
		// 1136, 1098

		// const points = [
		// 	{ x: 795, y: 112 },
		// 	{ x: 1490, y: 778 },
		// 	{ x: 1136, y: 1098 },
		// 	{ x: 447, y: 367 }
		// ];

		const points = [
			{ x: 795, y: 112 },
			{ x: 447, y: 367 },
			{ x: 1136, y: 1098 },
			{ x: 1490, y: 778 },
		];

		// const points = [
		// 	{ x: 200, y: 100 }, // RT
		// 	{ x: 100, y: 100 }, // LT
		// 	{ x: 100, y: 200 }, // LB
		// 	{ x: 1490, y: 778 } // RB
		// ];

		const event = new CustomEvent("myPointsChanged", { detail: points });
		document.dispatchEvent(event);
		


		// draw result

		const resultCanvas = document.getElementById("resultCanvas");
		const rctx = resultCanvas.getContext("2d");

		// draw background
		let srcCtx = document.querySelector(".myCanvas");
		rctx.drawImage(srcCtx, 0, 0);

		// draw foreground
		let base_image = new Image();
		base_image.src = "/images/smartphone-on-dark-background_1920px.png";
		base_image.onload = function() {
			rctx.drawImage(base_image, 0, 0);
		}
	});
</script>

<svelte:head>
	<script src="https://unpkg.com/perspective-transform@1.1.3/dist/perspective-transform.js"></script>
</svelte:head>

<div class="container h-full mx-auto flex justify-center items-center">
	<div style="display: flex; flex-flow: row">
	
		<div class="myPreview" style="display: none">
			<img class="myPreview__image" src="/images/sample.jpg" alt="sample" />
			
			<canvas width="1920" height="1280" class="myCanvas"></canvas>
		</div>
	
		<div id="canvasWrapper">
			<canvas width="1920" height="1280" id="resultCanvas"></canvas>
		</div>
	</div>
</div>

<style lang="postcss">
	/* .myPreview {
		overflow: hidden;
		width: 300px;
		height: 300px;
		background: lightgray;
	}

	.myPreview__image {
		width: 300px;
		height: 300px;
		transform-origin: 0 0;
	} */

	#canvasWrapper #resultCanvas {
		width: 100%;
	}
</style>
