
import Shader 			from "../../framework/WebGpu.js"

import Matrix4 			from "../../framework/Matrix4.js"

import Vector3 			from "../../framework/Vector3.js"

import Camera 			from "../../framework/Camera.js";

import EventManager 	from "../../framework/eventManager.js";

import ShaderInpector	from "../../framework/ShaderInpector.js";


export class ParticleSimulation {

	canvas;

	device;

	camera;

	useLocalSort		= true;

	eventManager		= new EventManager();

	frameCount 			= 0;

	setCanvas( canvas ) {

		this.canvas = canvas;

		this.eventManager.setCanvas( canvas );

	}



	createTextureFromImageBitmap( device, imageBitmap ) {

		const texture = device.createTexture( {

			size: [ imageBitmap.width, imageBitmap.height, 1 ],

			format: 'rgba8unorm',

			usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT

		} );

		device.queue.copyExternalImageToTexture(

			{ source: imageBitmap },

			{ texture: texture },

			[ imageBitmap.width, imageBitmap.height, 1 ]

		);

		return texture;

	}

	async loadImageBitmap(url) {

		const response = await fetch( url );

		const blob = await response.blob();

		const imageBitmap = await createImageBitmap( blob );

		return imageBitmap;
	}


	async loadTexture( url ) {

		const imageBitmap 	= await this.loadImageBitmap( url );

		const texture 		= this.createTextureFromImageBitmap( this.device, imageBitmap );

		return texture;

	}

 createPlane(width, height, repeatU, repeatV) {

	const vertices   = new Float32Array( 18 );  // 6 vertices (2 triangles) * 3 coords
	const normals    = new Float32Array( 18 );  // same count as vertices
	const uvs        = new Float32Array( 12 );  // 6 vertices * 2 coords

	// Positions (two triangles forming a plane on XY plane at z=0)
	// Large plane from (-width/2, -height/2) to (width/2, height/2)
	vertices.set([
		-width / 2, -height / 2, 0,
		 width / 2, -height / 2, 0,
		-width / 2,  height / 2, 0,

		-width / 2,  height / 2, 0,
		 width / 2, -height / 2, 0,
		 width / 2,  height / 2, 0
	]);

	// Normals all pointing +Z
	for (let i = 0; i < 6; i++) {
		normals[i * 3 + 0] = 0;
		normals[i * 3 + 1] = 0;
		normals[i * 3 + 2] = 1;
	}

	// UVs scaled by repeatU, repeatV to repeat texture over the plane
	uvs.set([
		0,         0,
		repeatU,   0,
		0,         repeatV,

		0,         repeatV,
		repeatU,   0,
		repeatU,   repeatV
	]);

	return { vertices, normals, uvs };

}

	async setup( offscreenCanvas, width, height ) {

		offscreenCanvas.width	= width;

		offscreenCanvas.height	= height;

		this.canvas				= offscreenCanvas;

		const context 			= offscreenCanvas.getContext("webgpu");

		this.camera 			= new Camera( [0, 0, 1115], [0, -.3, 0], [0, 1, 0] );

		this.eventManager.setup( offscreenCanvas, this.camera );


		const adapter 			= await self.navigator.gpu.requestAdapter();

		if ( !adapter ) {

			throw new Error("Failed to get GPU adapter");

		}

		const presentationFormat = navigator.gpu.getPreferredCanvasFormat();

		this.device 			= await adapter.requestDevice();

		this.renderShader 		= new Shader( this.device  );

		context.configure({
			device: this.device,
			format: presentationFormat,
			alphaMode: "opaque"
		});
		const instanceCount = 100;
		const instancePositions = new Float32Array(instanceCount * 4); // vec4 per instance

		for (let i = 0; i < instanceCount; i++) {

			const x = (i % 10) * 300.0;
			const y = Math.floor(i / 10) * 350.0;

			instancePositions[i * 4 + 0] = x - 1000;
			instancePositions[i * 4 + 1] = 0;
			instancePositions[i * 4 + 2] = y - 1000;
			instancePositions[i * 4 + 3] = 0; 
		}


		var model				= await this.loadJSON("../../models/demo.json");

		var mesh				= model.meshes[0];

		console.log("mesh", mesh);

		this.renderShader.setCanvas( this.canvas );

		this.renderShader.topology = "triangle-list";

		await this.renderShader.setup( "../../shaders/triangle-list-texture-normal.wgsl");

		this.renderShader.setAttribute( "position", mesh.vertices );

		this.renderShader.setAttribute( "normal", mesh.normals );

		this.renderShader.setAttribute( "bitangent", mesh.bitangents );

		this.renderShader.setAttribute( "uv", mesh.texturecoords[0] );

		var faces	= mesh.faces;

		const indexArray = new Uint32Array(faces.length * 3);

		for (let i = 0; i < faces.length; i++) {

		    indexArray[i * 3 + 0] 	= faces[i][0];

		    indexArray[i * 3 + 1] 	= faces[i][1];

		    indexArray[i * 3 + 2] 	= faces[i][2];

		}

		this.renderShader.setIndices( indexArray );

		this.renderShader.setCanvas( this.canvas );

		this.renderShader.topology = "triangle-list";

		await this.renderShader.setup( "../../shaders/triangle-list-texture-normal.wgsl");

		/*
		const { vertices, normals, uvs } = this.createPlane( 1000, 1000, 4, 4 );

		this.renderShader.setAttribute( "position", vertices );

		this.renderShader.setAttribute( "normal", normals );

		this.renderShader.setAttribute( "uv", uvs );


		this.vertexCount = vertices.length / 3
			
		*/
		this.renderShader.setVariable( "instancePositions", instancePositions );

		var texture		= await this.loadTexture("./textures/0_floorTiles_diff.png");

		const sampler 	= this.device.createSampler({
			minFilter: 'linear',
			magFilter: 'linear',
			mipmapFilter: 'linear',
			addressModeU: 'repeat',
			addressModeV: 'repeat',
		});

		this.renderShader.setVariable( "mySampler", sampler );

		this.renderShader.setVariable( "myTexture", texture );

		var normalTexture		= await this.loadTexture("./textures/0_floorTiles_ddn.png");

		this.renderShader.setVariable( "normalMapTexture", normalTexture );


		this.render();

	}

	updateTimeDelta() {

		const now				= performance.now();

		this.deltaTimeValue		= ( now - this.lastFrameTime ) / 1000;

		this.lastFrameTime		= now;

	}

	async render() {

		this.updateTimeDelta();

		const viewMatrixData 			= this.camera.getViewMatrix();

		const projectionMatrixData 		= Matrix4.createProjectionMatrix( this.camera, this.canvas )

		const viewProjectionMatrix 		= Matrix4.multiply( projectionMatrixData, viewMatrixData );

		const cameraWorldMatrix 		= Matrix4.invert( viewMatrixData );  

		const cameraPosition			= Matrix4.getColumn( cameraWorldMatrix, 3 );

		this.renderShader.setVariable( "viewProjectionMatrix", viewProjectionMatrix );

		this.renderShader.setVariable( "cameraPosition", cameraPosition );


		this.renderShader.renderToCanvas( this.vertexCount, 74, 0 );

		this.frameCount++;

		requestAnimationFrame( this.render.bind( this ) );

	}

	async loadJSON( pathName ) {

		const response = await fetch( pathName );

		if ( !response.ok ){

			throw new Error( `Failed to load shader: ${ pathName }` );

		}

		return await response.json();

	}

}
