/**
 * @author sunag / http://www.sunag.com.br/
 * @thanks bhouston / https://clara.io/
 */

THREE.FunctionNode = function( src, includes, extensions ) {

	THREE.GLNode.call( this );

	this.parse( src || '', includes, extensions );

};

THREE.FunctionNode.prototype = Object.create( THREE.GLNode.prototype );
THREE.FunctionNode.prototype.constructor = THREE.FunctionNode;

THREE.FunctionNode.prototype.parseReference = function( name ) {

	switch ( name ) {
		case 'uv': return new THREE.UVNode().name;
		case 'uv2': return new THREE.UVNode( 1 ).name;
		case 'position': return new THREE.PositionNode().name;
		case 'worldPosition': return new THREE.PositionNode( THREE.PositionNode.WORLD ).name;
		case 'normal': return new THREE.NormalNode().name;
		case 'normalPosition': return new THREE.NormalNode( THREE.NormalNode.WORLD ).name;
		case 'viewPosition': return new THREE.PositionNode( THREE.NormalNode.VIEW ).name;
		case 'viewNormal': return new THREE.NormalNode( THREE.NormalNode.VIEW ).name;
	}

	return name;

};

THREE.FunctionNode.prototype.getTypeNode = function( builder, type ) {

	return builder.getType( type ) || type;

};

THREE.FunctionNode.prototype.getInputByName = function( name ) {

	var i = this.inputs.length;

	while ( i -- ) {

		if ( this.inputs[ i ].name === name )
			return this.inputs[ i ];

	}

};

THREE.FunctionNode.prototype.getType = function( builder ) {

	return this.getTypeNode( builder, this.type );

};

THREE.FunctionNode.prototype.getInclude = function( name ) {

	var i = this.includes.length;

	while ( i -- ) {

		if ( this.includes[ i ].name === name )
			return this.includes[ i ];

	}

	return undefined;

};

THREE.FunctionNode.prototype.parse = function( src, includes, extensions ) {

	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
	var rProperties = /[a-z_0-9]+/ig;

	this.includes = includes || [];
	this.extensions = extensions || {};

	var match = src.match( rDeclaration );

	this.inputs = [];

	if ( match && match.length == 4 ) {

		this.type = match[ 1 ];
		this.name = match[ 2 ];

		var inputs = match[ 3 ].match( rProperties );

		if ( inputs ) {

			var i = 0;

			while ( i < inputs.length ) {

				var qualifier = inputs[ i ++ ];
				var type, name;

				if ( qualifier == 'in' || qualifier == 'out' || qualifier == 'inout' ) {

					type = inputs[ i ++ ];

				}
				else {

					type = qualifier;
					qualifier = '';

				}

				name = inputs[ i ++ ];

				this.inputs.push( {
					name : name,
					type : type,
					qualifier : qualifier
				} );

			}

		}

		var match, offset = 0;

		while ( match = rProperties.exec( src ) ) {

			var prop = match[ 0 ];
			var reference = this.parseReference( prop );

			if ( prop != reference ) {

				src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );

				offset += reference.length - prop.length;

			}

			if ( this.getInclude( reference ) === undefined && THREE.NodeLib.contains( reference ) ) {

				this.includes.push( THREE.NodeLib.get( reference ) );

			}

		}

		this.src = src;

	}
	else {

		this.type = '';
		this.name = '';

	}

};
