import { MathUtils } from '@uino/base-thing';
import { BaseCSSComponent } from './BaseCSSComponent';

let _mat4 = MathUtils.createMat4();

/**
 * @class CSS3DComponent
 * CSS 3D 组件。
 * @memberof THING
 * @extends THING..BaseCSSComponent
 * @public
 */
class CSS3DComponent extends BaseCSSComponent {

	constructor(options) {
		super(options);

		this._renderType = 'Sprite';
		this._pivot = null;
		this._offset = [0, 0, 0];
		this._css3DNode = null;
		this._cssFactor = 1 / 30;

		this._waitingCommands = [];
		this._loadingTimer = null

		// 临时变量用于修复scrollWidth/Height不包含border尺寸的问题
		// 默认值为false用于兼容,后续可考虑废弃此变量
		this.fixBorder = true;
	}

	// #region Private

	// Update CSS 3D object scale.
	_updateCSS3DObjectScale() {
		let cssFactor = this._cssFactor;
		this._css3DNode.scale = [cssFactor, cssFactor, 1];
	}

	_updateCSS3DObjectQuaternion() {

	}

	_getDomElementWidth() {
		const domElement = this._domElement;
		let width = this.fixBorder ? domElement.offsetWidth : domElement.scrollWidth;
		return width || domElement.scrollWidth;
	}

	_getDomElementHeight() {
		const domElement = this._domElement;
		let height = this.fixBorder ? domElement.offsetHeight : domElement.scrollHeight;
		return height || domElement.scrollHeight;
	}

	_updateCSS3DObjectPositionByPivot(pivot, domWidth, domHeight) {
		let domElement = this._domElement;
		let css3DNode = this._css3DNode;
		// let scale = css3DNode.scale;
		let scaledWidth = domWidth;
		let scaledHeight = domHeight;
		let offsetX = (0.5 - pivot[0]) * scaledWidth;
		let offsetY = (0.5 - pivot[1]) * scaledHeight;

		domElement._setLeft(0);
		domElement._setTop(0);
		css3DNode.setAnchorMatrix(offsetX, offsetY);
	}

	_processWaitingCommands() {
		let waitingCommands = this._waitingCommands;
		if (!waitingCommands.length) {
			return;
		}

		if (!this._getDomElementWidth() || !this._getDomElementHeight()) {
			return;
		}

		waitingCommands.forEach(command => {
			command.onProcess();
		});

		waitingCommands.length = 0;
	}

	// #endregion

	// #region Overrides

	onRemove() {
		if (this._css3DNode) {
			this.onDisposeCSS3DNode(this._css3DNode);

			this._css3DNode = null;
		}

		super.onRemove();
	}

	onVisibleChange(value) {
		let css3DNode = this._css3DNode;
		if (!css3DNode) {
			return;
		}

		css3DNode.visible = value;
	}

	onRender() {
		let css3DNode = this._css3DNode;
		if (!css3DNode) {
			return;
		}

		// Get current visible state
		let visible = this._visible !== null ? this._visible : true;

		// Sync CSS 3D renderable node visible state
		css3DNode.visible = visible;

		// Auto update visible state
		if (this.updateDomElementVisibility(visible)) {
			let matrixWorld = this._object.node.getMatrixWorld(_mat4);
			matrixWorld[12] += this._offset[0];
			matrixWorld[13] += this._offset[1];
			matrixWorld[14] += this._offset[2];

			// Update parent matrix
			css3DNode.parentMatrix = matrixWorld;

			// Update CSS 3D object transform
			let domWidth = this._getDomElementWidth();
			let domHeight = this._getDomElementHeight();
			if (domWidth && domHeight) {
				// 姑且认为dom准备就绪后,50毫秒,node完成加载
				if (css3DNode.loading) {
					this._loadingTimer = setTimeout(() => {
						css3DNode.loading = false;
					}, 50)
				}
				else {
					if (this._loadingTimer) {
						clearTimeout(this._loadingTimer)
						this._loadingTimer = null
					}
				}
				this._updateCSS3DObjectScale();
				this._updateCSS3DObjectQuaternion();
				let pivot = this._pivot;
				if (pivot) {
					this._updateCSS3DObjectPositionByPivot(pivot, domWidth, domHeight);
				}
			}
			else {
				// Here we must prevent element is still in loading state
				if (css3DNode.visible) {
					const scale = 0.00000001;
					this._css3DNode.scale = [scale, scale, scale];
				}
			}
		}

		// Update waiting commands
		this._processWaitingCommands();
	}

	onCreateCSS3DNode() {
		let css3DNode = this._delegate.createCSS3DNode(this._renderType);
		css3DNode.loading = true;
		return css3DNode;
	}

	onDisposeCSS3DNode(node) {
		this._delegate.disposeCSS3DNode(node);
	}

	// #endregion

	/**
	 * 获取CSS3D节点。
	 * @type {THING.DOM.CSS3DNode}
	 * @public
	 */
	get css3DNode() {
		return this._css3DNode;
	}

	/**
	 * 获取/设置DOM元素。
	 * @type {object}
	 * @public
	 */
	get domElement() {
		return super.domElement;
	}
	set domElement(value) {
		super.domElement = value;

		if (this._css3DNode) {
			if (this._css3DNode.domElement != value) {
				this._css3DNode.domElement = value;

				this._delegate.css3DRenderer.removeCache(this._css3DNode);
			}
		}
		else {
			this._css3DNode = this.onCreateCSS3DNode();
			this._css3DNode.visible = this._object.visible;

			this._css3DNode.domElement = value;
		}
	}

	/**
	 * 获取/设置从左下角计算的轴心点。
	 * @type {Array<number>}
	 * @public
	 */
	get pivot() {
		return this._pivot ? this._pivot : [0.5, 0.5];
	}
	set pivot(value) {
		if (value) {
			this._pivot = value.slice(0);
		}
		else {
			this._pivot = null;

			let domWidth = this._getDomElementWidth();
			let domHeight = this._getDomElementHeight();
			if (domWidth && domHeight) {
				this._updateCSS3DObjectPositionByPivot([0.5, 0.5], domWidth, domHeight);
			}
		}
	}

	/**
	 * 获取/设置偏移量(以像素为单位)。
	 * @type {Array<number>}
	 * @public
	 */
	get offset() {
		return this._offset;
	}
	set offset(value) {
		if (value) {
			this._offset = value.slice(0);
		}
		else {
			this._offset = [0, 0, 0];
		}
	}

	/**
	 * 获取/设置缩放因子。
	 * @type {number}
	 * @public
	 */
	get factor() {
		return this._cssFactor;
	}
	set factor(value) {
		this._cssFactor = value;
	}

	/**
	 * 获取/设置渲染类型。
	 * @type {THING.RenderType}
	 * @public
	 */
	get renderType() {
		let css3DNode = this._css3DNode;
		if (css3DNode) {
			return css3DNode.renderType;
		}

		return this._renderType;
	}
	set renderType(value) {
		this._renderType = value;

		// Check whether finished load DOM element
		let domElement = this.domElement;
		if (!domElement) {
			this._waitingCommands.push({
				onProcess: () => {
					this.renderType = value;
				}
			});

			return;
		}

		// Update render type
		this._css3DNode.renderType = value;

		// We hide it first until element scroll width and height loaded
		let domWidth = this._getDomElementWidth();
		let domHeight = this._getDomElementHeight();
		if (!domWidth || !domHeight) {
			this._css3DNode.scale = [Infinity, Infinity, Infinity];
		}
	}

}

export { CSS3DComponent }