import { ObjectProxy } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { BaseComponent } from './BaseComponent';

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

/**
 * @class CameraPostEffectComponent
 * 屏幕级后期
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class CameraPostEffectComponent extends BaseComponent {

	/**
	 * 相机的渲染效果,可以在这里更改一些屏幕后期效果。
	 */
	constructor() {
		super();

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

		_private.needsRefresh = false;

		/**
		 * Temporal super sampling
		 * @typedef {object} TemporalSuperSampling
		 * @property {boolean} enable True indicates enable it.
		 * @property {number} size The max frames.
		 */

		_private.temporalSuperSampling = {
			enable: false,
			size: 30
		};

		/**
		 * 辉光效果
		 * @typedef {object} Bloom
		 * @property {boolean} enable 是否开启
		 * @property {number} strength 强度
		 * @property {number} radius 半径
		 * @property {number} threshold 阈值
		 * @public
		 */

		_private.bloom = {
			enable: false,
			strength: 0.14,
			radius: 0.4,
			threshold: 0.7
		};

		/**
		 * 屏幕空间环境光遮蔽配置
		 * @remarks 工作空间中的采样半径。<br>
		 * 较大的半径会产生更柔和的阴影。<br>
		 * 但是需要更高的质量,否则会有更明显的伪影
		 * @typedef {object} ScreenSpaceAmbientOcclusion
		 * @property {boolean} enable 是否开启
		 * @property {number} radius 半径
		 * @property {string} quality SSAO的质量。可选值为'low'|'medium'|'high'|'ultra'。
		 * @property {number} intensity 强度
		 * @property {boolean} temporalFilter 是否在时序超采样模式下使用滤波器。
		 * @property {boolean} ignoreTransparent 是否忽略透明物体。
		 * @public
		 */

		_private.screenSpaceAmbientOcclusion = {
			enable: false,
			radius: 0.2,
			quality: 'medium',
			intensity: 0.8,
			temporalFilter: true,
			ignoreTransparent: false
		};

		/**
		 * GTAO
		 * @typedef {object} GroundTruthAmbientOcclusion
		 * @property {boolean} enable 是否开启
		 * @property {boolean} multiBounce是否开启多次反弹 开启产生更加真实和逼真的环境光遮蔽效果,也会增加计算的复杂度和资源消耗
		 * @property {number} maxDistance 环境光遮蔽计算的最大距离
		 * @property {number} maxPixel 环境光遮蔽计算的最大像素数量
		 * @property {number} rayMarchSegment 光线追踪的段数
		 * @property {number} darkFactor 深度因子
		 * @public
		 */
		_private.groundTruthAmbientOcclusion = {
			enable: false,
			multiBounce: false,
			maxDistance: 10,
			maxPixel: 14,
			rayMarchSegment: 10,
			darkFactor: 1.
		}

		/**
		 * 屏幕空间反射配置
		 * @typedef {object} ScreenSpaceReflection
		 * @property {boolean} enable 开关
		 * @property {number} maxRayDistance 反射光线追踪的最大距离
		 * @property {number} pixelStride 步长
		 * @property {boolean} enablePixelStrideZCutoff 是否限制反射光线的最大距离,以避免对远处或过远的物体进行反射计算
		 * @property {number} falloff 屏幕空间反射效果的衰减函数或者衰减参数
		 * @property {number} strength	反射强度
		 * @property {number} pixelStrideZCutoff 屏幕空间反射计算中根据像素之间的距离进行截断
		 * @property {number} screenEdgeFadeStart 屏幕边缘开始淡化的距离
		 * @property {number} eyeFadeStart 屏幕空间反射效果在摄像机视角附近开始逐渐减弱的位置
		 * @property {number} eyeFadeEnd  屏幕空间反射效果在摄像机视角附近结束减弱的位置
		 * @property {number} minGlossiness 控制屏幕空间反射效果中反射光滑度的最小值,具有高光滑度的表面通常会产生清晰的反射,而具有低光滑度的表面则会产生模糊的反射。
		 * @public
		 */

		_private.screenSpaceReflection = {
			enable: false,
			maxRayDistance: 200,
			pixelStride: 16,
			enablePixelStrideZCutoff: false,
			falloff: 0,
			strength: 0.8,
			pixelStrideZCutoff: 50,
			screenEdgeFadeStart: 0.9,
			eyeFadeStart: 0.4,
			eyeFadeEnd: 0.8,
			minGlossiness: 0.2
		};

		/**
		 * 颜色校正配置
		 * @typedef {object} ColorCorrection
		 * @property {boolean} enable 是否开启
		 * @property {number} exposure 曝光度
		 * @property {number} brightness 亮度
		 * @property {number} contrast 对比度
		 * @property {number} saturation 饱和度
		 * @property {number} gamma 伽马值
		 * @public
		 */

		_private.colorCorrection = {
			enable: true,
			exposure: 0,
			brightness: 0,
			contrast: 1.1,
			saturation: 1.1,
			gamma: 1
		};

		/**
		 * 景深效果配置
		 * @typedef {object} Dof
		 * @property {boolean} enable 是否开启
		 * @property {number} focalDepth 相机焦点的深度位置
		 * @property {number} focalLength 焦距
		 * @property {number} fstop 光圈
		 * @property {number} maxblur 最大允许的模糊半径
		 * @property {number} threshold 阈值
		 * @property {number} gain 模糊效果的增益因子 越大越模糊
		 * @property {number} bias 偏差 可以改变焦点周围模糊区域的过渡方式
		 * @property {number} dithering 抖动 改善模糊边缘的过渡效果
		 * @public
		 */

		_private.dof = {
			enable: false,
			focalDepth: 1,
			focalLength: 24,
			fstop: 0.9,
			maxblur: 1.0,
			threshold: 0.9,
			gain: 1.0,
			bias: 0.5,
			dithering: 0.0001
		};

		/**
		 * 镜头暗角效果配置
		 * @typedef {object} Vignetting
		 * @property {boolean} enable 是否开启
		 * @property {number[]} color 边缘的颜色,仅适用于颜色类型
		 * @property {number} offset 偏移
		 * @public
		 */

		_private.vignetting = {
			enable: false,
			color: [0, 0, 0],
			offset: 1.5
		};

		/**
		 * 模糊边缘效果配置
		 * @typedef {object} BlurEdge
		 * @property {boolean} enable 是否开启
		 * @property {number} offset 偏移
		 * @public
		 */

		_private.blurEdge = {
			enable: false,
			offset: 1.0
		};

		/**
		 * 电影效果配置
		 * @typedef {object} Film
		 * @property {boolean} enable 是否开启
		 * @property {boolean} grayscale 是否开启灰度模式(黑白效果)
		 * @property {number} noiseIntensity 噪声强度
		 * @property {number} scanlinesIntensity 扫描线强度
		 * @property {number} scanlinesCount 扫面线个数
		 * @public
		 */

		_private.film = {
			enable: false,
			grayscale: false,
			noiseIntensity: 0.35,
			scanlinesIntensity: 0,
			scanlinesCount: 2048
		};

		/**
		 * 色差
		 * @typedef {object} ChromaticAberration
		 * @property {boolean} enable 是否开启
		 * @property {number} chromaFactor 色彩分离系数
		 * @public
		 */

		_private.chromaticAberration = {
			enable: false,
			chromaFactor: 0.025
		};

		/**
		 * FXAA的配置信息。
		 * @typedef {object} FXAA
		 * @property {boolean} enable 是否开启。True表示开启。
		 * @public
		 */

		_private.FXAA = {
			enable: false
		};

		/**
		 * MSAA配置信息。
		 * @typedef {object} MSAA
		 * @property {boolean} enable 是否开启。True表示开启。
		 * @public
		 */

		_private.MSAA = {
			enable: true
		};

		_private.onChange = null;

		_private.objectProxy = new ObjectProxy({
			data: {
				enable: true,
				bloom: _private.bloom,
				screenSpaceAmbientOcclusion: _private.screenSpaceAmbientOcclusion,
				groundTruthAmbientOcclusion: _private.groundTruthAmbientOcclusion,
				screenSpaceReflection: _private.screenSpaceReflection,
				colorCorrection: _private.colorCorrection,
				dof: _private.dof,
				vignetting: _private.vignetting,
				blurEdge: _private.blurEdge,
				film: _private.film,
				chromaticAberration: _private.chromaticAberration,
				FXAA: _private.FXAA,
				MSAA: _private.MSAA,
				temporalSuperSampling: _private.temporalSuperSampling,
			},
			onConvertValue: function (type, value) {
				if (type == 'color') {
					return Utils.parseColor(value, [1, 1, 1]);
				}
			},
			onChange: function (ev) {
				_private.needsRefresh = true;

				if (_private.onChange) {
					_private.onChange(ev);
				}
			}
		});

		// Refresh effect by current config
		this._refresh();

		// Show config always
		Object.defineProperty(this, 'customFormatters', {
			enumerable: false,
			configurable: false,
			get: function () {
				return ['object', { object: _private.objectProxy.dataProxy }];
			}
		});
	}

	// #region Private Functions

	_refresh() {
		let object = this.object;
		if (!object) {
			return;
		}

		let _private = this[__.private];

		if (!_private.needsRefresh) {
			return;
		}

		_private.needsRefresh = false;

		object.node.setPostEffect({
			temporalSuperSampling: _private.temporalSuperSampling,
			postEffect: {
				enable: _private.objectProxy.dataProxy.enable,
				bloom: _private.bloom,
				screenSpaceAmbientOcclusion: _private.screenSpaceAmbientOcclusion,
				groundTruthAmbientOcclusion: _private.groundTruthAmbientOcclusion,
				screenSpaceReflection: _private.screenSpaceReflection,
				colorCorrection: _private.colorCorrection,
				dof: _private.dof,
				vignetting: _private.vignetting,
				blurEdge: _private.blurEdge,
				film: _private.film,
				chromaticAberration: _private.chromaticAberration,
				FXAA: _private.FXAA,
				MSAA: _private.MSAA,
			}
		});
	}

	// #endregion

	// #region BaseComponent Overrides

	onAdd(object) {
		super.onAdd(object);

		// Refresh config before render if needed
		this.app.addBeforeRenderCallback(this._onBeforeRenderCallback = () => {
			this._refresh();
		});
	}

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

		_private.objectProxy.dispose();

		this.app.removeBeforeRenderCallback(this._onBeforeRenderCallback);

		super.onRemove();
	}

	onCopy(component) {
		this.config = component.config;
	}

	// #endregion

	/**
	 * 是否开启
	 * @type {boolean}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.enable = false;
	 * // @expect(app.camera.postEffect.enable == false)
	 * @public
	 */
	get enable() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.enable;
	}
	set enable(value) {
		let _private = this[__.private];

		if (_private.objectProxy.dataProxy.enable == value) {
			return;
		}

		_private.objectProxy.dataProxy.enable = value;

		this._refresh();
	}

	/**
	 * TSS效果 在连续的帧之间进行抖动来减少图像中的锯齿
	 * @type {TemporalSuperSampling}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.temporalSuperSampling.enable = true;
	 * // @expect(app.camera.postEffect.temporalSuperSampling.enable == true)
	 * @public
	 */
	get temporalSuperSampling() {
		return this[__.private].objectProxy.dataProxy.temporalSuperSampling;
	}
	set temporalSuperSampling(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.temporalSuperSampling, value, true);
	}

	/**
	 * 屏幕级辉光效果
	 * @type {Bloom}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.bloom.enable = true;
	 * // @expect(app.camera.postEffect.bloom.enable == true)
	 * @public
	 */
	get bloom() {
		return this[__.private].objectProxy.dataProxy.bloom;
	}
	set bloom(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.bloom, value, true);
	}

	/**
	 * 屏幕空间环境光遮蔽(SSAO)
	 * @type {ScreenSpaceAmbientOcclusion}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.screenSpaceAmbientOcclusion.enable = true;
	 * // @expect(app.camera.postEffect.screenSpaceAmbientOcclusion.enable == true)
	 * @public
	*/
	get screenSpaceAmbientOcclusion() {
		return this[__.private].objectProxy.dataProxy.screenSpaceAmbientOcclusion;
	}
	set screenSpaceAmbientOcclusion(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.screenSpaceAmbientOcclusion, value, true);
	}


	/**
	 * 真实世界环境光遮蔽(GTAO)
	 * @type {GroundTruthAmbientOcclusion}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.groundTruthAmbientOcclusion.enable = true;
	 * // @expect(app.camera.postEffect.groundTruthAmbientOcclusion.enable == true)
	 * @public
	*/
	get groundTruthAmbientOcclusion() {
		return this[__.private].objectProxy.dataProxy.groundTruthAmbientOcclusion;
	}
	set groundTruthAmbientOcclusion(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.groundTruthAmbientOcclusion, value, true);
	}

	/**
	 * 屏幕空间反射(SSR) 模拟出物体表面的反射效果,从而增强场景的真实感和逼真度
	 * @type {ScreenSpaceReflection}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.screenSpaceReflection.enable = true;
	 * // @expect(app.camera.postEffect.screenSpaceReflection.enable == true)
	 * @public
	 */
	get screenSpaceReflection() {
		return this[__.private].objectProxy.dataProxy.screenSpaceReflection;
	}
	set screenSpaceReflection(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.screenSpaceReflection, value, true);
	}

	/**
	 * 颜色校正
	 * @type {ColorCorrection}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.colorCorrection.enable = true;
	 * // @expect(app.camera.postEffect.colorCorrection.enable == true)
	 * @public
	 */
	get colorCorrection() {
		return this[__.private].objectProxy.dataProxy.colorCorrection;
	}
	set colorCorrection(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.colorCorrection, value, true);
	}


	/**
	 * 景深效果
	 * @type {Dof}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.dof.enable = true;
	 * // @expect(app.camera.postEffect.dof.enable == true)
	 * @public
	 */
	get dof() {
		return this[__.private].objectProxy.dataProxy.dof;
	}
	set dof(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.dof, value, true);
	}

	/**
	 * 暗角效果 屏幕边缘给定一个颜色进行填充,并在中心位置过渡到场景
	 * @type {Vignetting}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.vignetting.enable = true;
	 * // @expect(app.camera.postEffect.vignetting.enable == true)
	 * @public
	 */
	get vignetting() {
		return this[__.private].objectProxy.dataProxy.vignetting;
	}
	set vignetting(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.vignetting, value, true);
	}

	/**
	 *  边缘模糊
	 * @type {BlurEdge}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.blurEdge.enable = true;
	 * // @expect(app.camera.postEffect.blurEdge.enable == true)
	 * @public
	 */
	get blurEdge() {
		return this[__.private].objectProxy.dataProxy.blurEdge;
	}
	set blurEdge(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.blurEdge, value, true);
	}

	/**
	 * 胶片效果
	 * @type {Film}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.film.enable = true;
	 * // @expect(app.camera.postEffect.film.enable == true)
	 * @public
	 */
	get film() {
		return this[__.private].objectProxy.dataProxy.film;
	}
	set film(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.film, value, true);
	}


	/**
	 * 色彩分离效果
	 * @type {ChromaticAberration}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.chromaticAberration.enable = true;
	 * // @expect(app.camera.postEffect.chromaticAberration.enable == true)
	 * @public
	 */
	get chromaticAberration() {
		return this[__.private].objectProxy.dataProxy.chromaticAberration;
	}
	set chromaticAberration(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.chromaticAberration, value, true);
	}

	/**
	 * 快速近似抗锯齿
	 * @type {FXAA}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.FXAA.enable = true;
	 * // @expect(app.camera.postEffect.FXAA.enable == true)
	 * @public
	 */
	get FXAA() {
		return this[__.private].objectProxy.dataProxy.FXAA;
	}
	set FXAA(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.FXAA, value, true);
	}

	/**
	 * 多重采样抗锯齿
	 * @type {MSAA}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config.MSAA.enable = false;
	 * // @expect(app.camera.postEffect.MSAA.enable == false)
	 * @public
	 */
	get MSAA() {
		return this[__.private].objectProxy.dataProxy.MSAA;
	}
	set MSAA(value) {
		let _private = this[__.private];
		Utils.mergeObject(_private.objectProxy.dataProxy.MSAA, value, true);
	}

	/**
	 * 后期效果的配置。
	 * @typedef {object} PostEffectInfo
	 * @property {boolean} enable true表示开启。是否开启后期效果
	 * @property {Bloom} bloom 泛光效果。
	 * @property {ScreenSpaceAmbientOcclusion} screenSpaceAmbientOcclusion 屏幕空间环境光遮蔽(SSAO)效果。
	 * @property {GroundTruthAmbientOcclusion} groundTruthAmbientOcclusion 真实世界环境光遮蔽(GTAO)效果。
	 * @property {ScreenSpaceReflection} screenSpaceReflection 屏幕空间反射(SSR)效果。
	 * @property {ColorCorrection} colorCorrection 颜色校正效果。
	 * @property {Dof} dof 景深效果。
	 * @property {Vignetting} vignetting 暗角效果。
	 * @property {BlurEdge} blurEdge 边缘模糊效果。
	 * @property {Film} film 胶片效果。
	 * @property {ChromaticAberration} chromaticAberration 色彩分离效果。
	 * @property {FXAA} FXAA 快速近似抗锯齿效果。
	 * @property {MSAA} MSAA 多重采样抗锯齿效果。
	 * @property {TemporalSuperSampling} temporalSuperSampling 时序超采样效果。(TSS)
	 * @public
	 */

	/**
	 * 设置/获取 后期配置
	 * @type {PostEffectInfo}
	 * @example
	 * let app = THING.App.current
	 * app.camera.postEffect.config = {
	 *   FXAA: {enable: true}
	 * };
	 * // @expect(app.camera.postEffect.config.FXAA.enable == true)
	 * @public
	 */
	get config() {
		return this[__.private].objectProxy.dataProxy;
	}
	set config(value = {}) {
		let _private = this[__.private];

		let to = _private.objectProxy.dataProxy;
		if (Utils.isValid(value.enable)) {
			to.enable = value.enable;
		}

		Utils.mergeObject(to.bloom, value.bloom, true);
		Utils.mergeObject(to.screenSpaceAmbientOcclusion, value.screenSpaceAmbientOcclusion, true);
		Utils.mergeObject(to.groundTruthAmbientOcclusion, value.groundTruthAmbientOcclusion, true);
		Utils.mergeObject(to.screenSpaceReflection, value.screenSpaceReflection, true);
		Utils.mergeObject(to.colorCorrection, value.colorCorrection, true);
		Utils.mergeObject(to.dof, value.dof, true);
		Utils.mergeObject(to.vignetting, value.vignetting, true);
		Utils.mergeObject(to.blurEdge, value.blurEdge, true);
		Utils.mergeObject(to.film, value.film, true);
		Utils.mergeObject(to.chromaticAberration, value.chromaticAberration, true);
		Utils.mergeObject(to.FXAA, value.FXAA, true);
		Utils.mergeObject(to.MSAA, value.MSAA, true);
		Utils.mergeObject(to.temporalSuperSampling, value.temporalSuperSampling, true);
	}

	/**
	 * Get/Set when change callback function.
	 * @type {Function}
	 * @private
	 */
	get onChange() {
		let _private = this[__.private];

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

		_private.onChange = value;
	}

}

export { CameraPostEffectComponent }