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

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

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

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

import { ParticleStateBase } from './ParticleStateBase';

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

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

	private _particleSpriteSheetNode: ParticleSpriteSheetNode;
	private _usesCycle: boolean;
	private _usesPhase: boolean;
	private _totalFrames: number;
	private _numColumns: number;
	private _numRows: number;
	private _cycleDuration: number;
	private _cyclePhase: number;
	private _spriteSheetData: Array<number>;

	/**
	 * Defines the cycle phase, when in global mode. Defaults to zero.
	 */
	public get cyclePhase(): number {
		return this._cyclePhase;
	}

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

		this.updateSpriteSheetData();
	}

	/**
	 * Defines the cycle duration in seconds, when in global mode. Defaults to 1.
	 */
	public get cycleDuration(): number {
		return this._cycleDuration;
	}

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

		this.updateSpriteSheetData();
	}

	constructor(animator: ParticleAnimator, particleSpriteSheetNode: ParticleSpriteSheetNode) {
		super(animator, particleSpriteSheetNode);

		this._particleSpriteSheetNode = particleSpriteSheetNode;

		this._usesCycle = this._particleSpriteSheetNode._iUsesCycle;
		this._usesPhase = this._particleSpriteSheetNode._iUsesCycle;
		this._totalFrames = this._particleSpriteSheetNode._iTotalFrames;
		this._numColumns = this._particleSpriteSheetNode._iNumColumns;
		this._numRows = this._particleSpriteSheetNode._iNumRows;
		this._cycleDuration = this._particleSpriteSheetNode._iCycleDuration;
		this._cyclePhase = this._particleSpriteSheetNode._iCyclePhase;

		this.updateSpriteSheetData();
	}

	public setRenderState(shader: ShaderBase, renderable: _Render_RenderableBase, animationElements: AnimationElements, animationRegisterData: AnimationRegisterData): void {
		if (!shader.usesUVTransform) {
			shader.setVertexConst(animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleSpriteSheetState.UV_INDEX_0), this._spriteSheetData[0], this._spriteSheetData[1], this._spriteSheetData[2], this._spriteSheetData[3]);
			if (this._usesCycle) {
				const index: number = animationRegisterData.getRegisterIndex(this._pAnimationNode, ParticleSpriteSheetState.UV_INDEX_1);
				if (this._particleSpriteSheetNode.mode == ParticlePropertiesMode.LOCAL_STATIC) {
					if (this._usesPhase)
						animationElements.activateVertexBuffer(index, this._particleSpriteSheetNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_3);
					else
						animationElements.activateVertexBuffer(index, this._particleSpriteSheetNode._iDataOffset, shader.stage, ContextGLVertexBufferFormat.FLOAT_2);
				} else
					shader.setVertexConst(index, this._spriteSheetData[4], this._spriteSheetData[5]);
			}
		}
	}

	private updateSpriteSheetData(): void {
		this._spriteSheetData = new Array<number>(8);

		const uTotal: number = this._totalFrames / this._numColumns;

		this._spriteSheetData[0] = uTotal;
		this._spriteSheetData[1] = 1 / this._numColumns;
		this._spriteSheetData[2] = 1 / this._numRows;

		if (this._usesCycle) {
			if (this._cycleDuration <= 0)
				throw (new Error('the cycle duration must be greater than zero'));
			this._spriteSheetData[4] = uTotal / this._cycleDuration;
			this._spriteSheetData[5] = this._cycleDuration;
			if (this._usesPhase)
				this._spriteSheetData[6] = this._cyclePhase;
		}
	}
}