import { Utils } from '../common/Utils'
import { Marker } from './Marker';
import { ImageTexture } from '../resources/ImageTexture';
import { CanvasRenderer } from '../renderers/CanvasRenderer';
import { AlignType, ImageWrapType, FontWeight } from '../const';

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

/**
 * Label - 文字标注
 * 用于在 3D 场景中创建和管理文字标注,支持自定义字体、颜色、阴影和对齐方式。
 * @class Label
 * @memberof THING
 * @extends THING.Marker
 * @public
 * @example
 const label = new THING.Label({
   fontText: 'Hello ThingJS',
   fontSize: 16,
   fontColor: '#ffffff',
   position: [0, 2, 0],
 });
 */
class Label extends Marker {

	static canvasRenderer = null;
	/**
	 * 构造方法
	 * @public
	 * @param {object} param 参数对象。
	 * @param {string} param.fontText 文字内容
	 * @param {string} [param.fontType = 'Arial'] 字体
	 * @param {number} [param.fontSize=10] 字号
	 * @param {FontWeight} [param.fontWeight=THING.FontWeight.Normal] 字体粗细
	 * @param {number} [param.fontLineWidth=Infinity] 文本换行最大宽度
	 * @param {number} [param.fontLineHeight=0] 文本换行高度
	 * @param {number[]|string} [param.fontColor='#ffffff'] 文本颜色
	 * @param {number[]|string} [param.fontShadowColor='#000000'] 阴影颜色
	 * @param {number} [param.fontShadowAlpha=1.0] 阴影透明度
	 * @param {number} [param.fontShadowAngle=45] 阴影旋转角度
	 * @param {number} [param.fontShadowBlur=1] 阴影模糊系数
	 * @param {number} [param.fontShadowDistance=1] 阴影距离
	 * @param {AlignType} [param.fontAlignType=THING.AlignType.Center] 文本对齐方式
	 * @param {boolean} [param.richText=false] 是否开启富文本模式
	 * @param {string} [param.mode='defalt'] 文本渲染模式 包括 'sdf','default','msdf','msdf-woreker'
	 */
	constructor(param = {}) {
		super(param);

		// Renderer
		Label.canvasRenderer = Label.canvasRenderer || new CanvasRenderer();

		this[__.private] = {};
		let _private = this[__.private];

		// The font info
		_private.text = '';
		_private.fontInfo = {};

		_private.image = new ImageTexture();
		_private.image.wrapType = ImageWrapType.ClampToEdge;

		_private.fontInfo = {
			type: 'Arial',
			size: 10,
			lineWidth: +Infinity,
			lineHeight: 0, // 0 indicates use font size as default
			color: [1, 1, 1],
			shadowColor: [0, 0, 0],
			shadowAlpha: 1,
			shadowAngle: 45,
			shadowBlur: 1,
			shadowDistance: 1,
			alignType: AlignType.Center,
			richText: false,
			fontWeight: FontWeight.Normal,
			mode: 'default',
			paddingTop: 0,
			paddingBottom: 0,
			paddingLeft: 0,
			paddingRight: 0,
		};

		// Sync text
		_private.text = '' + Utils.parseValue(param['fontText'], _private.text);

		// Sync font info
		var fontInfo = _private.fontInfo;
		fontInfo.type = Utils.parseValue(param['fontType'], fontInfo.type);
		fontInfo.size = Utils.parseValue(param['fontSize'], fontInfo.size);
		fontInfo.lineWidth = Utils.parseValue(param['fontLineWidth'], fontInfo.lineWidth);
		fontInfo.lineHeight = Utils.parseValue(param['fontLineHeight'], fontInfo.lineHeight);
		fontInfo.color = Utils.parseColor(param['fontColor'], fontInfo.color);
		fontInfo.shadowColor = Utils.parseColor(param['fontShadowColor'], fontInfo.shadowColor);
		fontInfo.shadowAlpha = Utils.parseValue(param['fontShadowAlpha'], fontInfo.shadowAlpha);
		fontInfo.shadowAngle = Utils.parseValue(param['fontShadowAngle'], fontInfo.shadowAngle);
		fontInfo.shadowBlur = Utils.parseValue(param['fontShadowBlur'], fontInfo.shadowBlur);
		fontInfo.shadowDistance = Utils.parseValue(param['fontShadowDistance'], fontInfo.shadowDistance);
		fontInfo.alignType = Utils.parseValue(param['fontAlignType'], fontInfo.alignType);
		fontInfo.richText = Utils.parseValue(param['richText'], fontInfo.richText);
		fontInfo.fontWeight = Utils.parseValue(param['fontWeight'], fontInfo.fontWeight);
		fontInfo.mode = Utils.parseValue(param['mode'], fontInfo.mode);

		fontInfo.paddingTop = Utils.parseValue(param['paddingTop'], fontInfo.paddingTop);
		fontInfo.paddingBottom = Utils.parseValue(param['paddingBottom'], fontInfo.paddingBottom);
		fontInfo.paddingLeft = Utils.parseValue(param['paddingLeft'], fontInfo.paddingLeft);
		fontInfo.paddingRight = Utils.parseValue(param['paddingRight'], fontInfo.paddingRight);

		let background = param['background'];

		// 初始化 background,并从构造函数参数中提取值
		if (background) {
			_private.background = new LabelBackground(background, this);
		}

		// Set render order to high value for rendering in the end
		if (Utils.isNull(param['renderOrder'])) {
			this.renderOrder = 1000 * 1000;
		}

		this.autoFitBodyScale = Utils.parseValue(param['autoFitBodyScale'], true);
		this.scaleFactor = Utils.parseValue(param['scaleFactor'], 0.035);
		this._sdfRenderType = param['renderType'] || 'Sprite';

		// Seems we need to dirty here to load resources
		this.setDirty(true);
	}

	// #region Private

	// #endregion

	// #region Overrides

	onCopy(object) {
		super.onCopy(object);

		let _private = this[__.private];

		_private.text = object.fontText;
		Object.assign(_private.fontInfo, object.fontInfo);

		this.onRefresh();
	}

	onLoadResource(options, resolve, reject) {
		if (options.mode == 'sdf' || options.mode == 'msdf' || options.mode == 'msdf-worker') {
			this.creatNode(options, () => {
				resolve();
				this.onRefresh();
			});
			this.app.objectManager.addResizableObject(this);
		}
		else {
			super.onLoadResource(options, () => {
				this.onRefresh();
				resolve();
			}, reject);
		}
	}

	onRefresh() {
		if (this.destroyed) {
			return;
		}
		if (!this.loaded) {
			return;
		}

		let _private = this[__.private];
		if (_private.fontInfo.mode == 'sdf' || _private.fontInfo.mode == 'msdf' || _private.fontInfo.mode == 'msdf-worker') {
			let scale = 1;
			if (_private.scale) {
				scale = _private.scale[0];
			}
			const scaleFactor = this._scaleFactor;
			this.body.localScale = [scaleFactor, scaleFactor, 1];
			_private.fontInfo.renderType = this._sdfRenderType;
			if (this.bodyNode) {
				this.bodyNode.update({ ..._private.fontInfo, text: _private.text, scaleFactor: scaleFactor * scale }, (node) => {
					node.$boundingBox.selfBoundingBoxNeedsUpdate = true;

					this.setPivot(this.pivot);
				});
			}
			this.superOnRefresh();
			return;
		}
		else {
			// Update text
			if (_private.text) {
				// Update background
				let canvasRenderer = Label.canvasRenderer;
				canvasRenderer.drawText(_private.text, _private.fontInfo, _private.background);

				// Update image
				_private.image.resource = canvasRenderer.getImageData();
				// _private.image.resource = canvasRenderer.getContext();
				if (this.style.image != _private.image) {
					this.style.image = _private.image;
				}
			}

			super.onRefresh();
		}
	}

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

		let data = super.onExportExternalData(options) || {};

		if (_private.text) {
			data['fontText'] = _private.text;
		}
		const fontInfo = _private.fontInfo;
		if (fontInfo.type !== 'Arial') {
			data['fontType'] = fontInfo.type;
		}
		if (fontInfo.size !== 10) {
			data['fontSize'] = fontInfo.size;
		}
		if (fontInfo.lineWidth !== Infinity) {
			data['fontLineWidth'] = fontInfo.lineWidth;
		}
		if (fontInfo.lineHeight !== 0) {
			data['fontLineHeight'] = fontInfo.lineHeight;
		}
		if (fontInfo.color !== '#FFFFFF') {
			data['fontColor'] = fontInfo.color;
		}
		if (fontInfo.shadowColor !== '#000000') {
			data['fontShadowColor'] = fontInfo.shadowColor;
		}
		if (fontInfo.shadowAlpha !== 1) {
			data['fontShadowAlpha'] = fontInfo.shadowAlpha;
		}
		if (fontInfo.shadowAngle !== 45) {
			data['fontShadowAngle'] = fontInfo.shadowAngle;
		}
		if (fontInfo.shadowBlur !== 1) {
			data['fontShadowBlur'] = fontInfo.shadowBlur;
		}
		if (fontInfo.shadowDistance !== 1) {
			data['fontShadowDistance'] = fontInfo.shadowDistance;
		}
		if (fontInfo.alignType !== AlignType.Center) {
			data['fontAlignType'] = fontInfo.alignType;
		}
		if (fontInfo.richText !== false) {
			data['richText'] = fontInfo.richText;
		}
		if (fontInfo.fontWeight !== FontWeight.Normal) {
			data['fontWeight'] = fontInfo.fontWeight;
		}
		if (this.renderOrder !== 1000 * 1000) {
			data['renderOrder'] = this.renderOrder;
		}
		if (fontInfo.mode !== 'default') {
			data['mode'] = fontInfo.mode;
		}
		return data;
	}

	onCreateBodyNode(type) {
		let node;
		let _private = this[__.private];
		if (_private.fontInfo.mode == 'default') {
			node = super.onCreateBodyNode(type);
			this.loaded = true;
		}
		else {
			// let _private = this[__.private];
			// node = this.creatNode({ ..._private.fontInfo, fontText: _private.text, renderType: this._sdfRenderType });
		}
		return node;
	}

	onBeforeSetup(options) {
		if (options.mode == 'sdf' || options.mode == 'msdf' || options.mode == 'msdf-worker') {
			// this.creatNode(options);
			// this.app.objectManager.addResizableObject(this);
		}
		else {
			super.onBeforeSetup(options);
		}
	}


	creatNode(param, callback) {
		if (!param.lineHeight) {
			delete param.lineHeight;
		}

		let renderableNode = param['renderableNode'];
		if (renderableNode) {
			return;
		}
		let scale = 1;
		if (param.scale) {
			scale = param.scale[0];
		}
		let fontInfo = {
			type: 'Arial',
			shadowColor: [0, 0, 0],
			shadowAlpha: 1,
			shadowAngle: 45,
			shadowBlur: 1,
			shadowDistance: 1,
			fontColor: [1, 1, 1],
			fontText: 'sdf'
		}
		fontInfo.fontColor = Utils.parseColor(param['fontColor'], fontInfo.fontColor);
		fontInfo.type = Utils.parseValue(param['fontType'], fontInfo.type);

		fontInfo.shadowColor = Utils.parseColor(param['fontShadowColor'], fontInfo.shadowColor);
		fontInfo.shadowAlpha = Utils.parseValue(param['fontShadowAlpha'], fontInfo.shadowAlpha);
		fontInfo.shadowAngle = Utils.parseValue(param['fontShadowAngle'], fontInfo.shadowAngle);
		fontInfo.shadowBlur = Utils.parseValue(param['fontShadowBlur'], fontInfo.shadowBlur);
		fontInfo.shadowDistance = Utils.parseValue(param['fontShadowDistance'], fontInfo.shadowDistance);
		fontInfo.fontText = Utils.parseValue(param['fontText'], fontInfo.fontText);
		let color = Utils.parseColor(param['fontColor'], [1, 1, 1]);
		renderableNode = Utils.createObject('Font', {
			app: this.app,
			renderType: 'Sprite', ...param, ...fontInfo, fontScale: 0.035 * scale, fontColor: color, callback: (node) => {
				let pivot = param['pivot'];
				if (pivot) {
					node.$boundingBox.selfBoundingBoxNeedsUpdate = true;
					this.setPivot(pivot);
				}
				this.loaded = true;
				callback(true)
			}
		},);
		this.setBodyNode(renderableNode);
		param['renderableNode'] = renderableNode;
		return renderableNode;
	}



	onResize(width, height) {
		let _private = this[__.private];
		if (_private.fontInfo.mode == 'sdf' || _private.fontInfo.mode == 'msdf' || _private.fontInfo.mode == 'msdf-worker') {
			if (this.bodyNode.updateSize) {
				this.bodyNode.updateSize(width, height);
			}
		}
		super.onResize(width, height);
	}

	_useSpriteCenterAsPivot() {
		let _private = this[__.private];
		if (_private) {
			if (_private.fontInfo.mode == 'sdf' || _private.fontInfo.mode == 'msdf' || _private.fontInfo.mode == 'msdf-worker') {
				return false;
			}
		}
		return super._useSpriteCenterAsPivot();
	}

	// #endregion

	/**
	 * 设置/获取 文本内容
	 * @type {string}
	 * @public
	 */
	get fontText() {
		let _private = this[__.private];

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

		_private.text = value;

		this.setDirty();
	}

	/**
	 * Get font info.
	 * @type {object}
	 * @private
	 */
	get fontInfo() {
		let _private = this[__.private];

		return _private.fontInfo;
	}

	/**
	 * 获取/设置 文本字体类型
	 * @type {string}
	 * @default 'Arial'
	 * @public
	 */
	get fontType() {
		let _private = this[__.private];

		return _private.fontInfo.type;
	}
	set fontType(value) {
		let _private = this[__.private];

		_private.fontInfo.type = value;

		this.setDirty();
	}

	/**
	 * 获取/设置 文本尺寸大小
	 * @type {number}
	 * @public
	 */
	get fontSize() {
		let _private = this[__.private];

		return _private.fontInfo.size;
	}
	set fontSize(value) {
		let _private = this[__.private];

		_private.fontInfo.size = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本换行最大宽度,单位:像素。
	 * @type {number}
	 * @public
	 */
	get fontLineWidth() {
		let _private = this[__.private];

		return _private.fontInfo.lineWidth;
	}
	set fontLineWidth(value) {
		let _private = this[__.private];

		_private.fontInfo.lineWidth = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本换行高度,单位:像素。0 表示自动调整尺寸
	 * @type {number}
	 * @public
	 */
	get fontLineHeight() {
		let _private = this[__.private];

		return _private.fontInfo.lineHeight;
	}
	set fontLineHeight(value) {
		let _private = this[__.private];

		_private.fontInfo.lineHeight = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本颜色
	 * @type {number|string|Array<number>}
	 * @public
	 */
	get fontColor() {
		let _private = this[__.private];

		return Utils.colorToHexString(_private.fontInfo.color);
	}
	set fontColor(value) {
		let _private = this[__.private];

		_private.fontInfo.color = Utils.parseColor(value);

		this.setDirty();
	}

	/**
	 * 获取/设置文本阴影颜色
	 * @type {number|string|Array<number>}
	 * @public
	 */
	get fontShadowColor() {
		let _private = this[__.private];

		return Utils.colorToHexString(_private.fontInfo.shadowColor);
	}
	set fontShadowColor(value) {
		let _private = this[__.private];

		_private.fontInfo.shadowColor = Utils.parseColor(value);

		this.setDirty();
	}

	/**
	 * 获取/设置文本阴影透明度。
	 * @type {number}
	 * @public
	 */
	get fontShadowAlpha() {
		let _private = this[__.private];

		return _private.fontInfo.shadowAlpha;
	}
	set fontShadowAlpha(value) {
		let _private = this[__.private];

		_private.fontInfo.shadowAlpha = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本阴影角度。
	 * @type {number}
	 * @public
	 */
	get fontShadowAngle() {
		let _private = this[__.private];

		return _private.fontInfo.shadowAngle;
	}
	set fontShadowAngle(value) {
		let _private = this[__.private];

		_private.fontInfo.shadowAngle = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本阴影模糊系数
	 * @type {number}
	 * @public
	 */
	get fontShadowBlur() {
		let _private = this[__.private];

		return _private.fontInfo.shadowBlur;
	}
	set fontShadowBlur(value) {
		let _private = this[__.private];

		_private.fontInfo.shadowBlur = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本阴影距离
	 * @type {number}
	 * @public
	 */
	get fontShadowDistance() {
		let _private = this[__.private];

		return _private.fontInfo.shadowDistance;
	}
	set fontShadowDistance(value) {
		let _private = this[__.private];

		_private.fontInfo.shadowDistance = value;

		this.setDirty();
	}

	/**
	 * 获取/设置文本对齐方式
	 * @type {AlignType}
	 * @public
	 */
	get fontAlignType() {
		let _private = this[__.private];

		return _private.fontInfo.alignType;
	}
	set fontAlignType(value) {
		let _private = this[__.private];

		_private.fontInfo.alignType = value;

		this.setDirty();
	}

	/**
	 * 开启/关闭富文本模式
	 * @type {boolean}
	 * @public
	 */
	get richText() {
		let _private = this[__.private];

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

		_private.fontInfo.richText = value;

		this.setDirty();
	}

	get isLabel() {
		return true;
	}

	/**
	 * 获取/设置字体粗细
	 * @type {FontWeight}
	 * @public
	 */
	get fontWeight() {
		let _private = this[__.private];

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

		_private.fontInfo.fontWeight = value;

		this.setDirty();
	}

	set renderType(value) {
		let _private = this[__.private];
		if (_private.fontInfo.mode == 'default') {
			super.renderType = value;
		}
		else {
			this._sdfRenderType = value;
			this[__.private].fontInfo.renderType = value;
			this.setDirty();
		}
	}
	get renderType() {
		return super.renderType;
	}
	get mode() {
		let _private = this[__.private];
		return _private.fontInfo.mode;
	}

	/**
	 * 获取/设置上内边距
	 * @type {number}
	 * @public
	 */
	get paddingTop() {
		let _private = this[__.private];

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

		_private.fontInfo.paddingTop = value;

		this.setDirty();
	}

	/**
	 * 获取/设置下内边距
	 * @type {number}
	 * @public
	 */
	get paddingBottom() {
		let _private = this[__.private];

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

		_private.fontInfo.paddingBottom = value;

		this.setDirty();
	}

	/**
	 * 获取/设置左内边距
	 * @type {number}
	 * @public
	 */
	get paddingLeft() {
		let _private = this[__.private];

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

		_private.fontInfo.paddingLeft = value;

		this.setDirty();
	}

	/**
	 * 获取/设置右内边距
	 * @type {number}
	 * @public
	 */
	get paddingRight() {
		let _private = this[__.private];

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

		_private.fontInfo.paddingRight = value;

		this.setDirty();
	}

	/**
	 * 获取/设置背景
	 * @type {LabelBackground}
	 * @public
	 */
	get background() {
		let _private = this[__.private];

		return _private.background;
	}

	set background(value) {
		let _private = this[__.private];

		if (value instanceof LabelBackground) {
			_private.background = value;
		}
		else if (typeof value === 'object' && value !== null) {
			_private.background = new LabelBackground(value);
		}
		else {
			console.error('Invalid value for background');
		}
	}

}

export { Label }


class LabelBackground {

	constructor({ color = "white", opacity = 1, cornerRadius = 0, border } = {}, label) {
		this._color = color;
		this._opacity = opacity;
		this._cornerRadius = cornerRadius;
		this._border = border ? new LabelBackgroundBorder(border, label) : undefined;

		this._label = label;
	}

	// Getter and Setter for color
	get color() {
		return this._color;
	}

	set color(value) {
		this._color = value;
		this._label.setDirty();
	}

	// Getter and Setter for opacity
	get opacity() {
		return this._opacity;
	}

	set opacity(value) {
		if (typeof value === 'number') {
			this._opacity = value;
			this._label.setDirty();
		}
		else {
			console.error('Invalid value for background opacity');
		}
	}

	// Getter and Setter for cornerRadius
	get cornerRadius() {
		return this._cornerRadius;
	}

	set cornerRadius(value) {
		if (typeof value === 'number') {
			this._cornerRadius = value;
			this._label.setDirty();
		}
		else {
			console.error('Invalid value for cornerRadius');
		}
	}

	// Getter and Setter for border
	get border() {
		return this._border;
	}

	set border(value) {
		if (value instanceof LabelBackgroundBorder) {
			this._border = value;
		}
		else if (typeof value === 'object' && value !== null) {
			this._border = new LabelBackgroundBorder(value);
		}
		else {
			console.error('Invalid value for border');
		}
	}

}

class LabelBackgroundBorder {

	constructor({ color = "white", opacity = 1, width = 1 } = {}, label) {
		this._color = color;
		this._opacity = opacity;
		this._width = width;

		this._label = label;
	}

	// Getter and Setter for color
	get color() {
		return this._color;
	}

	set color(value) {
		this._color = value;
		this._label.setDirty();
	}

	// Getter and Setter for opacity
	get opacity() {
		return this._opacity;
	}

	set opacity(value) {
		if (typeof value === 'number') {
			this._opacity = value;
			this._label.setDirty();
		}
		else {
			console.error('Invalid value for border opacity');
		}
	}

	// Getter and Setter for width
	get width() {
		return this._width;
	}

	set width(value) {
		if (typeof value === 'number') {
			this._width = value;
			this._label.setDirty();
		}
		else {
			console.error('Invalid value for border width');
		}
	}

}