/**
* @author b2ox
*/
package org.b2ox.pv3d.MikuMikuDance
{
	import flash.events.*;
	import flash.net.*;
	import flash.utils.*;
	import org.b2ox.pv3d.*;
	import org.b2ox.pv3d.MikuMikuDance.*;
	import org.b2ox.thread.*;
	import org.libspark.thread.*;
	import org.libspark.thread.threads.net.*;
	import org.libspark.thread.threads.utils.*;
	import org.libspark.thread.utils.*;
	import org.tarotaro.flash.pv3d.*;

	public class PMDLoaderThread extends Thread
	{
		//---------------------------------------------------------------------
		// 内部定数
		private const sjis:String = "shift-jis";
		private const skip_noneeds:Boolean = true; // 必要ない部分を読み飛ばす

		//---------------------------------------------------------------------
		// 内部的なパラメータ類
		private var _mmd:MikuMikuDance;
		private var _pmdController:PMDController;
		private var _scaling:Number;
		private var _loaderThread:SerialExecutor;

		//---------------------------------------------------------------------
		/**
		 * コンストラクタ
		 *
		 * @param	url
		 * @param	scaling
		 */
		public function PMDLoaderThread(mmd:MikuMikuDance, url:String, scaling:Number=1.0):void
		{
			this._mmd = mmd;
			this._pmdController = mmd.pmdController;
			this._scaling = scaling;

			var req:URLRequest = new URLRequest(url);
			var sourceDir:String = url.slice(0, url.lastIndexOf("/") + 1);
			var dataLoader:ZipLoader = new ZipLoader();
			_loaderThread = new SerialExecutor();
			_loaderThread.addThread( new URLLoaderThread(req, dataLoader) );
			_loaderThread.addThread( new TraceThread("読み込みを完了、パース開始: " + url) );
			_loaderThread.addThread( new FunctionThread(function ():void { parse_it(dataLoader.data, sourceDir); }) );
			// 注: new FunctionThread(parse_it, dataLoader.data, sourceDir) にすると
			// _loaderThreadを動かす前のdataLoader.dataの値、つまりnullが引数になるのでNG
		}

		override protected function run():void
		{
			_loaderThread.start();
			_loaderThread.join();
		}

		/**
		 * 
		 * @param	func: function (pmd:PMDLoaderThread) {}
		 * @return
		 */
		public function $next(func:Function):PMDLoaderThread
		{
			_loaderThread.addThread(new FunctionThread(func, this));
			return this;
		}

		/**
		 * PMD読み込みのメインルーチン
		 * @param	pmd
		 * @param	sourceDir
		 */
		private function parse_it(pmd:ByteArray, sourceDir:String):void
		{
			pmd.endian = Endian.LITTLE_ENDIAN;
			// PMD形式について http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4

			//------------------------------------
			// 各種一時変数
			var x:Number, y:Number, z:Number, u:Number, v:Number, w:Number;
			var i:int, j:int, r:int, g:int, b:int;
			var count0:uint;
			var str:String;

			//------------------------------------
			// ヘッダ
			if (pmd.readMultiByte(3, sjis) != "Pmd") throw new Error("PMD形式ではありません。");
			_mmd.version = pmd.readFloat();
			_mmd.modelName = pmd.readMultiByte(20, sjis);
			_mmd.comment = pmd.readMultiByte(256, sjis);

			trace(_mmd.modelName);
			trace(_mmd.comment);

			//------------------------------------
			// 頂点
			var vertex_count:uint = pmd.readUnsignedInt();
			_mmd.initVertexUVarrays(vertex_count);
			// var normals:Array = new Array(vertex_count); // 今は使ってない
			_pmdController.initBind(vertex_count);
			for (i = 0; i < vertex_count; i++) {
				x = pmd.readFloat() * _scaling;
				y = pmd.readFloat() * _scaling;
				z = pmd.readFloat() * _scaling;
				_mmd.regVertex(i, x, y, z);
				_pmdController.regVertex(i, x, y, z);
				x = pmd.readFloat();
				y = pmd.readFloat();
				z = pmd.readFloat();
				// normals[i] = new Vertex3D(x, y, z); // 今は使ってない
				u = pmd.readFloat();
				v = 1.0 - pmd.readFloat();
				_mmd.regUV(i, u, v);
				// (i, bone0, bone1, weight, edge)
				_pmdController.regBind(i, pmd.readUnsignedShort(), pmd.readUnsignedShort(), pmd.readByte(), pmd.readByte());
			}
			trace("parser: 頂点登録完了。頂点数: " + vertex_count);

			//------------------------------------
			// 面
			var face_count:uint = pmd.readUnsignedInt() / 3; // 各面3個の頂点インデックス
			var v0:int, v1:int, v2:int;
			_mmd.initTriangleArray(face_count);
			for (i = 0; i < face_count; i++) {
				v0 = pmd.readUnsignedShort();
				v1 = pmd.readUnsignedShort();
				v2 = pmd.readUnsignedShort();
				_mmd.regTriangle(i, v0, v1, v2);
			}
			trace("parser: 面登録完了。面数: "+face_count);

			//------------------------------------
			// 材質
			_mmd.initMaterials();
			var material_count:uint = pmd.readUnsignedInt();
			var faces:Array = _mmd.geometry.faces;
			var faceOffset:uint = 0;
			var fillColor:uint = 0;
			for (i = 0; i < material_count; i++) {
				r = pmd.readFloat() * 255;
				g = pmd.readFloat() * 255;
				b = pmd.readFloat() * 255;
				fillColor = (r << 16) | (g << 8) | b;
				if (skip_noneeds) // 読み飛ばし
				{
					pmd.position += 34;
				} else {
					pmd.readFloat(); // alpha
					pmd.readFloat(); // specularity
					r = pmd.readFloat() * 255; // specular
					g = pmd.readFloat() * 255;
					b = pmd.readFloat() * 255;
					r = pmd.readFloat() * 255; // ambient
					g = pmd.readFloat() * 255;
					b = pmd.readFloat() * 255;
					pmd.readByte(); // toon
					pmd.readByte(); // edge
				}
				count0 = pmd.readUnsignedInt() / 3; // 適用する面数
				str = pmd.readMultiByte(20, sjis); // texture
				str = str.replace(/^\.\//, ''); // 相対パス対策
				_mmd.regMaterial("mat" + i.toString(), str == "" ? "" : sourceDir + str, fillColor, faceOffset, count0);
				faceOffset += count0;
			}
			trace("parser: 材質登録完了。材質数: " + material_count);

			//------------------------------------
			// ボーン
			var bone_count:uint = pmd.readUnsignedShort();
			var pbone:PMDBone;
			for (i = 0; i < bone_count; i++) {
				// (bone name, parent id, tail id, bone type, IK parent id)
				pbone = new PMDBone(pmd.readMultiByte(20, sjis), pmd.readUnsignedShort(), pmd.readUnsignedShort(), pmd.readByte(), pmd.readUnsignedShort());
				pbone.initTranslation(pmd.readFloat() * _scaling, pmd.readFloat() * _scaling, pmd.readFloat() * _scaling);
				_pmdController.addBone(pbone);
				trace("bone[" + i + "] = \"" + pbone.name + "\"");
			}
			_pmdController.bindBones();
			trace("parser: ボーン処理完了。ボーン数: " + bone_count);

			//------------------------------------
			// IK
			var IK_count:uint = pmd.readUnsignedShort();
			for (i = 0; i < IK_count; i++) {
				var boneID:int = pmd.readUnsignedShort(); // bone id
				var chain:Array = new Array();
				chain.push( pmd.readUnsignedShort() ); // effector id(chain top)
				count0 = pmd.readUnsignedByte();
				chain.length = count0 + 1;
				var iterations:int = pmd.readUnsignedShort(); // iterations
				w = pmd.readFloat(); // weight
				for (j = 1; j < count0+1; j++) {
					chain[j] = pmd.readUnsignedShort(); // chain id
				}
				_pmdController.regIKparams(boneID, iterations, w, chain);
			}
			trace("parser: IK処理完了。IK数: " + IK_count);

			//------------------------------------
			// Skin
			var pskin:PMDSkin;
			var Skin_count:uint = pmd.readUnsignedShort();
			for (i = 0; i < Skin_count; i++) {
				// (skin name, vertex count, skin type)
				pskin = new PMDSkin(pmd.readMultiByte(20, sjis), pmd.readUnsignedInt(), pmd.readByte());
				count0 = pskin.size;
				for (j = 0; j < count0; j++) {
					r = pmd.readUnsignedInt(); // base skin の index
					x = pmd.readFloat() * _scaling; // base skinからの変移
					y = pmd.readFloat() * _scaling;
					z = pmd.readFloat() * _scaling;
					pskin.setVertex(j, r, x, y, z);
				}
				_pmdController.addSkin(pskin);
				trace("skin[" + i + "] = \"" + pskin.name + "\", type:" + pskin.type);
			}
			trace("parser: Skin処理完了。Skin数: " + Skin_count);

			trace("PMDロード完了")
		}
	}
}

