Source: core/Logo.js

import { StringEncoder, Timer } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';

const __ = {
	private: Symbol('private'),
}

const _spriteTypeName = StringEncoder.toText("<@secret Sprite>");
const _spritesName = StringEncoder.toText("<@secret sprites>");
const _watermarkEvent = StringEncoder.toText("<@secret __!water#mark*event@__>");
const _hideWatermarkEvent = StringEncoder.toText("<@secret __!hide!water#mark*event@__>");

/**
 * @class Logo
 * The logo.
 * @memberof THING
 */
class Logo {

	/**
	 * The logo controller.
	 */
	constructor() {
		this[__.private] = {};
		let _private = this[__.private];

		_private.timer = new Timer({
			interval: 500,
			onInterval: () => {
				this._check();
			}
		});

		_private.options = {};
	}

	// #region Private

	_createWatermarkStyle(text) {
		let _private = this[__.private];

		let renderer = Utils.createObject('CanvasRenderer');
		renderer.resize(512, 512);

		let options = {
			size: 24,
			type: 'Verdana',
			color: [1, 1, 1],
			shadowColor: [0, 0, 0],
			shadowAngle: 30,
			shadowDistance: 2,
		};

		let result = renderer.getTextRows(text, options);
		if (!result) {
			renderer.dispose();
			return null;
		}

		renderer.resize(result.size[0], result.size[1]);
		renderer.drawText(result, options);

		let canvas = renderer.getContext();

		let style = Utils.createObject('Style');
		style.setOpacityOp(() => { return _PRO_ ? 0.05 : 0.08; });
		style.setImage('Map', Utils.createObject('ImageTextureResource', { data: canvas }));

		// Update options
		_private.options['imageWidth'] = canvas.width;
		_private.options['imageHeight'] = canvas.height;

		renderer.dispose();

		return style;
	}

	_removeWatermarkSprites() {
		let sprites = this[_spritesName];
		if (Utils.isArray(sprites)) {
			sprites.forEach(sprite => {
				sprite.dispose();
			});

			sprites.length = 0;
		}
	}

	_createWatermarkSprites(rootNode, style, size, screenSize, imageSize) {
		let width = screenSize[0];
		let height = screenSize[1];
		let spriteWidth = size[0];
		let spriteHeight = size[1];

		// Get image size
		let imageWidth = imageSize[0];
		let imageHeight = imageSize[1];

		// Prepare to create sprites
		this[_spritesName] = this[_spritesName] || [];
		let sprites = this[_spritesName];

		// Destroy previous sprites
		this._removeWatermarkSprites();

		// Hide sprite type to prevent cracker
		for (let y = -height; y < height; y += spriteHeight) {
			for (let x = -width; x < width; x += spriteWidth) {
				let sprite = Utils.createObject(_spriteTypeName);
				sprite.setScale([imageWidth, imageHeight, 1]);
				sprite.setPosition([x, y, -1]);
				sprite.setStyle(style);
				sprite.setAttribute('Rotation', MathUtils.degToRad(30));

				sprites.push(sprite);

				rootNode.add(sprite);
			}
		}
	}

	_initOptions(options) {
		let _private = this[__.private];

		let rootNode = options['rootNode'];
		let enable = Utils.parseValue(options['enable'], true);
		let text = options['text'] || StringEncoder.toText("<@secret ThingJS by UINO>");
		let width = options['width'] || 480;
		let height = options['height'] || 320;

		// Check text
		if (!text) {
			return;
		}

		// Create watermark style
		let style = this._createWatermarkStyle(text);
		if (!style) {
			return;
		}

		// Setup options
		_private.options['enable'] = enable;
		_private.options['rootNode'] = rootNode;
		_private.options['style'] = style;
		_private.options['width'] = width;
		_private.options['height'] = height;
	}

	_initEvents() {
		let _private = this[__.private];

		if (!_private.options['enable']) {
			return;
		}

		// Get the application
		let app = Utils.getCurrentApp();

		// Hook secret event to wait for watermark image update
		app.on(_watermarkEvent, async (ev) => {
			let { scaleFacotr, opacity, width, height, data, text } = ev;

			// Update image from text
			if (text) {
				// Create watermark style
				let style = this._createWatermarkStyle(text);
				if (!style) {
					return;
				}

				// Update the style image
				_private.options['style'] = style;

				// Get the app viewport size
				let app = Utils.getCurrentApp();
				let size = app.size;

				// Create sprites to show watermark
				let { rootNode, width, height, imageWidth, imageHeight } = _private.options;
				this._createWatermarkSprites(rootNode, style, [width, height], size, [imageWidth, imageHeight]);
			}
			// Update image from base64
			else if (data) {
				// Get the style image
				let style = _private.options['style'];

				// Get the texture image resource
				let textureResource = style.getImage('Map');
				if (!textureResource) {
					return;
				}

				// If it's base64 format then need to convert to image resource type
				if (Utils.isString(data)) {
					let image = await Utils.loadBitmapImageFromBase64Async(data);
					if (!image) {
						return;
					}

					textureResource.setImage(image);

					width = image.width;
					height = image.height;
				}
				else if (width && height) {
					textureResource.setImage({ width, height, data });
					textureResource.enable('FlipY', true);
				}

				// Get the sprite scale
				scaleFacotr = Utils.parseValue(scaleFacotr, 1);
				let scale = [width * scaleFacotr, height * scaleFacotr, 1];

				// Auto fit best size
				while (true) {
					if (scale[0] > 256 || scale[1] > 256) {
						scale[0] *= 0.5;
						scale[1] *= 0.5;
					}
					else {
						break;
					}
				}

				// Update sprite scale
				let rootNode = _private.options['rootNode'];
				rootNode.getChildren().forEach(sprite => {
					sprite.setScale(scale);
				});
			}

			// Update opacity value
			if (Utils.isNumber(opacity)) {
				// Get the style image
				let style = _private.options['style'];

				style.setOpacityOp(() => { return opacity; });
			}
		}, {
			enumerable: false
		});

		// Hide watermark
		app.on(_hideWatermarkEvent, (ev) => {
			this._removeWatermarkSprites();
		}, {
			enumerable: false
		});
	}

	_isVisible() {
		let _private = this[__.private];

		let rootNode = _private.options['rootNode'];
		while (rootNode) {
			if (!rootNode.getVisible()) {
				return false;
			}

			rootNode = rootNode.getParent();
		}

		return true;
	}

	_forceShow() {
		let _private = this[__.private];

		let rootNode = _private.options['rootNode'];
		while (rootNode) {
			// We do not use interface to show to prevent cracker hook it
			rootNode._visible = true;
			rootNode._node.visible = true;

			rootNode = rootNode.getParent();
		}
	}

	_check() {
		let _private = this[__.private];

		let options = _private.options;
		let enable = options['enable'];

		if (enable) {
			if (!this._isVisible()) {
				let rootNode = options['rootNode'];

				let children = rootNode.getChildren().slice(0);
				children.forEach(child => {
					child.dispose();
				});

				let style = options['style'];
				let width = options['width'];
				let height = options['height'];
				let imageWidth = options['imageWidth'];
				let imageHeight = options['imageHeight'];
				let screenSize = app.size;
				this._createWatermarkSprites(rootNode, style, [width, height], screenSize, [imageWidth, imageHeight]);

				this._forceShow();
			}
		}
	}

	// #endregion

	init(rootNode) {
		let _private = this[__.private];

		this._initOptions({ rootNode });
		this._initEvents();

		let app = Utils.getCurrentApp();
		let size = app.size;

		let { style, width, height, imageWidth, imageHeight } = _private.options;
		this._createWatermarkSprites(rootNode, style, [width, height], size, [imageWidth, imageHeight]);
	}

	update(deltaTime) {
		let _private = this[__.private];

		// Convert seconds -> milliseconds
		_private.timer.update(deltaTime * 1000);
	}

}

export { Logo }