/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

import { Vector2 } from '../math/Vector2.js';
import { Vector3 } from '../math/Vector3.js';
import { Matrix4 } from '../math/Matrix4.js';
import { Triangle } from '../math/Triangle.js';
import { Object3D } from '../core/Object3D.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
import { InterleavedBuffer } from '../core/InterleavedBuffer.js';
import { InterleavedBufferAttribute } from '../core/InterleavedBufferAttribute.js';
import { SpriteMaterial } from '../materials/SpriteMaterial.js';

var geometry;

function Sprite( material ) {

	Object3D.call( this );

	this.type = 'Sprite';

	if ( geometry === undefined ) {

		geometry = new BufferGeometry();

		var float32Array = new Float32Array( [
			- 0.5, - 0.5, 0, 0, 0,
			0.5, - 0.5, 0, 1, 0,
			0.5, 0.5, 0, 1, 1,
			- 0.5, 0.5, 0, 0, 1
		] );

		var interleavedBuffer = new InterleavedBuffer( float32Array, 5 );

		geometry.setIndex( [ 0, 1, 2,	0, 2, 3 ] );
		geometry.addAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) );
		geometry.addAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) );

	}

	this.geometry = geometry;
	this.material = ( material !== undefined ) ? material : new SpriteMaterial();

	this.center = new Vector2( 0.5, 0.5 );

}

Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {

	constructor: Sprite,

	isSprite: true,

	raycast: ( function () {

		var intersectPoint = new Vector3();
		var worldScale = new Vector3();
		var mvPosition = new Vector3();

		var alignedPosition = new Vector2();
		var rotatedPosition = new Vector2();
		var viewWorldMatrix = new Matrix4();

		var vA = new Vector3();
		var vB = new Vector3();
		var vC = new Vector3();

		var uvA = new Vector2();
		var uvB = new Vector2();
		var uvC = new Vector2();

		function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) {

			// compute position in camera space
			alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );

			// to check if rotation is not zero
			if ( sin !== undefined ) {

				rotatedPosition.x = ( cos * alignedPosition.x ) - ( sin * alignedPosition.y );
				rotatedPosition.y = ( sin * alignedPosition.x ) + ( cos * alignedPosition.y );

			} else {

				rotatedPosition.copy( alignedPosition );

			}


			vertexPosition.copy( mvPosition );
			vertexPosition.x += rotatedPosition.x;
			vertexPosition.y += rotatedPosition.y;

			// transform to world space
			vertexPosition.applyMatrix4( viewWorldMatrix );

		}

		return function raycast( raycaster, intersects ) {

			worldScale.setFromMatrixScale( this.matrixWorld );
			viewWorldMatrix.getInverse( this.modelViewMatrix ).premultiply( this.matrixWorld );
			mvPosition.setFromMatrixPosition( this.modelViewMatrix );

			var rotation = this.material.rotation;
			var sin, cos;
			if ( rotation !== 0 ) {

				cos = Math.cos( rotation );
				sin = Math.sin( rotation );

			}

			var center = this.center;

			transformVertex( vA.set( - 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
			transformVertex( vB.set( 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
			transformVertex( vC.set( 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos );

			uvA.set( 0, 0 );
			uvB.set( 1, 0 );
			uvC.set( 1, 1 );

			// check first triangle
			var intersect = raycaster.ray.intersectTriangle( vA, vB, vC, false, intersectPoint );

			if ( intersect === null ) {

				// check second triangle
				transformVertex( vB.set( - 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
				uvB.set( 0, 1 );

				intersect = raycaster.ray.intersectTriangle( vA, vC, vB, false, intersectPoint );
				if ( intersect === null ) {

					return;

				}

			}

			var distance = raycaster.ray.origin.distanceTo( intersectPoint );

			if ( distance < raycaster.near || distance > raycaster.far ) return;

			intersects.push( {

				distance: distance,
				point: intersectPoint.clone(),
				uv: Triangle.getUV( intersectPoint, vA, vB, vC, uvA, uvB, uvC, new Vector2() ),
				face: null,
				object: this

			} );

		};

	}() ),

	clone: function () {

		return new this.constructor( this.material ).copy( this );

	},

	copy: function ( source ) {

		Object3D.prototype.copy.call( this, source );

		if ( source.center !== undefined ) this.center.copy( source.center );

		return this;

	}


} );

export { Sprite };
