
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 );

	}

	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"
		});

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

		var mesh				= model.meshes[0];

		console.log( mesh );

		const vertices = new Float32Array([
		  0.0,   0.5,   // 0 - top outer point
		  0.1123, 0.1545,  // 1 - inner point between top and right
		  0.4755, 0.1545,  // 2 - right outer point
		  0.1816, -0.0590, // 3 - inner point between right and bottom right
		  0.2939, -0.4045, // 4 - bottom right outer point
		  0.0,  -0.1909,  // 5 - inner bottom point
		  -0.2939,-0.4045, // 6 - bottom left outer point
		  -0.1816, -0.0590,// 7 - inner point between bottom left and left
		  -0.4755, 0.1545, // 8 - left outer point
		  -0.1123, 0.1545, // 9 - inner point between left and top
		]);

		const indices = new Uint16Array([
		  0, 1, 9,
		  1, 2, 3,
		  3, 4, 5,
		  5, 6, 7,
		  7, 8, 9,
		  1, 3, 5,
		  1, 5, 7,
		  1, 7, 9,
		]);

		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 - 500;
			instancePositions[i * 4 + 1] = 0;
			instancePositions[i * 4 + 2] = y - 500;
			instancePositions[i * 4 + 3] = 0; 
		}

		this.renderShader.setCanvas( this.canvas );

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

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

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

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

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


		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.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, 60, 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();

	}

}
