import { client } from "../index.js";
import { Game } from "../api/game/Game.js";
import AbstractView from "./abstracts/AbstractView.js";
import { initShaderProgram } from "../3D/shaders.js"
import { initBuffers } from "../3D/buffers.js"
import "../3D/maths/gl-matrix-min.js"

export default class extends AbstractView
{
	constructor(params)
	{
		super(params, "Game");
		this.game = new Game(client, params.id);
		this.keys_pressed = [];
		this.my_player = undefined;
		this.gl = null;
		this.shader_prog = null;
		this.buffers = null;
		this.programInfo = null;
	}

	initGL()
	{
		const canvas = document.getElementById('glcanva');
		this.gl = canvas.getContext("webgl");

		if(this.gl === null)
		{
			alert("Unable to initialize WebGL. Your browser or machine may not support it. You may also be a bozo");
			return;
		}

		this.shader_prog = initShaderProgram(this.gl);
		this.buffers = initBuffers(this.gl);

		this.programInfo = {
			program: this.shader_prog,
			attribLocations: {
				vertexPosition: this.gl.getAttribLocation(this.shader_prog, "aPos"),
				vertexNormal: this.gl.getAttribLocation(this.shader_prog, "aNormal"),
			},
			uniformLocations: {
				projectionMatrix: this.gl.getUniformLocation(this.shader_prog, "uProj"),
				modelViewMatrix: this.gl.getUniformLocation(this.shader_prog, "uModView"),
				normalMatrix: this.gl.getUniformLocation(this.shader_prog, "uNormalMat"),
			},
		};

		this.gl.enable(this.gl.CULL_FACE);
		this.gl.cullFace(this.gl.BACK);
	}

	async join_game()
	{
		await this.game.join()

		let canvas = document.createElement("canvas");

		canvas.height = this.game.config.size_x;
		canvas.width = this.game.config.size_y;
		canvas.id = "glcanva"; 

		document.getElementById("app").appendChild(canvas);

		this.initGL();

		this.render_game();
	}

	draw()
	{
		const canvas = document.getElementById('glcanva');
		if(canvas === null)
			return 1;

		this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
		this.gl.clearDepth(1.0);
		this.gl.enable(this.gl.DEPTH_TEST);
		this.gl.depthFunc(this.gl.LEQUAL);
		this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

		const fieldOfView = (90 * Math.PI) / 180;
		const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight;
		const zNear = 0.1;
		const zFar = 100.0;
		const projectionMatrix = mat4.create();

		mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);

		this.setPositionAttribute();
		this.setNormalAttribute();

		this.gl.useProgram(this.programInfo.program);

		this.gl.uniformMatrix4fv(
			this.programInfo.uniformLocations.projectionMatrix,
			false,
			projectionMatrix
		);

		// rendering player bar
		this.renderCube(0.0, -5.0, -8.0, 0.0, 5);
	}

	renderCube(x, y, z, angle = 0, sx = 1, sy = 1, sz = 1)
	{
		const modelViewMatrix = mat4.create();

		mat4.translate(
			modelViewMatrix,
			modelViewMatrix,
			[x, y, z]
		);

		mat4.rotate(
			modelViewMatrix,
			modelViewMatrix,
			angle,
			[0, 1, 1],
		);

		mat4.scale(
			modelViewMatrix,
			modelViewMatrix,
			[sx, sy, sz]
		);

		const normalMatrix = mat4.create();
		mat4.invert(normalMatrix, modelViewMatrix);
		mat4.transpose(normalMatrix, normalMatrix);

		this.gl.uniformMatrix4fv(
			this.programInfo.uniformLocations.modelViewMatrix,
			false,
			modelViewMatrix
		);
		this.gl.uniformMatrix4fv(
			this.programInfo.uniformLocations.normalMatrix,
			false,
			normalMatrix,
		);

		const vertexCount = 36;
		const type = this.gl.UNSIGNED_SHORT;
		const offset = 0;
		this.gl.drawElements(this.gl.TRIANGLES, vertexCount, type, offset);
	}

	setNormalAttribute()
	{
		const numComponents = 3;
		const type = this.gl.FLOAT;
		const normalize = false;
		const stride = 0;
		const offset = 0;
		this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.normal);
		this.gl.vertexAttribPointer(
			this.programInfo.attribLocations.vertexNormal,
			numComponents,
			type,
			normalize,
			stride,
			offset,
		);
		this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexNormal);
	}

	setPositionAttribute(programInfo)
	{
		const numComponents = 3;
		const type = this.gl.FLOAT;
		const normalize = false;
		const stride = 0;
		const offset = 0;
		this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.vertex);
		this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.buffers.index);
		this.gl.vertexAttribPointer(
			this.programInfo.attribLocations.vertexPosition,
			numComponents,
			type,
			normalize,
			stride,
			offset
		);
		this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
	}

	render_game()
	{
		let loop_id = setInterval(() => {
			if (this.game === undefined)
				clearInterval(loop_id);
			if (this.my_player)
				this.my_player.update_paddle(this.keys_pressed);
			this.draw();
			this.game?.time.new_frame();
		// 1 sec  fps
		}, 1000 / 60);
	}

	async update_game_state()
	{
		document.getElementById("game-state").innerText = this.game.state;

		if (this.game.finished === false)
			await this.join_game();
	}

	async postInit()
	{
		let error_code = await this.game.init();

		if (error_code)
			return error_code;

		await this.update_game_state();
	}

	async getHtml()
	{
		return /* HTML */ `
			<link rel="stylesheet" href="/static/css/game.css">
			<h2 id="game-state"></h2>
			<div id="player_list"></div>
		`;
	}
}

