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

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

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

import { ParticlePropertiesMode } from '../data/ParticlePropertiesMode';
import { ParticleUVState } from '../states/ParticleUVState';

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

import { ParticleNodeBase } from './ParticleNodeBase';

/**
 * A particle animation node used to control the UV offset and scale of a particle over time.
 */
export class ParticleUVNode extends ParticleNodeBase {
	/** @private */
	public _iUvData: Vector3D;

	/**
	 *
	 */
	public static U_AXIS: string = 'x';

	/**
	 *
	 */
	public static V_AXIS: string = 'y';

	private _cycle: number;
	private _scale: number;
	private _axis: string;

	/**
	 * Creates a new <code>ParticleTimeNode</code>
	 *
	 * @param               mode            Defines whether the mode of operation acts on local properties of a particle or global properties of the node.
	 * @param    [optional] cycle           Defines whether the time track is in loop mode. Defaults to false.
	 * @param    [optional] scale           Defines whether the time track is in loop mode. Defaults to false.
	 * @param    [optional] axis            Defines whether the time track is in loop mode. Defaults to false.
	 */
	constructor(mode: number, cycle: number = 1, scale: number = 1, axis: string = 'x') {
		//because of the stage3d register limitation, it only support the global mode
		super('ParticleUV', ParticlePropertiesMode.GLOBAL, 4, ParticleAnimationSet.POST_PRIORITY + 1);

		this._pStateClass = ParticleUVState;

		this._cycle = cycle;
		this._scale = scale;
		this._axis = axis;

		this.updateUVData();
	}

	/**
	 *
	 */
	public get cycle(): number {
		return this._cycle;
	}

	public set cycle(value: number) {
		this._cycle = value;

		this.updateUVData();
	}

	/**
	 *
	 */
	public get scale(): number {
		return this._scale;
	}

	public set scale(value: number) {
		this._scale = value;

		this.updateUVData();
	}

	/**
	 *
	 */
	public get axis(): string {
		return this._axis;
	}

	public set axis(value: string) {
		this._axis = value;
	}

	/**
	 * @inheritDoc
	 */
	public getAGALUVCode(shader: ShaderBase, animationSet: ParticleAnimationSet, registerCache: ShaderRegisterCache, animationRegisterData: AnimationRegisterData): string {
		let code: string = '';

		const uvConst: ShaderRegisterElement = registerCache.getFreeVertexConstant();
		animationRegisterData.setRegisterIndex(this, ParticleUVState.UV_INDEX, uvConst.index);

		const axisIndex: number = this._axis == 'x' ? 0 : (this._axis == 'y' ? 1 : 2);

		const target: ShaderRegisterElement = new ShaderRegisterElement(animationRegisterData.uvTarget.regName, animationRegisterData.uvTarget.index, axisIndex);

		const sin: ShaderRegisterElement = registerCache.getFreeVertexSingleTemp();

		if (this._scale != 1)
			code += 'mul ' + target + ',' + target + ',' + uvConst + '.y\n';

		code += 'mul ' + sin + ',' + animationRegisterData.vertexTime + ',' + uvConst + '.x\n';
		code += 'sin ' + sin + ',' + sin + '\n';
		code += 'add ' + target + ',' + target + ',' + sin + '\n';

		return code;
	}

	/**
	 * @inheritDoc
	 */
	public getAnimationState(animator: AnimatorBase): ParticleUVState {
		return <ParticleUVState> animator.getAnimationState(this);
	}

	private updateUVData(): void {
		this._iUvData = new Vector3D(Math.PI * 2 / this._cycle, this._scale, 0, 0);
	}

	/**
	 * @inheritDoc
	 */
	public _iProcessAnimationSetting(particleAnimationSet: ParticleAnimationSet): void {
		particleAnimationSet.hasUVNode = true;
	}
}