package classes.away3d.tools{
	
	import classes.away3d.arcane;
	import classes.away3d.core.base.*;
	import classes.away3d.materials.*;
	
	import flash.geom.*;
	
	use namespace arcane;
	
	/**
	 * Class Merge merges two meshes into one.<code>Merge</code>
	 */
	public class Merge{
		
		private var _objectspace:Boolean;
		private var _unicgeometry:Boolean;
		private var _keepMaterial:Boolean;
		private var _tmp:Vector3D;
		 
		private function applyRotations(v:Vertex, t:Matrix3D):Vertex
		{
			if(_tmp == null)
				_tmp = new Vector3D();
			
			_tmp.x = v.x;
			_tmp.y = v.y;
			_tmp.z = v.z;
			
			_tmp = t.deltaTransformVector(_tmp);
			
			v.setValue(_tmp.x, _tmp.y, _tmp.z);
			
			return v;
		}

		private function merge(mesh1:Mesh, mesh2:Mesh):void
		{
			var i:int;
			var face:Face;
			var mat:Material = (_keepMaterial)? null: mesh2.material as Material;
			var facemat:Material;
			var v0:Vertex;
			var v1:Vertex;
			var v2:Vertex;
			var uv0:UV;
			var uv1:UV;
			var uv2:UV;
			
			if(!_objectspace){
				var t:Matrix3D = mesh2.transform;
				var scenex:Number = mesh2.scenePosition.x;
				var sceney:Number = mesh2.scenePosition.y;
				var scenez:Number = mesh2.scenePosition.z;
			}
			
			for(i= 0;i<mesh2.faces.length;++i){
				face = mesh2.faces[i];
				
				if(_unicgeometry ||!_objectspace){
					v0 = new Vertex(face.v0.x, face.v0.y, face.v0.z);
					v1 = new Vertex(face.v1.x, face.v1.y, face.v1.z);
					v2 = new Vertex(face.v2.x, face.v2.y, face.v2.z);
					uv0 = new UV(face.uv0.u, face.uv0.v);
					uv1 = new UV(face.uv1.u, face.uv1.v);
					uv2 = new UV(face.uv2.u, face.uv2.v);
				} else {
					v0 = face.v0;
					v1 = face.v1;
					v2 = face.v2;
					uv0 = face.uv0;
					uv1 = face.uv1;
					uv2 = face.uv2;
				}
				
				if(!_objectspace){
					
					v0 = applyRotations(v0, t);
					v1 = applyRotations(v1, t);
					v2 = applyRotations(v2, t);
					
					v0.x += scenex;
					v0.y += sceney;
					v0.z += scenez;
					
					v1.x += scenex;
					v1.y += sceney;
					v1.z += scenez;
					
					v2.x += scenex;
					v2.y += sceney;
					v2.z += scenez;
				}
				
				if(_keepMaterial && face.material != null){
					facemat = face.material;
				} else{
					facemat = mat;
				}
				
				mesh1.geometry.addFace(new Face(v0, v1, v2, facemat, uv0, uv1, uv2 ) );
			}
			 
		}
		 
		/**
		* @param	 objectspace		[optional] Boolean. Defines if mesh2 is merge using its objectspace or worldspace. Default is true.
		* @param	 unicgeometry	[optional] Boolean. Defines if the receiver object must generate new vertexes, uv's or uses mesh2's. Default is false.
		* @param	 keepMaterial		[optional] Boolean. Defines if the receiver object must use the mesh2 material information. If false the merge information from mesh2 gets the mesh1 material. Default is false.
		* If keepMaterial is set to false, mesh2 receives mesh1 material, code assumes the material is not applied at faces level. Instead mesh1 material needs to applied as mesh.material = somematerial;
		* If set to true. mesh2 can have multiple materials at face level or applied as single material. Face material will get then priority if both case is found.
		*/
		
		function Merge(objectspace:Boolean = true, unicgeometry:Boolean = false, keepMaterial:Boolean = false):void
		{
			_objectspace = objectspace;
			_unicgeometry = unicgeometry;
			_keepMaterial = keepMaterial;
		}
		
		/**
		*  Merges two meshes into one.
		* 
		* @param	 mesh1				Mesh. The receiver object that will hold both meshes information.
		* @param	 mesh2				Mesh. The Mesh object to be merge with mesh1.
		*/
		public function apply(mesh1:Mesh, mesh2:Mesh):void
		{
			merge(mesh1, mesh2);
		}
		
		/**
		* Defines if mesh2 is merged using its objectspace.
		*/
		public function set objectspace(b:Boolean):void
		{
			_objectspace = b;
		}
		
		public function get objectspace():Boolean
		{
			return _objectspace;
		}
		/**
		* Defines if mesh2 will be merged using new instances of UV's and Vertexes or shared from mesh1.
		*/
		public function set unicgeometry(b:Boolean):void
		{
			_unicgeometry = b;
		}
		
		public function get unicgeometry():Boolean
		{
			return _unicgeometry;
		}
		/**
		* Defines if mesh2 will be merged using its own material information.
		*/
		public function set keepmaterial(b:Boolean):void
		{
			_keepMaterial = b;
		}
		
		public function get keepMaterial():Boolean
		{
			return _keepMaterial;
		}
		
		
	}
}