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

import { ContextGLVertexBufferFormat } from '@awayjs/stage';

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

import { AnimationElements } from '../data/AnimationElements';
import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode';
import { ParticleColorNode } from '../nodes/ParticleColorNode';

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

import { ParticleStateBase } from './ParticleStateBase';

/**
 * ...
 * @author ...
 */
export class ParticleColorState extends ParticleStateBase {
	/** @private */
	public static START_MULTIPLIER_INDEX: number = 0;

	/** @private */
	public static DELTA_MULTIPLIER_INDEX: number = 1;

	/** @private */
	public static START_OFFSET_INDEX: number = 2;

	/** @private */
	public static DELTA_OFFSET_INDEX: number = 3;

	/** @private */
	public static CYCLE_INDEX: number = 4;

	private _particleColorNode: ParticleColorNode;
	private _usesMultiplier: boolean;
	private _usesOffset: boolean;
	private _usesCycle: boolean;
	private _usesPhase: boolean;
	private _startColor: ColorTransform;
	private _endColor: ColorTransform;
	private _cycleDuration: number;
	private _cyclePhase: number;
	private _cycleData: Vector3D;
	private _startMultiplierData: Vector3D;
	private _deltaMultiplierData: Vector3D;
	private _startOffsetData: Vector3D;
	private _deltaOffsetData: Vector3D;

	/**
	 * Defines the start color transform of the state, when in global mode.
	 */
	public get startColor(): ColorTransform {
		return this._startColor;
	}

	public set startColor(value: ColorTransform) {
		this._startColor = value;

		this.updateColorData();
	}

	/**
	 * Defines the end color transform of the state, when in global mode.
	 */
	public get endColor(): ColorTransform {
		return this._endColor;
	}

	public set endColor(value: ColorTransform) {
		this._endColor = value;

		this.updateColorData();
	}

	/**
	 * Defines the duration of the animation in seconds, used as a period independent of particle duration when in global mode. Defaults to 1.
	 */
	public get cycleDuration(): number {
		return this._cycleDuration;
	}

	public set cycleDuration(value: number) {
		this._cycleDuration = value;

		this.updateColorData();
	}

	/**
	 * Defines the phase of the cycle in degrees, used as the starting offset of the cycle when in global mode. Defaults to 0.
	 */
	public get cyclePhase(): number {
		return this._cyclePhase;
	}

	public set cyclePhase(value: number) {
		this._cyclePhase = value;

		this.updateColorData();
	}

	constructor(animator: ParticleAnimator, particleColorNode: ParticleColorNode) {
		super(animator, particleColorNode);

		this._particleColorNode = particleColorNode;
		this._usesMultiplier = this._particleColorNode._iUsesMultiplier;
		this._usesOffset = this._particleColorNode._iUsesOffset;
		this._usesCycle = this._particleColorNode._iUsesCycle;
		this._usesPhase = this._particleColorNode._iUsesPhase;
		this._startColor = this._particleColorNode._iStartColor;
		this._endColor = this._particleColorNode._iEndColor;
		this._cycleDuration = this._particleColorNode._iCycleDuration;
		this._cyclePhase = this._particleColorNode._iCyclePhase;

		this.updateColorData();
	}

	public setRenderState(shader: ShaderBase, renderable: _Render_RenderableBase, animationElements: AnimationElements, animationRegisterData: AnimationRegisterData): void {
		if (shader.usesFragmentAnimation) {
			let dataOffset: number = this._particleColorNode._iDataOffset;
			if (this._usesCycle)
				shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.CYCLE_INDEX), this._cycleData.x, this._cycleData.y, this._cycleData.z, this._cycleData.w);

			if (this._usesMultiplier) {
				if (this._particleColorNode.mode == ParticlePropertiesMode.LOCAL_STATIC) {
					animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.START_MULTIPLIER_INDEX), dataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_4);
					dataOffset += 4;
					animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.DELTA_MULTIPLIER_INDEX), dataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_4);
					dataOffset += 4;
				} else {
					shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.START_MULTIPLIER_INDEX), this._startMultiplierData.x, this._startMultiplierData.y, this._startMultiplierData.z, this._startMultiplierData.w);
					shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.DELTA_MULTIPLIER_INDEX), this._deltaMultiplierData.x, this._deltaMultiplierData.y, this._deltaMultiplierData.z, this._deltaMultiplierData.w);
				}
			}
			if (this._usesOffset) {
				if (this._particleColorNode.mode == ParticlePropertiesMode.LOCAL_STATIC) {
					animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.START_OFFSET_INDEX), dataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_4);
					dataOffset += 4;
					animationElements.activateVertexBuffer(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.DELTA_OFFSET_INDEX), dataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_4);
				} else {
					shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.START_OFFSET_INDEX), this._startOffsetData.x, this._startOffsetData.y, this._startOffsetData.z, this._startOffsetData.w);
					shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleColorState.DELTA_OFFSET_INDEX), this._deltaOffsetData.x, this._deltaOffsetData.y, this._deltaOffsetData.z, this._deltaOffsetData.w);
				}
			}
		}
	}

	private updateColorData(): void {
		if (this._usesCycle) {
			if (this._cycleDuration <= 0)
				throw (new Error('the cycle duration must be greater than zero'));
			this._cycleData = new Vector3D(Math.PI * 2 / this._cycleDuration, this._cyclePhase * Math.PI / 180, 0, 0);
		}
		if (this._particleColorNode.mode == ParticlePropertiesMode.GLOBAL) {
			if (this._usesCycle) {
				if (this._usesMultiplier) {
					this._startMultiplierData = new Vector3D((this._startColor.redMultiplier + this._endColor.redMultiplier) / 2, (this._startColor.greenMultiplier + this._endColor.greenMultiplier) / 2, (this._startColor.blueMultiplier + this._endColor.blueMultiplier) / 2, (this._startColor.alphaMultiplier + this._endColor.alphaMultiplier) / 2);
					this._deltaMultiplierData = new Vector3D((this._endColor.redMultiplier - this._startColor.redMultiplier) / 2, (this._endColor.greenMultiplier - this._startColor.greenMultiplier) / 2, (this._endColor.blueMultiplier - this._startColor.blueMultiplier) / 2, (this._endColor.alphaMultiplier - this._startColor.alphaMultiplier) / 2);
				}

				if (this._usesOffset) {
					this._startOffsetData = new Vector3D((this._startColor.redOffset + this._endColor.redOffset) / (255 * 2), (this._startColor.greenOffset + this._endColor.greenOffset) / (255 * 2), (this._startColor.blueOffset + this._endColor.blueOffset) / (255 * 2), (this._startColor.alphaOffset + this._endColor.alphaOffset) / (255 * 2));
					this._deltaOffsetData = new Vector3D((this._endColor.redOffset - this._startColor.redOffset) / (255 * 2), (this._endColor.greenOffset - this._startColor.greenOffset) / (255 * 2), (this._endColor.blueOffset - this._startColor.blueOffset) / (255 * 2), (this._endColor.alphaOffset - this._startColor.alphaOffset) / (255 * 2));
				}
			} else {
				if (this._usesMultiplier) {
					this._startMultiplierData = new Vector3D(this._startColor.redMultiplier, this._startColor.greenMultiplier, this._startColor.blueMultiplier, this._startColor.alphaMultiplier);
					this._deltaMultiplierData = new Vector3D((this._endColor.redMultiplier - this._startColor.redMultiplier), (this._endColor.greenMultiplier - this._startColor.greenMultiplier), (this._endColor.blueMultiplier - this._startColor.blueMultiplier), (this._endColor.alphaMultiplier - this._startColor.alphaMultiplier));
				}

				if (this._usesOffset) {
					this._startOffsetData = new Vector3D(this._startColor.redOffset / 255, this._startColor.greenOffset / 255, this._startColor.blueOffset / 255, this._startColor.alphaOffset / 255);
					this._deltaOffsetData = new Vector3D((this._endColor.redOffset - this._startColor.redOffset) / 255, (this._endColor.greenOffset - this._startColor.greenOffset) / 255, (this._endColor.blueOffset - this._startColor.blueOffset) / 255, (this._endColor.alphaOffset - this._startColor.alphaOffset) / 255);
				}
			}
		}
	}
}