import { version } from '../../core/core.js';
import { EventHandler } from '../../core/event-handler.js';
import { platform } from '../../core/platform.js';
import { now } from '../../core/time.js';
import { Vec2 } from '../../core/math/vec2.js';
import { Color } from '../../core/math/color.js';
import { CLEARFLAG_COLOR, CLEARFLAG_DEPTH, DISPLAYFORMAT_LDR, PRIMITIVE_TRIFAN, TYPE_FLOAT32, SEMANTIC_POSITION, CULLFACE_BACK, PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PRIMITIVE_POINTS } from './constants.js';
import { BlendState } from './blend-state.js';
import { DepthState } from './depth-state.js';
import { ScopeSpace } from './scope-space.js';
import { VertexBuffer } from './vertex-buffer.js';
import { VertexFormat } from './vertex-format.js';
import { StencilParameters } from './stencil-parameters.js';

class GraphicsDevice extends EventHandler {
		static{
				this.EVENT_RESIZE = 'resizecanvas';
		}
		constructor(canvas, options){
				super(), this.backBuffer = null, this.backBufferSize = new Vec2(), this.backBufferAntialias = false, this.isWebGPU = false, this.isWebGL2 = false, this.isHdr = false, this.maxIndirectDrawCount = 1024, this.maxColorAttachments = 1, this.maxSamples = 1, this.supportsCompute = false, this.supportsStorageTextureRead = false, this.renderTarget = null, this.shaders = [], this.textures = [], this.targets = new Set(), this.renderVersion = 0, this.insideRenderPass = false, this.supportsUniformBuffers = false, this.supportsClipDistances = false, this.textureRG11B10Renderable = false, this.textureFloatFilterable = false, this.blendState = new BlendState(), this.depthState = new DepthState(), this.stencilEnabled = false, this.stencilFront = new StencilParameters(), this.stencilBack = new StencilParameters(), this.defaultClearOptions = {
						color: [
								0,
								0,
								0,
								1
						],
						depth: 1,
						stencil: 0,
						flags: CLEARFLAG_COLOR | CLEARFLAG_DEPTH
				}, this.clientRect = {
						width: 0,
						height: 0
				}, this._shadersDirty = false, this.capsDefines = new Map(), this.mapsToClear = new Set();
				this.canvas = canvas;
				if ('setAttribute' in canvas) {
						canvas.setAttribute('data-engine', `PlayCanvas ${version}`);
				}
				this.initOptions = {
						...options
				};
				this.initOptions.alpha ??= true;
				this.initOptions.depth ??= true;
				this.initOptions.stencil ??= true;
				this.initOptions.antialias ??= true;
				this.initOptions.powerPreference ??= 'high-performance';
				this.initOptions.displayFormat ??= DISPLAYFORMAT_LDR;
				this._maxPixelRatio = platform.browser ? Math.min(1, window.devicePixelRatio) : 1;
				this.buffers = [];
				this._vram = {
						texShadow: 0,
						texAsset: 0,
						texLightmap: 0,
						tex: 0,
						vb: 0,
						ib: 0,
						ub: 0,
						sb: 0
				};
				this._shaderStats = {
						vsCompiled: 0,
						fsCompiled: 0,
						linked: 0,
						materialShaders: 0,
						compileTime: 0
				};
				this.initializeContextCaches();
				this._drawCallsPerFrame = 0;
				this._shaderSwitchesPerFrame = 0;
				this._primsPerFrame = [];
				for(let i = PRIMITIVE_POINTS; i <= PRIMITIVE_TRIFAN; i++){
						this._primsPerFrame[i] = 0;
				}
				this._renderTargetCreationTime = 0;
				this.scope = new ScopeSpace('Device');
				this.textureBias = this.scope.resolve('textureBias');
				this.textureBias.setValue(0.0);
		}
		postInit() {
				const vertexFormat = new VertexFormat(this, [
						{
								semantic: SEMANTIC_POSITION,
								components: 2,
								type: TYPE_FLOAT32
						}
				]);
				const positions = new Float32Array([
						-1,
						-1,
						1,
						-1,
						-1,
						1,
						1,
						1
				]);
				this.quadVertexBuffer = new VertexBuffer(this, vertexFormat, 4, {
						data: positions
				});
		}
		initCapsDefines() {
				const { capsDefines } = this;
				capsDefines.clear();
				if (this.textureFloatFilterable) capsDefines.set('CAPS_TEXTURE_FLOAT_FILTERABLE', '');
				if (this.textureFloatRenderable) capsDefines.set('CAPS_TEXTURE_FLOAT_RENDERABLE', '');
		}
		destroy() {
				this.fire('destroy');
				this.quadVertexBuffer?.destroy();
				this.quadVertexBuffer = null;
				this.dynamicBuffers?.destroy();
				this.dynamicBuffers = null;
				this.gpuProfiler?.destroy();
				this.gpuProfiler = null;
		}
		onDestroyShader(shader) {
				this.fire('destroy:shader', shader);
				const idx = this.shaders.indexOf(shader);
				if (idx !== -1) {
						this.shaders.splice(idx, 1);
				}
		}
		postDestroy() {
				this.scope = null;
				this.canvas = null;
		}
		loseContext() {
				this.contextLost = true;
				this.backBufferSize.set(-1, -1);
				for (const texture of this.textures){
						texture.loseContext();
				}
				for (const buffer of this.buffers){
						buffer.loseContext();
				}
				for (const target of this.targets){
						target.loseContext();
				}
				this.gpuProfiler?.loseContext();
		}
		restoreContext() {
				this.contextLost = false;
				this.initializeRenderState();
				this.initializeContextCaches();
				for (const buffer of this.buffers){
						buffer.unlock();
				}
				this.gpuProfiler?.restoreContext?.();
		}
		toJSON(key) {
				return undefined;
		}
		initializeContextCaches() {
				this.vertexBuffers = [];
				this.shader = null;
				this.shaderValid = undefined;
				this.shaderAsyncCompile = false;
				this.renderTarget = null;
		}
		initializeRenderState() {
				this.blendState = new BlendState();
				this.depthState = new DepthState();
				this.cullMode = CULLFACE_BACK;
				this.vx = this.vy = this.vw = this.vh = 0;
				this.sx = this.sy = this.sw = this.sh = 0;
				this.blendColor = new Color(0, 0, 0, 0);
		}
		setStencilState(stencilFront, stencilBack) {}
		setBlendState(blendState) {}
		setBlendColor(r, g, b, a) {}
		setDepthState(depthState) {}
		setCullMode(cullMode) {}
		setRenderTarget(renderTarget) {
				this.renderTarget = renderTarget;
		}
		setVertexBuffer(vertexBuffer) {
				if (vertexBuffer) {
						this.vertexBuffers.push(vertexBuffer);
				}
		}
		clearVertexBuffer() {
				this.vertexBuffers.length = 0;
		}
		getIndirectDrawSlot() {
				return 0;
		}
		get indirectDrawBuffer() {
				return null;
		}
		getRenderTarget() {
				return this.renderTarget;
		}
		initRenderTarget(target) {
				if (target.initialized) return;
				const startTime = now();
				this.fire('fbo:create', {
						timestamp: startTime,
						target: this
				});
				target.init();
				this.targets.add(target);
				this._renderTargetCreationTime += now() - startTime;
		}
		draw(primitive, indexBuffer, numInstances, indirectSlot, first = true, last = true) {}
		_isBrowserInterface(texture) {
				return this._isImageBrowserInterface(texture) || this._isImageCanvasInterface(texture) || this._isImageVideoInterface(texture);
		}
		_isImageBrowserInterface(texture) {
				return typeof ImageBitmap !== 'undefined' && texture instanceof ImageBitmap || typeof HTMLImageElement !== 'undefined' && texture instanceof HTMLImageElement;
		}
		_isImageCanvasInterface(texture) {
				return typeof HTMLCanvasElement !== 'undefined' && texture instanceof HTMLCanvasElement;
		}
		_isImageVideoInterface(texture) {
				return typeof HTMLVideoElement !== 'undefined' && texture instanceof HTMLVideoElement;
		}
		resizeCanvas(width, height) {
				const pixelRatio = Math.min(this._maxPixelRatio, platform.browser ? window.devicePixelRatio : 1);
				const w = Math.floor(width * pixelRatio);
				const h = Math.floor(height * pixelRatio);
				if (w !== this.canvas.width || h !== this.canvas.height) {
						this.setResolution(w, h);
				}
		}
		setResolution(width, height) {
				this.canvas.width = width;
				this.canvas.height = height;
				this.fire(GraphicsDevice.EVENT_RESIZE, width, height);
		}
		update() {
				this.updateClientRect();
		}
		updateClientRect() {
				if (platform.worker) {
						this.clientRect.width = this.canvas.width;
						this.clientRect.height = this.canvas.height;
				} else {
						const rect = this.canvas.getBoundingClientRect();
						this.clientRect.width = rect.width;
						this.clientRect.height = rect.height;
				}
		}
		get width() {
				return this.canvas.width;
		}
		get height() {
				return this.canvas.height;
		}
		set fullscreen(fullscreen) {}
		get fullscreen() {
				return false;
		}
		set maxPixelRatio(ratio) {
				this._maxPixelRatio = ratio;
		}
		get maxPixelRatio() {
				return this._maxPixelRatio;
		}
		get deviceType() {
				return this._deviceType;
		}
		startRenderPass(renderPass) {}
		endRenderPass(renderPass) {}
		startComputePass(name) {}
		endComputePass() {}
		frameStart() {
				this.renderPassIndex = 0;
				this.renderVersion++;
		}
		frameEnd() {
				this.mapsToClear.forEach((map)=>map.clear());
				this.mapsToClear.clear();
		}
		computeDispatch(computes, name = 'Unnamed') {}
		getRenderableHdrFormat(formats = [
				PIXELFORMAT_111110F,
				PIXELFORMAT_RGBA16F,
				PIXELFORMAT_RGBA32F
		], filterable = true, samples = 1) {
				for(let i = 0; i < formats.length; i++){
						const format = formats[i];
						switch(format){
								case PIXELFORMAT_111110F:
										{
												if (this.textureRG11B10Renderable) {
														return format;
												}
												break;
										}
								case PIXELFORMAT_RGBA16F:
										if (this.textureHalfFloatRenderable) {
												return format;
										}
										break;
								case PIXELFORMAT_RGBA32F:
										if (this.isWebGPU && samples > 1) {
												continue;
										}
										if (this.textureFloatRenderable && (!filterable || this.textureFloatFilterable)) {
												return format;
										}
										break;
						}
				}
				return undefined;
		}
		validateAttributes(shader, vb0Format, vb1Format) {}
}

export { GraphicsDevice };
