package classes.away3d.loaders
{
    import classes.away3d.arcane;
	import classes.away3d.animators.*;
    import classes.away3d.core.base.*;
    import classes.away3d.core.utils.*;
	import classes.away3d.loaders.data.*;
	import classes.away3d.loaders.utils.*;
    import classes.away3d.materials.*;
    
    import flash.geom.*;
    import flash.utils.*;
	
	use namespace arcane;
	
    /**
    * File loader for the Md2 file format.
    * 
    * @author Philippe Ajoux (philippe.ajoux{at}gmail.com)
    */
    public class Md2 extends AbstractParser
    {
    	private var animationLibrary:AnimationLibrary;
    	private var _animationData:AnimationData;
    	private var _defaultAnimationClip:AnimationData;
    	private var _vertices:Vector.<Vertex> = new Vector.<Vertex>();
    	private var _uvs:Vector.<UV> = new Vector.<UV>();
    	
    	private function buildAnimations():void
		{
		   	
			for each (var _animationData:AnimationData in animationLibrary)
			{
				switch (_animationData.animationType)
				{
					case AnimationDataType.SKIN_ANIMATION:
						break;
					case AnimationDataType.VERTEX_ANIMATION:
						var animator:VertexAnimator = new VertexAnimator();
						animator.target = _container;
						animator.name = _animationData.name;
						
						for each (var frame:Vector.<Vector3D> in _animationData.frames)
							animator.addFrame(frame);
						
						for each (var vertex:Vertex in _animationData.vertices)
							animator.addVertex(vertex);
						
						animator.delay = _animationData.start;
						animator.fps = fps;
						
						_animationData.animator = animator;
						
						if (_animationData == _defaultAnimationClip)
							animator.progress = 1;
						
						break;
				}
			}
		}
		
		/** @private */
        arcane override function prepareData(data:*):void
        {
        	md2 = Cast.bytearray(data);
        	
            md2.endian = Endian.LITTLE_ENDIAN;
            
            ident = md2.readInt();
            version = md2.readInt();

            // Make sure it is valid MD2 file
            if (ident != 844121161 || version != 8)
                throw new Error("Error loading MD2 file: Not a valid MD2 file/bad version");
                
            skinwidth = md2.readInt();
            skinheight = md2.readInt();
            framesize = md2.readInt();
            num_skins = md2.readInt();
            num_vertices = md2.readInt();
            num_st = md2.readInt();
            num_tris = md2.readInt();
            num_glcmds = md2.readInt();
            num_frames = md2.readInt();
            offset_skins = md2.readInt();
            offset_st = md2.readInt();
            offset_tris = md2.readInt();
            offset_frames = md2.readInt();
            offset_glcmds = md2.readInt();
            offset_end = md2.readInt();

            var i:uint;
            // Vertice setup
            //      Be sure to allocate memory for the vertices to the object
            //      These vertices will be updated each frame with the proper coordinates
            for (i = 0; i < num_vertices; ++i)
                _vertices.push(new Vertex());
				
			// map
			md2.position = offset_skins;
			var url:String = "";
			var char:uint;
			for (i = 0; i < 64; ++i) {
				char = md2.readUnsignedByte();
				if (char == 0)
					break;
				url += String.fromCharCode(char);
			}
			
			//overridden by the material property in constructor
			if (material) {
				mesh.material = material;
			} else if(url.substring(url.length -4, url.length -3) == "."){
				if(url.toLowerCase().indexOf("pcx") != -1){
					url = url.substring(-1, url.length -3) + pcxConvert;
				}
				trace("Material source: "+url+". Pass pcxConvert:'gif' or 'png' to load other file types. Filename remains unchanged");
				mesh.material = new BitmapFileMaterial(url);
			}

            // UV coordinates
            md2.position = offset_st;
            for (i = 0; i < num_st; i++)
                _uvs.push(new UV(md2.readShort() / skinwidth, 1 - ( md2.readShort() / skinheight) ));

            // Faces
            md2.position = offset_tris;
			// export requirement
			mesh.indexes = new Array();
			
            for (i = 0; i < num_tris; i++)
            {
                var a:int = md2.readUnsignedShort();
                var b:int = md2.readUnsignedShort();
                var c:int = md2.readUnsignedShort();
                var ta:int = md2.readUnsignedShort();
                var tb:int = md2.readUnsignedShort();
                var tc:int = md2.readUnsignedShort();
				
				mesh.indexes.push([a,b,c,ta,tb,tc]);
                
                mesh.addFace(new Face(_vertices[a], _vertices[b], _vertices[c], null, _uvs[ta], _uvs[tb], _uvs[tc]));
            }
            
            parseFrames(num_frames);
            
            mesh.type = ".Md2";
        }
        /** @private */
        arcane override function parseNext():void
        {
			//build animations
			buildAnimations();
			
	        notifySuccess();
        }
        
        private var md2:ByteArray;
        private var ident:int;
        private var version:int;
        private var skinwidth:int;
        private var skinheight:int;
        private var framesize:int;
        private var num_skins:int;
        private var num_vertices:int;
        private var num_st:int;
        private var num_tris:int;
        private var num_glcmds:int;
        private var num_frames:int;
        private var offset_skins:int;
        private var offset_st:int;
        private var offset_tris:int;
        private var offset_frames:int;
        private var offset_glcmds:int;
        private var offset_end:int;
    	private var mesh:Mesh;
        
        private function parseFrames(num_frames:int):void
        {
        	md2.position = offset_frames;
        	
        	_defaultAnimationClip = animationLibrary.addAnimation("default");
        	_defaultAnimationClip.animationType = AnimationDataType.VERTEX_ANIMATION;
        	_defaultAnimationClip.start = 0;
        	_defaultAnimationClip.vertices = _vertices;
        	
			for (var i:uint = 0; i < num_frames; i++)
            {
                var frame:Vector.<Vector3D> = new Vector.<Vector3D>();
                
                var sx:Number = md2.readFloat();
                var sy:Number = md2.readFloat();
                var sz:Number = md2.readFloat();
                
                var tx:Number = md2.readFloat();
                var ty:Number = md2.readFloat();
                var tz:Number = md2.readFloat();

                var name:String = "";
                
                for (var j:int = 0; j < 16; j++)
                {
                    var char:int = md2.readUnsignedByte();
                    if (char != 0) {
                    	var str:String = String.fromCharCode(char);
                    	if (isNaN(Number(str)))
                        	name += str;
                    }
                    
				}
				
				if (!animationLibrary[name]) {
					Debug.trace(" + Parse Animation : " + name);
					
					_animationData = animationLibrary.addAnimation(name);
					_animationData.animationType = AnimationDataType.VERTEX_ANIMATION;
					_animationData.start = 0;
					_animationData.vertices = _vertices;
				} else {
					_animationData = animationLibrary.getAnimation(name);
				}
				
				_animationData.frames.push(frame);
				_defaultAnimationClip.frames.push(frame);
				
                //mesh.geometry.framenames[name] = i;
                //mesh.geometry.frames[i] = frame;
                for (var h:int = 0; h < _vertices.length; h++)
                {
                    var position:Vector3D = new Vector3D();
                    position.x = -((sx * md2.readUnsignedByte()) + tx) * scaling;
                    position.z = ((sy * md2.readUnsignedByte()) + ty) * scaling;
                    position.y = ((sz * md2.readUnsignedByte()) + tz) * scaling;
                    md2.readUnsignedByte(); // "vertex normal index"
                    frame.push(position);
                }
            }
        }
        
    	/**
    	 * Extension to use if .pcx format encountered. Defaults to jpg.
    	 */
        public var pcxConvert:String;
        
    	/**
    	 * A scaling factor for all geometry in the model. Defaults to 1.
    	 */
        public var scaling:Number;
        
    	/**
    	 * defines the global frames per second for all animations in the Md2 file.
    	 */
        public var fps:Number;
        
		/**
		 * Creates a new <code>Md2</code> object. Not intended for direct use, use the static <code>parse</code> or <code>load</code> methods.
		 * 
		 * @param	data				The binary data of a loaded file.
		 * @param	init	[optional]	An initialisation object for specifying default instance properties.
		 * 
		 * @see away3d.loaders.Md2#parse()
		 * @see away3d.loaders.Md2#load()
		 */
        public function Md2(init:Object = null)
        {
            super(init);
            
			pcxConvert = ini.getString("pcxConvert", "jpg");
            scaling = ini.getNumber("scaling", 1) * 100;
			fps = ini.getNumber("fps", 10);
			
            mesh = (_container = new Mesh(ini)) as Mesh;
            
            animationLibrary = _container.animationLibrary = new AnimationLibrary();
            
            binary = true;
        }

		/**
		 * Creates a 3d mesh object from the raw binary data of an md2 file.
		 * 
		 * @param	data				The binary data of a loaded file.
		 * @param	init	[optional]	An initialisation object for specifying default instance properties.
		 * 
		 * @return						A 3d mesh object representation of the md2 file.
		 */
        public static function parse(data:*, init:Object = null):Mesh
        {
            return Loader3D.parse(data, Md2, init).handle as Mesh;
        }
    	
    	/**
    	 * Loads and parses an md2 file into a 3d mesh object.
    	 * 
    	 * @param	url					The url location of the file to load.
    	 * @param	init	[optional]	An initialisation object for specifying default instance properties.
    	 * @return						A 3d loader object that can be used as a placeholder in a scene while the file is loading.
    	 */
        public static function load(url:String, init:Object = null):Loader3D
        {
            return Loader3D.load(url, Md2, init);
        }
    }
}