import { ObjectProxy } from '@uino/base-thing';
import { Utils } from '../common/Utils'
import { BaseLight } from './BaseLight';
import { ShadowLightTransformComponent } from '../components/ShadowLightTransformComponent';
import { ShadowQualityType } from '../const';

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

const registerComponentParam = { isResident: true };

/**
 * @class BaseShadowLight
 * 可产生阴影的光
 * @memberof THING
 * @extends THING.BaseLight
 * @public
 */
class BaseShadowLight extends BaseLight {

	/**
	 * 基础阴影光源类,负责管理可产生阴影的光源属性和行为
	 * @param {object} param 初始化参数列表
	 * @param {number|string|Array<number>} param.color 光的颜色
	 * @param {number} param.intensity 光的强度
	 * @param {boolean} param.castShadow 开启/关闭阴影效果
	 * @param {number} param.shadowQuality 阴影效果品质(2的n次幂的一个数值,数值越大质量越高)
	 * @param {number} param.shadowBias 阴影偏移量
	 * @param {ShadowRangeInfo} param.shadowRange 阴影范围
	 * @param {number[]} param.position 灯光位置
	 * @param {number[]} param.target 灯光的目标位置 根据position和target可确定平行光方向
	 */
	constructor(param = {}) {
		super(param);

		this[__.private] = {};


		// Set the default values
		if (Utils.isNull(param['shadowQuality'])) {
			this.shadowQuality = ShadowQualityType.High;
		}

		// Sync options
		Utils.syncOptions(this, [
			'enableShadow',
			'castShadow',
			'shadowQuality',
			'shadowBias',
			'shadowNormalBias',
			'shadowRadius',
			'shadowRange',
			'target'
		], param, param['extras'] || param['external']);

		// Refresh
		this.onRefresh();
	}

	// #region Object3D Overrides

	onSetupAttributes(param) {
		super.onSetupAttributes(param);

		this._shadowRange = new ObjectProxy({
			data: {
				width: 64,
				height: 64,
				near: 0.5,
				far: 1000
			},
			onChange: (ev) => {
				this.bodyNode.setShadowRange(ev.data);
			}
		});
	}

	onDestroy() {
		this._shadowRange.dispose();
		this._shadowRange = null;

		super.onDestroy();
	}

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

		this.castShadow = object.castShadow;
		this.shadowQuality = object.shadowQuality;
		this.shadowBias = object.shadowBias;
		this.shadowNormalBias = object.shadowNormalBias;
		this.shadowRadius = object.shadowRadius;
		this.shadowRange = object.shadowRange;
		this.target = object.target;
	}

	onRefresh() {
		super.onRefresh();

		this._shadowRange.notifyChange();

		this.bodyNode.setShadowNormalBias(this.shadowNormalBias);
	}

	onSetupComponent(param) {
		super.onSetupComponent(param);

		this.addComponent(ShadowLightTransformComponent, 'transform', registerComponentParam);
	}

	onUpdate(deltaTime) {
		super.onUpdate(deltaTime);

		this.bodyNode.updateShadow();
	}

	onImportExternalData(external, options) {
		super.onImportExternalData(external, options);
		this.castShadow = Utils.parseValue(external['castShadow'], this.castShadow);
		this.shadowQuality = Utils.parseValue(external['shadowQuality'], this.shadowQuality);
		this.shadowBias = Utils.parseValue(external['shadowBias'], this.shadowBias);
		this.shadowNormalBias = Utils.parseValue(external['shadowNormalBias'], this.shadowNormalBias);
		this.shadowRadius = Utils.parseValue(external['shadowRadius'], this.shadowRadius);
		this.shadowRange = Utils.parseValue(external['shadowRange'], this.shadowRange);
	}

	onExportExternalData() {
		let external = Object.assign({}, super.onExportExternalData());
		Utils.setAttributeIfExist(external, 'castShadow', this);
		Utils.setAttributeIfExist(external, 'shadowQuality', this);
		Utils.setAttributeIfExist(external, 'shadowBias', this);
		Utils.setAttributeIfExist(external, 'shadowNormalBias', this);
		Utils.setAttributeIfExist(external, 'shadowRadius', this);
		Utils.setAttributeIfExist(external, 'shadowRange', this);
		return external;
	}

	// #endregion

	// #region Accessor

	/**
	 * Enable/Disable shadow.
	 * @type {boolean}
	 * @deprecated 2.7
	 * @private
	 */
	get enableShadow() {
		Utils.warn('Please use castShadow instead');

		return this.castShadow;
	}
	set enableShadow(value) {
		Utils.warn('Please use castShadow instead');

		this.castShadow = value;
	}

	/**
	 * 是否开启阴影
	 * @type {boolean}
	 * @public
	 */
	get castShadow() {
		return this.bodyNode.getCastShadow();
	}
	set castShadow(value) {
		if (this.castShadow == value) {
			return;
		}

		this.bodyNode.setCastShadow(value);
	}

	/**
	 * 获取/设置阴影效果品质
	 * @type {ShadowQualityType|Number}
	 * @public
	 */
	get shadowQuality() {
		return this.bodyNode.getShadowQuality();
	}
	set shadowQuality(value) {
		this.bodyNode.setShadowQuality(value);
	}

	/**
	 * 获取/设置阴影偏移量
	 * @type {number}
	 * @public
	 */
	get shadowBias() {
		return this.bodyNode.getShadowBias();
	}
	set shadowBias(value) {
		this.bodyNode.setShadowBias(value);
	}

	/**
	 * 获取/设置阴影范围
	 * @type {ShadowRangeInfo}
	 * @public
	 */
	get shadowNormalBias() {
		return this.bodyNode.getShadowNormalBias();
	}
	set shadowNormalBias(value) {
		this.bodyNode.setShadowNormalBias(value);
	}

	/**
	 * Get/Set shadow radius, default is 1 (not working yet).
	 * @type {number}
	 * @private
	 */
	get shadowRadius() {
		return this.bodyNode.getShadowRadius();
	}
	set shadowRadius(value) {
		this.bodyNode.setShadowRadius(value);
	}

	/**
	 * 阴影范围
	 * @typedef {object} ShadowRangeInfo
	 * @property {number} width 宽度
	 * @property {number} height 高度
	 * @property {number} near 近
	 * @property {number} far 远
	 * @public
	 */

	/**
	 * 获取/设置阴影范围。
	 * @type {ShadowRangeInfo}
	 * @public
	 */
	get shadowRange() {
		return this._shadowRange.dataProxy;
	}
	set shadowRange(value) {
		this._shadowRange.merge(value);
	}

	// #endregion

	// #region Internal components attributes and functions

	// #region Component - animation

	/**
	 * 平行光看向的位置,根据target和自身的position可以确定朝向
	 * @public
	 * @type {number[]}
	 */
	get target() { return this.transform.target; }

	set target(value) { this.transform.target = value; }

	// #endregion

	// #endregion

	/**
	 * 检查是否为BaseShadowLight类型或继承自它。
	 * @type {boolean}
	 * @public
	 */
	get isBaseShadowLight() {
		return true;
	}

}

export { BaseShadowLight }