import { ColorTransform } from '@awayjs/core';

import { ShaderRegisterCache, ShaderRegisterElement } from '@awayjs/stage';

import { ShaderBase, AnimationRegisterData } from '@awayjs/renderer';

import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode';
import { ColorSegmentPoint } from '../data/ColorSegmentPoint';
import { ParticleSegmentedColorState } from '../states/ParticleSegmentedColorState';

import { ParticleAnimationSet } from '../ParticleAnimationSet';

import { ParticleNodeBase } from './ParticleNodeBase';

/**
 *
 */
export class ParticleSegmentedColorNode extends ParticleNodeBase {
	/** @private */
	public _iUsesMultiplier: boolean;
	/** @private */
	public _iUsesOffset: boolean;
	/** @private */
	public _iStartColor: ColorTransform;
	/** @private */
	public _iEndColor: ColorTransform;
	/** @private */
	public _iNumSegmentPoint: number;
	/** @private */
	public _iSegmentPoints: Array<ColorSegmentPoint>;

	constructor(usesMultiplier: boolean, usesOffset: boolean, numSegmentPoint: number, startColor: ColorTransform, endColor: ColorTransform, segmentPoints: Array<ColorSegmentPoint>) {
		//because of the stage3d register limitation, it only support the global mode
		super('ParticleSegmentedColor', ParticlePropertiesMode.GLOBAL, 0, ParticleAnimationSet.COLOR_PRIORITY);

		this._pStateClass = ParticleSegmentedColorState;

		if (numSegmentPoint > 4)
			throw (new Error('the numSegmentPoint must be less or equal 4'));
		this._iUsesMultiplier = usesMultiplier;
		this._iUsesOffset = usesOffset;
		this._iNumSegmentPoint = numSegmentPoint;
		this._iStartColor = startColor;
		this._iEndColor = endColor;
		this._iSegmentPoints = segmentPoints;
	}

	/**
	 * @inheritDoc
	 */
	public _iProcessAnimationSetting(particleAnimationSet: ParticleAnimationSet): void {
		if (this._iUsesMultiplier)
			particleAnimationSet.hasColorMulNode = true;
		if (this._iUsesOffset)
			particleAnimationSet.hasColorAddNode = true;
	}

	/**
	 * @inheritDoc
	 */
	public getAGALVertexCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string {
		let code: string = '';
		if (shader.usesFragmentAnimation) {
			let accMultiplierColor: ShaderRegisterElement;
			//var accOffsetColor:ShaderRegisterElement;
			if (this._iUsesMultiplier) {
				accMultiplierColor = registerCache.getFreeVertexVectorTemp();
				registerCache.addVertexTempUsages(accMultiplierColor, 1);
			}

			const tempColor: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
			registerCache.addVertexTempUsages(tempColor, 1);

			const temp: ShaderRegisterElement = registerCache.getFreeVertexVectorTemp();
			const accTime: ShaderRegisterElement = new ShaderRegisterElement(temp.regName, temp.index, 0);
			let tempTime: ShaderRegisterElement = new ShaderRegisterElement(temp.regName, temp.index, 1);

			if (this._iUsesMultiplier)
				registerCache.removeVertexTempUsage(accMultiplierColor);

			registerCache.removeVertexTempUsage(tempColor);

			//for saving all the life values (at most 4)
			const lifeTimeRegister: ShaderRegisterElement = registerCache.getFreeVertexConstant();
			animationRegisterData.setRegisterIndex(this, ParticleSegmentedColorState.TIME_DATA_INDEX, lifeTimeRegister.index);

			let i: number;

			let startMulValue: ShaderRegisterElement;
			let deltaMulValues: Array<ShaderRegisterElement>;
			if (this._iUsesMultiplier) {
				startMulValue = registerCache.getFreeVertexConstant();
				animationRegisterData.setRegisterIndex(this, ParticleSegmentedColorState.START_MULTIPLIER_INDEX, startMulValue.index);
				deltaMulValues = new Array<ShaderRegisterElement>();
				for (i = 0; i < this._iNumSegmentPoint + 1; i++)
					deltaMulValues.push(registerCache.getFreeVertexConstant());
			}

			let startOffsetValue: ShaderRegisterElement;
			let deltaOffsetValues: Array<ShaderRegisterElement>;
			if (this._iUsesOffset) {
				startOffsetValue = registerCache.getFreeVertexConstant();
				animationRegisterData.setRegisterIndex(this, ParticleSegmentedColorState.START_OFFSET_INDEX, startOffsetValue.index);
				deltaOffsetValues = new Array<ShaderRegisterElement>();
				for (i = 0; i < this._iNumSegmentPoint + 1; i++)
					deltaOffsetValues.push(registerCache.getFreeVertexConstant());
			}

			if (this._iUsesMultiplier)
				code += 'mov ' + accMultiplierColor + ',' + startMulValue + '\n';
			if (this._iUsesOffset)
				code += 'add ' + animationRegisterData.colorAddTarget + ',' + animationRegisterData.colorAddTarget + ',' + startOffsetValue + '\n';

			for (i = 0; i < this._iNumSegmentPoint; i++) {
				switch (i) {
					case 0:
						code += 'min ' + tempTime + ',' + animationRegisterData.vertexLife + ',' + lifeTimeRegister + '.x\n';
						break;
					case 1:
						code += 'sub ' + accTime + ',' + animationRegisterData.vertexLife + ',' + lifeTimeRegister + '.x\n';
						code += 'max ' + tempTime + ',' + accTime + ',' + animationRegisterData.vertexZeroConst + '\n';
						code += 'min ' + tempTime + ',' + tempTime + ',' + lifeTimeRegister + '.y\n';
						break;
					case 2:
						code += 'sub ' + accTime + ',' + accTime + ',' + lifeTimeRegister + '.y\n';
						code += 'max ' + tempTime + ',' + accTime + ',' + animationRegisterData.vertexZeroConst + '\n';
						code += 'min ' + tempTime + ',' + tempTime + ',' + lifeTimeRegister + '.z\n';
						break;
					case 3:
						code += 'sub ' + accTime + ',' + accTime + ',' + lifeTimeRegister + '.z\n';
						code += 'max ' + tempTime + ',' + accTime + ',' + animationRegisterData.vertexZeroConst + '\n';
						code += 'min ' + tempTime + ',' + tempTime + ',' + lifeTimeRegister + '.w\n';
						break;
				}
				if (this._iUsesMultiplier) {
					code += 'mul ' + tempColor + ',' + tempTime + ',' + deltaMulValues[i] + '\n';
					code += 'add ' + accMultiplierColor + ',' + accMultiplierColor + ',' + tempColor + '\n';
				}
				if (this._iUsesOffset) {
					code += 'mul ' + tempColor + ',' + tempTime + ',' + deltaOffsetValues[i] + '\n';
					code += 'add ' + animationRegisterData.colorAddTarget + ',' + animationRegisterData.colorAddTarget + ',' + tempColor + '\n';
				}
			}

			//for the last segment:
			if (this._iNumSegmentPoint == 0)
				tempTime = animationRegisterData.vertexLife;
			else {
				switch (this._iNumSegmentPoint) {
					case 1:
						code += 'sub ' + accTime + ',' + animationRegisterData.vertexLife + ',' + lifeTimeRegister + '.x\n';
						break;
					case 2:
						code += 'sub ' + accTime + ',' + accTime + ',' + lifeTimeRegister + '.y\n';
						break;
					case 3:
						code += 'sub ' + accTime + ',' + accTime + ',' + lifeTimeRegister + '.z\n';
						break;
					case 4:
						code += 'sub ' + accTime + ',' + accTime + ',' + lifeTimeRegister + '.w\n';
						break;
				}
				code += 'max ' + tempTime + ',' + accTime + ',' + animationRegisterData.vertexZeroConst + '\n';
			}
			if (this._iUsesMultiplier) {
				code += 'mul ' + tempColor + ',' + tempTime + ',' + deltaMulValues[this._iNumSegmentPoint] + '\n';
				code += 'add ' + accMultiplierColor + ',' + accMultiplierColor + ',' + tempColor + '\n';
				code += 'mul ' + animationRegisterData.colorMulTarget + ',' + animationRegisterData.colorMulTarget + ',' + accMultiplierColor + '\n';
			}
			if (this._iUsesOffset) {
				code += 'mul ' + tempColor + ',' + tempTime + ',' + deltaOffsetValues[this._iNumSegmentPoint] + '\n';
				code += 'add ' + animationRegisterData.colorAddTarget + ',' + animationRegisterData.colorAddTarget + ',' + tempColor + '\n';
			}

		}
		return code;
	}

}