Source: objects/WebView.js

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 };

/**
 * @class WebView
 * The webView object.
 * @memberof THING
 * @extends THING.Object3D
 * @public
 */
class WebView extends Object3D {

	/**
	 * The web view object that can show it as renderable plane in scene.
	 * @param {Object} param The initial parameters.
	 */
	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 = 0;

		// Add web view component
		_private.iframeComponent = Utils.createObject('WebViewComponent');
		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');
		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');
		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);
			}

			// 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;
	}

	/**
	 * Get/Set the render type.
	 * @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();
	}

	/**
	 * Get/Set 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;
	}

	/**
	 * Get/Set DOM width in pixel.
	 * @type {Number}
	 * @public
	 */
	get domWidth() {
		let _private = this[__.private];

		return _private.iframeComponent.domWidth;
	}
	set domWidth(value) {
		let _private = this[__.private];

		_private.iframeComponent.domWidth = value;
	}

	/**
	 * Get/Set DOM height in pixel.
	 * @type {Number}
	 * @public
	 */
	get domHeight() {
		let _private = this[__.private];

		return _private.iframeComponent.domHeight;
	}
	set domHeight(value) {
		let _private = this[__.private];

		_private.iframeComponent.domHeight = value;
	}

	/**
	 * Get/Set DOM scale(factor).
	 * @type {Number|Array<Number>}
	 * @example
	 * 	// Keep ratio of size
	 * 	webView.domScale = 0.01;
	 * 	// Just set width and height without keeping ratio of size
	 * 	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();
		}
	}

	/**
	 * Enable/Disable interactive.
	 * @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 }