import { StringEncoder } from '@uino/base-thing';

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

import { Object3D } from './Object3D';

import { RenderType } from '../const';



const __ = {

	private: Symbol('private'),

}



let _scale = [1, 1, 1];



const _spriteTypeName = StringEncoder.toText("<@secret Sprite>");



const registerComponentParam = { isResident: true };



/**
 * WebView - WebView 对象
 * 
 * 可以在场景中显示为可渲染的平面,支持交互和自定义分辨率。
 * 
 * @class WebView
 * @memberof THING
 * @extends THING.Object3D
 * @public
 * 
 * @example
 const webview = new THING.WebView({
   url: 'https://www.uino.com',
   domWidth: 800,
   domHeight: 600,
   position: [0, 1.5, -3],
 });
 */
class WebView extends Object3D {



	/**

	 * webview对象,可以在场景中显示为可渲染的平面。

	 * @param {object} param 初始参数。

	 * @param {string} [param.url=''] - WebView 的 URL。

	 * @param {number} [param.domWidth=1920] - WebView 的 DOM 宽度(像素)。

	 * @param {number} [param.domHeight=1080] - WebView 的 DOM 高度(像素)。

	 * @param {boolean} [param.interactive=false] - 是否启用交互功能。

	 * @param {RenderType} [param.renderType=THING.RenderType.Plane] - 渲染类型。

	 * @param {number|number[]} [param.domScale] - DOM 缩放比例。可以是单个数值或长度为2的数组。
	 * @constructor
	 * @public
	 */

	constructor(param = {}) {

		super(param);



		this[__.private] = {};

		let _private = this[__.private];



		_private.iframeComponent = null;

		_private.domScale = [1, 1];



		_private.style = null;



		_private.regionNode = null;



		_private.renderableNode = null;

		_private.renderableNodeOpacity = _WEB_VIEW_DEFAULT_OPACITY_;



		// Add web view component

		_private.iframeComponent = Utils.createObject('WebViewComponent', { app: this.app });

		this.addComponent(_private.iframeComponent, 'iframe', registerComponentParam);



		// Setup web view component

		_private.iframeComponent.url = Utils.parseValue(param['url'], '');

		_private.iframeComponent.domWidth = Utils.parseValue(param['domWidth'], 1920);

		_private.iframeComponent.domHeight = Utils.parseValue(param['domHeight'], 1080);

		_private.iframeComponent.interactive = Utils.parseValue(param['interactive'], false);

		_private.iframeComponent.enableEventProxy = true;



		// Register web view events

		this._registerEvents();



		// Create style for digging region

		let style = Utils.createObject('Style', { scene: this.app.scene.context, view: this.app.view });

		style.setTransparent(false);

		style.setSideType('Double');

		style.setBlendingType('NoBlending');

		style.setOpacityOp(() => { return _private.renderableNodeOpacity; });

		style.setColorOp(() => { return [0, 0, 0]; });

		_private.style = style;



		// Create region node to body

		_private.regionNode = Utils.createObject('Node', { app: this.app });

		this.bodyNode.add(_private.regionNode);



		// Dig a renderable region to show web view by plane

		this._createRenderableNode();



		// Update attributes

		this.renderType = Utils.parseValue(param['renderType'], RenderType.Plane);

		if (param['domScale']) {

			this.domScale = param['domScale'];

		}

	}



	hasResource() {

		return false;

	}



	// #region Private Functions



	_updateRgionNodeScale() {

		let _private = this[__.private];



		if (Utils.isArray(_private.domScale)) {

			_scale[0] = _private.domScale[0];

			_scale[1] = _private.domScale[1];

		}

		else {

			_scale[0] = this.domWidth * _private.domScale;

			_scale[1] = this.domHeight * _private.domScale;

		}



		_private.regionNode.setScale(_scale);

	}



	_createRenderableNode() {

		let _private = this[__.private];



		// We must wait for global cache finished

		this.app.global.waitForComplete().then(() => {

			if (_private.renderableNode) {

				_private.renderableNode.dispose();

				_private.renderableNode = null;

			}



			if (this.renderType == RenderType.Plane) {

				_private.renderableNode = this.app.global.cache.models['plane'].clone();

			}

			else {

				_private.renderableNode = Utils.createObject(_spriteTypeName, { app: this.app });

			}



			// Setup style

			_private.renderableNode.setAttribute('SkipParentStyleChange', true); // Ignore parent style change

			_private.renderableNode.setStyle(_private.style);



			// Make renderable region a little smaller to prevent 'white' edge

			const scaleFactor = 0.995;

			_private.renderableNode.setScale([scaleFactor, scaleFactor, scaleFactor]);



			// Render plane to dig region(hole)

			_private.renderableNode.setVisible(true);

			_private.regionNode.add(_private.renderableNode);



			// Update scale

			this._updateRgionNodeScale();



			// Update region node for web view component

			_private.iframeComponent.regionNode = _private.regionNode;

		});

	}



	_registerEvents() {

		// Due to interactive web view would catch all events, so we must stop camera control when move into it

		this.on('mousemove', (ev) => {

			this.app.camera.control.stop();

		});

	}



	// #endregion



	/**

	 * Get renderable node opacity.

	 * @returns {number} 返回渲染节点的透明度。

	 * @private

	 */

	getRenderableNodeOpacity() {

		let _private = this[__.private];



		return _private.renderableNodeOpacity;

	}



	/**

	 * Set renderable node opacity.

	 * @param {number} value The opacity value.

	 * @private

	 */

	setRenderableNodeOpacity(value) {

		let _private = this[__.private];



		_private.renderableNodeOpacity = value;



		if (_private.renderableNode) {

			_private.renderableNode.getStyle().setOpacityOp(() => {

				return _private.renderableNodeOpacity;

			});

		}

	}



	// #region Accessor



	/**

	 * Get the region node.

	 * @type {object}

	 * @private

	 */

	get regionNode() {

		let _private = this[__.private];



		return _private.regionNode;

	}



	/**

	 * Get the renderable node.

	 * @type {object}

	 * @private

	 */

	get renderableNode() {

		let _private = this[__.private];



		return _private.renderableNode;

	}



	/**

	 * Get the iframe component.

	 * @type {object}

	 * @private

	 */

	get iframeComponent() {

		let _private = this[__.private];



		return _private.iframeComponent;

	}



	/**

	 * 获取/设置渲染类型。

	 * @type {RenderType}

	 * @public

	 */

	get renderType() {

		let _private = this[__.private];



		return _private.iframeComponent.renderType;

	}

	set renderType(value) {

		let _private = this[__.private];



		if (_private.iframeComponent.renderType == value) {

			return;

		}



		_private.iframeComponent.renderType = value;



		this._createRenderableNode();

	}



	/**

	 * 获取/设置URL。

	 * @type {string}

	 * @public

	 */

	get url() {

		let _private = this[__.private];



		return _private.iframeComponent.url;

	}

	set url(value) {

		let _private = this[__.private];



		_private.iframeComponent.url = value;

	}



	/**

	 * 获取/设置以像素为单位的DOM宽度。

	 * @type {number}

	 * @public

	 */

	get domWidth() {

		let _private = this[__.private];



		return _private.iframeComponent.domWidth;

	}

	set domWidth(value) {

		let _private = this[__.private];



		_private.iframeComponent.domWidth = value;

	}



	/**

	 * 获取/设置以像素为单位的DOM高度。

	 * @type {number}

	 * @public

	 */

	get domHeight() {

		let _private = this[__.private];



		return _private.iframeComponent.domHeight;

	}

	set domHeight(value) {

		let _private = this[__.private];



		_private.iframeComponent.domHeight = value;

	}



	/**

	 * 获取/设置DOM缩放比例。

	 * @type {number|Array<number>}

	 * @example

	 * 	// 保持尺寸比例

	 * 	webView.domScale = 0.01;

	 * 	// 只设置宽度和高度,不保持尺寸比例

	 * 	webView.domScale = [2, 3];

	 * @public

	 */

	get domScale() {

		let _private = this[__.private];



		return _private.domScale;

	}

	set domScale(value) {

		let _private = this[__.private];



		if (Utils.isArray(value)) {

			_private.domScale = value.slice(0);

		}

		else {

			_private.domScale = value;

		}



		if (_private.regionNode) {

			this._updateRgionNodeScale();

		}

	}



	/**

	 * 启用/禁用交互功能。

	 * @type {boolean}

	 * @public

	 */

	get interactive() {

		let _private = this[__.private];



		return _private.iframeComponent.interactive;

	}

	set interactive(value) {

		let _private = this[__.private];



		_private.iframeComponent.interactive = value;

	}



	// #endregion



	get isWebView() {

		return true;

	}



}



export { WebView }