import { Vector3D } 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 { ParticleScaleNode } from '../nodes/ParticleScaleNode';

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

import { ParticleStateBase } from './ParticleStateBase';

/**
 * ...
 */
export class ParticleScaleState extends ParticleStateBase {
	/** @private */
	public static SCALE_INDEX: number = 0;

	private _particleScaleNode: ParticleScaleNode;
	private _usesCycle: boolean;
	private _usesPhase: boolean;
	private _minScale: number;
	private _maxScale: number;
	private _cycleDuration: number;
	private _cyclePhase: number;
	private _scaleData: Vector3D;

	/**
	 * Defines the end scale of the state, when in global mode. Defaults to 1.
	 */
	public get minScale(): number {
		return this._minScale;
	}

	public set minScale(value: number) {
		this._minScale = value;

		this.updateScaleData();
	}

	/**
	 * Defines the end scale of the state, when in global mode. Defaults to 1.
	 */
	public get maxScale(): number {
		return this._maxScale;
	}

	public set maxScale(value: number) {
		this._maxScale = value;

		this.updateScaleData();
	}

	/**
	 * 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.updateScaleData();
	}

	/**
	 * 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.updateScaleData();
	}

	constructor(animator: ParticleAnimator, particleScaleNode: ParticleScaleNode) {
		super(animator, particleScaleNode);

		this._particleScaleNode = particleScaleNode;
		this._usesCycle = this._particleScaleNode._iUsesCycle;
		this._usesPhase = this._particleScaleNode._iUsesPhase;
		this._minScale = this._particleScaleNode._iMinScale;
		this._maxScale = this._particleScaleNode._iMaxScale;
		this._cycleDuration = this._particleScaleNode._iCycleDuration;
		this._cyclePhase = this._particleScaleNode._iCyclePhase;

		this.updateScaleData();
	}

	public setRenderState(shader: ShaderBase, renderable: _Render_RenderableBase, animationElements: AnimationElements, animationRegisterData: AnimationRegisterData): void {
		const index: number = animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleScaleState.SCALE_INDEX);

		if (this._particleScaleNode.mode == ParticlePropertiesMode.LOCAL_STATIC) {
			if (this._usesCycle) {
				if (this._usesPhase)
					animationElements.activateVertexBuffer(index, this._particleScaleNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_4);
				else
					animationElements.activateVertexBuffer(index, this._particleScaleNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_3);
			} else
				animationElements.activateVertexBuffer(index, this._particleScaleNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_2);
		} else
			shader.setVertexConst(index, this._scaleData.x, this._scaleData.y, this._scaleData.z, this._scaleData.w);
	}

	private updateScaleData(): void {
		if (this._particleScaleNode.mode == ParticlePropertiesMode.GLOBAL) {
			if (this._usesCycle) {
				if (this._cycleDuration <= 0)
					throw (new Error('the cycle duration must be greater than zero'));
				this._scaleData = new Vector3D((this._minScale + this._maxScale) / 2, Math.abs(this._minScale - this._maxScale) / 2, Math.PI * 2 / this._cycleDuration, this._cyclePhase * Math.PI / 180);
			} else
				this._scaleData = new Vector3D(this._minScale, this._maxScale - this._minScale, 0, 0);
		}
	}
}