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

import { _Render_Shape } from '../renderables/Shape';

import { AnimationElements } from './data/AnimationElements';
import { ParticleCollection } from './data/ParticleCollection';
import { ParticlePropertiesMode } from './data/ParticlePropertiesMode';
import { ParticleNodeBase } from './nodes/ParticleNodeBase';
import { ParticleStateBase } from './states/ParticleStateBase';

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

/**
 * Provides an interface for assigning paricle-based animation data sets to sprite-based entity objects
 * and controlling the various available states of animation through an interative playhead that can be
 * automatically updated or manually triggered.
 *
 * Requires that the containing geometry of the parent sprite is particle geometry
 *
 * @see away.base.ParticleAnimator
 */
export class ParticleAnimator extends AnimatorBase {

	private _particleAnimationSet: ParticleAnimationSet;
	private _animationParticleStates: Array<ParticleStateBase> = new Array<ParticleStateBase>();
	private _animatorParticleStates: Array<ParticleStateBase> = new Array<ParticleStateBase>();
	private _timeParticleStates: Array<ParticleStateBase> = new Array<ParticleStateBase>();
	private _totalLenOfOneVertex: number = 0;
	private _animatorSubGeometries: Object = new Object();

	/**
	 * Creates a new <code>ParticleAnimator</code> object.
	 *
	 * @param particleAnimationSet The animation data set containing the particle animations used by the animator.
	 */
	constructor(particleAnimationSet: ParticleAnimationSet) {
		super(particleAnimationSet);
		this._particleAnimationSet = particleAnimationSet;

		let state: ParticleStateBase;
		let node: ParticleNodeBase;

		for (let i: number = 0; i < this._particleAnimationSet.particleNodes.length; i++) {
			node = this._particleAnimationSet.particleNodes[i];
			state = <ParticleStateBase> this.getAnimationState(node);
			if (node.mode == ParticlePropertiesMode.LOCAL_DYNAMIC) {
				this._animatorParticleStates.push(state);
				node._iDataOffset = this._totalLenOfOneVertex;
				this._totalLenOfOneVertex += node.dataLength;
			} else {
				this._animationParticleStates.push(state);
			}
			if (state.needUpdateTime)
				this._timeParticleStates.push(state);
		}
	}

	/**
	 * @inheritDoc
	 */
	public clone(): AnimatorBase {
		return new ParticleAnimator(this._particleAnimationSet);
	}

	/**
	 * @inheritDoc
	 */
	public setRenderState(shader: ShaderBase, renderable: _Render_Shape): void {
		const animationRegisterData: AnimationRegisterData = this._particleAnimationSet._iAnimationRegisterData;

		const particleCollection: ParticleCollection = renderable.shape.particleCollection;

		const elements: IElements = renderable.shape.elements;

		//process animation sub geometries
		const animationElements: AnimationElements = this._particleAnimationSet.getAnimationElements(particleCollection, elements);
		let i: number;

		for (i = 0; i < this._animationParticleStates.length; i++)
			this._animationParticleStates[i].setRenderState(shader, renderable, animationElements, animationRegisterData);

		//process animator subgeometries
		const animatorElements: AnimationElements = this.getAnimatorElements(particleCollection, elements);

		for (i = 0; i < this._animatorParticleStates.length; i++)
			this._animatorParticleStates[i].setRenderState(shader, renderable, animatorElements, animationRegisterData);
	}

	/**
	 * @inheritDoc
	 */
	public testGPUCompatibility(shader: ShaderBase): void {

	}

	/**
	 * @inheritDoc
	 */
	public start(): void {
		super.start();

		for (let i: number = 0; i < this._timeParticleStates.length; i++)
			this._timeParticleStates[i].offset(this._pAbsoluteTime);
	}

	/**
	 * @inheritDoc
	 */
	public _pUpdateDeltaTime(dt: number): void {
		this._pAbsoluteTime += dt;

		for (let i: number = 0; i < this._timeParticleStates.length; i++)
			this._timeParticleStates[i].update(this._pAbsoluteTime);
	}

	/**
	 * @inheritDoc
	 */
	public resetTime(offset: number = 0): void {
		for (let i: number = 0; i < this._timeParticleStates.length; i++)
			this._timeParticleStates[i].offset(this._pAbsoluteTime + offset);
		this.update(this.time);
	}

	public dispose(): void {
		for (const key in this._animatorSubGeometries)
			(<AnimationElements> this._animatorSubGeometries[key]).dispose();
	}

	private getAnimatorElements(particleCollection: ParticleCollection, elements: IElements): AnimationElements {
		if (!this._animatorParticleStates.length)
			return;

		const animatorElements: AnimationElements = this._animatorSubGeometries[elements.id] = new AnimationElements();

		//create the vertexData vector that will be used for local state data
		animatorElements.createVertexData(elements.numVertices, this._totalLenOfOneVertex);

		//pass the particles data to the animator elements
		animatorElements.animationParticles = this._particleAnimationSet.getAnimationElements(particleCollection, elements).animationParticles;
	}
}