import { Utils } from '../common/Utils'

import { BaseImageTexture } from './BaseImageTexture';

import { ImageWrapType, ImageMappingType } from '../const';

import { CubeTexture } from './CubeTexture';



/**
 * ImageTexture - 图像纹理
 * 
 * 用于加载图像资源并在样式中使用,支持从 URL、像素缓冲区或 Canvas 创建。
 * 
 * @class ImageTexture
 * @memberof THING
 * @extends THING.BaseImageTexture
 * @public
 * 
 * @example
 const texture = new THING.ImageTexture({
   url: 'https://example.com/texture.png',
 });
 */
class ImageTexture extends BaseImageTexture {



	/**

	 * 数据类型的纹理

	 * @typedef {object} PixelBuffer

	 * @property {*} data The pixel buffer data. 纹理数据(一般都是TypedArray)

	 * @property {number} width The buffer width. 纹理宽

	 * @property {number} height The buffer height. 纹理高

	 */



	/**

	 * Canvas类型的纹理

	 * @typedef {object} CanvasResource

	 * @property {HTMLCanvasElement} resource The pixel buffer data. Canvas对象

	 */



	/**

	 * 图像纹理,用于加载图像资源并在样式中使用。

	 * @param {object|PixelBuffer|CanvasResource} param 初始化参数,支持 url、数据类型的纹理 Buffer 和 Canvas 类型的纹理 Buffer

	 * @param {string} param.url 贴图资源的路径
	 * @constructor
	 * @public
	 */

	constructor(param = {}) {

		// Check whether it is cube texture.

		if (Utils.isArray(param) || Utils.isArray(param['url'])) {

			console.warn('Please create cube texture with THING.CubeTexture instead of THING.ImageTexture');

			return new CubeTexture(param);

		}



		super(param);

	}



	// #region Private



	/**

	 * Load texture resource.

	 * @param {object} param The texture param.

	 * @private

	 */

	onLoad(param) {

		// Parse arguments

		if (Utils.isString(param)) {

			param = { url: param };

		}

		else if (param.width !== undefined && param.height !== undefined) {

			param = { resource: param };

		}



		let url = param['url'];



		// Load from data(could be compressed image data)

		if (param['type'] && param['data']) {

			// We can keep url as key even though it come from data

			this._url = url;



			this._loadFromData(param);

		}

		// Load from url

		else if (url) {

			this._loadFromURL(param);

		}

		// Start to load from resource by data(pixels), width and height

		else {

			let wrapType = param['wrapType'];

			if (wrapType) {

				this.wrapType = wrapType;

			}

			else {

				this.wrapTypeS = Utils.parseValue(param['wrapTypeS'], this.wrapTypeS);

				this.wrapTypeT = Utils.parseValue(param['wrapTypeT'], this.wrapTypeT);

			}



			this.mappingType = Utils.parseValue(param['mappingType'], this.mappingType);

			this.flipY = Utils.parseValue(param['flipY'], this.flipY);



			// Save image resource directly

			let data = param['data'];

			if (data) {

				let width = param['width'];

				let height = param['height'];



				this._image = {

					width,

					height,

					data,

				};

			}

			else {

				this._image = Utils.parseValue(param['resource'], null);

			}



			// Only set loaded flag when image resource is ready

			if (this._image) {

				this.onNotifyComplete();

			}

		}

	}



	_loadFromURL(param) {

		let onLoad = Utils.parseValue(param['onLoad'], null);



		this._url = param['url'];

		this._updateUrl();



		// Set options

		if (param['wrapType']) {

			this.wrapType = param['wrapType'];

		}



		this.mappingType = Utils.parseValue(param['mappingType'], ImageMappingType.UV);

		this.flipY = Utils.parseValue(param['flipY'], true);



		Utils.loadImageFile(this._url,

			// Load

			(image) => {

				this._image = image;



				this.onFixupFromImage(image);



				if (onLoad) {

					onLoad({ image: this });

				}



				this.onNotifyComplete();

			},

			// Progress

			(ev) => {



			},

			// Error

			(ev) => {

				this.onTriggerErrorEvent(ev);

			},

			{

				flipY: this.flipY,

				premultiplyAlpha: this.premultiplyAlpha,

				generateMipmaps: this.generateMipmaps,

				extensions: Utils.getCurrentApp().view.getExtensions()

			}

		);

	}



	_loadFromData(param) {

		let { type, data } = param



		let onLoad = Utils.parseValue(param['onLoad'], null);



		this.wrapType = Utils.parseValue(param['wrapType'], ImageWrapType.Repeat);

		this.mappingType = Utils.parseValue(param['mappingType'], ImageMappingType.UV);

		this.flipY = Utils.parseValue(param['flipY'], true);



		Utils.loadImageFileFromData(type, data,

			// Load

			(image) => {

				this._image = image;



				this.onFixupFromImage(image);



				if (onLoad) {

					onLoad({ image: this });

				}



				this.onNotifyComplete();

			},

			// Progress

			(ev) => {



			},

			// Error

			(ev) => {

				Utils.error(ev);

				this.onTriggerErrorEvent(ev);

			},

			{

				extensions: Utils.getCurrentApp().view.getExtensions()

			}

		);

	}



	// #endregion



	// #region Overrides



	onDispose() {

		this._image = null;



		super.onDispose();

	}



	onCreateTextureResource() {

		// Some features do not support in compressed texture

		if (this._isCompressed(this._url)) {

			this.flipY = false;

			this.generateMipmaps = false;

		}



		let image = this._image;

		if (image) {

			// Image had been loaded

			return Utils.createObject('ImageTextureResource', { data: image, external: this.external, scene: this.app.scene.context, view: this.app.view });

		}

		else {

			// Image is still loading, we would replace it when it finished

			let textureResource = Utils.createObject('ImageTextureResource', { textureType: 'HTMLElement', external: this.external, scene: this.app.scene.context, view: this.app.view });



			this.waitForComplete().then((image) => {

				// Replace texture image

				textureResource.setImage(image.resource);

			}).catch(ev => {

				this.onTriggerErrorEvent(ev);

			});



			return textureResource;

		}

	}



	// #endregion





	// #region Accessors



	/**

	 * 贴图的 url 或者 base64 字符串

	 * @type {string}

	 * @public

	 * @readonly

	 */

	get src() {

		let image = this._image;

		if (image) {

			if (image.src) {

				return image.src;

			}

		}



		return this._url;

	}



	/**

	 * Set the dirty flag.

	 * @type {boolean}

	 * @private

	 */

	set dirty(value) {

		let textureResource = this.textureResource;

		if (!textureResource) {

			return;

		}



		textureResource.refresh();

	}



	/**

	 * 纹理的宽度(像素)

	 * @type {number}

	 * @public

	 * @readonly

	 */

	get width() {

		let resource = this._image;

		if (!resource) {

			return 0;

		}



		if (resource.width) {

			return resource.width;

		}

		else if (resource.complete !== undefined) {

			return resource.width;

		}

		else if (Utils.isArray(resource)) {

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

				if (resource[i].complete) {

					return resource[i].width;

				}

			}

		}



		return 0;

	}



	/**

	 * 纹理的高度(像素)

	 * @type {number}

	 * @public

	 * @readonly

	 */

	get height() {

		let resource = this._image;

		if (!resource) {

			return 0;

		}



		if (resource.height) {

			return resource.height;

		}

		else if (resource.complete !== undefined) {

			return resource.height;

		}

		else if (Utils.isArray(resource)) {

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

				if (resource[i].complete) {

					return resource[i].height;

				}

			}

		}



		return 0;

	}



	/**

	 * 设置/获取 纹理资源

	 * @type {*}

	 * @private

	 */

	get resource() {

		return this._image;

	}

	set resource(value) {

		this._image = value;



		if (this.textureResource) {

			this.textureResource.setImage(value);

		}



		this.onNotifyComplete();

	}



	/**

	 * 开启/禁用 y 轴翻转(y 轴镜像)

	 * @type {boolean}

	 * @public

	 */

	get flipY() {

		// Compressed texture do not support flipY

		if (this._isCompressed(this._url)) {

			return false;

		}



		return super.flipY;

	}

	set flipY(value) {

		// Compressed texture do not support flipY

		if (value && this._isCompressed(this._url)) {

			return;

		}



		super.flipY = value;

	}



	/**

	 * 是否生成 mipMap。如果开启,纹理的 minFilterType 会被自动改成 LinearMipmapLinear

	 * @type {boolean}

	 * @public

	 */

	get generateMipmaps() {

		// Compressed texture do not support generateMipmaps

		if (this._isCompressed(this._url)) {

			return false;

		}



		return super.generateMipmaps;

	}

	set generateMipmaps(value) {

		// Compressed texture do not support generateMipmaps

		if (value && this._isCompressed(this._url)) {

			return;

		}



		super.generateMipmaps = value;

	}



	// #endregion



	/**

	 * 是否是 ImageTexture

	 * @type {boolean}

	 */

	get isImageTexture() {

		return true;

	}



}



export { ImageTexture }