import DataMap from './DataMap.js';
import { Vector2, DepthTexture, DepthStencilFormat, UnsignedInt248Type } from 'three';

const _size = new Vector2();

class Textures extends DataMap {

	constructor( backend, info ) {

		super();

		this.backend = backend;
		this.info = info;

	}

	updateRenderTarget( renderTarget ) {

		const renderTargetData = this.get( renderTarget );
		const sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;

		const texture = renderTarget.texture;
		const size = this.getSize( texture );

		let depthTexture = renderTarget.depthTexture || renderTargetData.depthTexture;

		if ( depthTexture === undefined ) {

			depthTexture = new DepthTexture();
			depthTexture.format = DepthStencilFormat;
			depthTexture.type = UnsignedInt248Type;
			depthTexture.image.width = size.width;
			depthTexture.image.height = size.height;

		}

		if ( renderTargetData.width !== size.width || size.height !== renderTargetData.height ) {

			texture.needsUpdate = true;
			depthTexture.needsUpdate = true;

			depthTexture.image.width = size.width;
			depthTexture.image.height = size.height;

		}

		renderTargetData.width = size.width;
		renderTargetData.height = size.height;
		renderTargetData.texture = texture;
		renderTargetData.depthTexture = depthTexture;

		if ( renderTargetData.sampleCount !== sampleCount ) {

			texture.needsUpdate = true;
			depthTexture.needsUpdate = true;

			renderTargetData.sampleCount = sampleCount;

		}

		const options = { sampleCount };

		this.updateTexture( texture, options );
		this.updateTexture( depthTexture, options );

		// dispose handler

		if ( renderTargetData.initialized !== true ) {

			renderTargetData.initialized = true;

			// dispose

			const onDispose = () => {

				renderTarget.removeEventListener( 'dispose', onDispose );

				this._destroyTexture( texture );
				this._destroyTexture( depthTexture );

			};

			renderTarget.addEventListener( 'dispose', onDispose );

		}

	}

	updateTexture( texture, options = {} ) {

		const textureData = this.get( texture );
		if ( textureData.initialized === true && textureData.version === texture.version ) return;

		const isRenderTarget = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture;
		const backend = this.backend;

		if ( isRenderTarget && textureData.initialized === true ) {

			// it's an update

			backend.destroySampler( texture );
			backend.destroyTexture( texture );

		}

		//

		if ( isRenderTarget ) {

			backend.createSampler( texture );
			backend.createTexture( texture, options );

		} else {

			const needsCreate = textureData.initialized !== true;

			if ( needsCreate ) backend.createSampler( texture );

			if ( texture.version > 0 ) {

				const image = texture.image;

				if ( image === undefined ) {

					console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' );

				} else if ( image.complete === false ) {

					console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' );

				} else {

					if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) {

						backend.createTexture( texture, options );

						textureData.isDefaultTexture = false;

					}

					backend.updateTexture( texture );

				}

			} else {

				// async update

				backend.createDefaultTexture( texture );

				textureData.isDefaultTexture = true;

			}

		}

		// dispose handler

		if ( textureData.initialized !== true ) {

			textureData.initialized = true;

			//

			this.info.memory.textures ++;

			// dispose

			const onDispose = () => {

				texture.removeEventListener( 'dispose', onDispose );

				this._destroyTexture( texture );

				this.info.memory.textures --;

			};

			texture.addEventListener( 'dispose', onDispose );

		}

		//

		textureData.version = texture.version;

	}

	getSize( texture, target = _size ) {

		if ( texture.isCubeTexture ) {

			target.width = texture.image[ 0 ].width;
			target.height = texture.image[ 0 ].height;

		} else {

			target.width = texture.image.width;
			target.height = texture.image.height;

		}

		return target;

	}

	_destroyTexture( texture ) {

		this.backend.destroySampler( texture );
		this.backend.destroyTexture( texture );

		this.delete( texture );

	}

}

export default Textures;
