/**
 * MikuMikuDance VMD形式のパーサ
 *
 * @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.papervision3d.core.math.*;
	import org.tarotaro.flash.pv3d.*;

	public class VMDLoaderThread extends Thread
	{
		private const sjis:String = "shift-jis";
		private var _scaling:Number;

		private var _controller:VMDController;
		public function get controller():VMDController { return _controller; }

		private var _loaderThread:SerialExecutor;

		/**
		 *
		 * @param	url
		 * @param	pmdc
		 * @param	scaling
		 */
		public function VMDLoaderThread(url:String, pmdc:PMDController, scaling:Number=1.0):void
		{
			_scaling = scaling;
			_controller = new VMDController(pmdc);

			var dataLoader:ZipLoader = new ZipLoader();
			_loaderThread = new SerialExecutor();
			_loaderThread.addThread( new TraceThread("読み込み開始: " + url) );
			_loaderThread.addThread( new URLLoaderThreadPatch(new URLRequest(url), dataLoader) ); // URLLoaderThreadの不具合に対する暫定処置
			_loaderThread.addThread( new TraceThread("読み込みを完了、パース開始: " + url) );
			_loaderThread.addThread( new FunctionThread(function ():void { parse_it(dataLoader.data); }) );
			// 注: new FunctionThread(parse_it, dataLoader.data) にすると
			// _loaderThreadを動かす前のdataLoader.dataの値、つまりnullが引数になるのでNG
		}

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

		override protected function run():void
		{
			trace("VMDLoaderThread running.");
			_loaderThread.start();
			_loaderThread.join();
		}

		override protected function finalize():void
		{
			trace("VMDLoaderThread finalized.");
		}

		private function parse_it(vmd:ByteArray):void
		{
			vmd.endian = Endian.LITTLE_ENDIAN;
			// VMD形式について http://jbbs.livedoor.jp/bbs/read.cgi/music/23040/1219738115/967-983

			//------------------------------------
			// 各種一時変数
			var i:int, frame:int;

			//------------------------------------
			// ヘッダ
			var header:String = vmd.readMultiByte(30, sjis);
			if (header.indexOf("Vocaloid Motion Data 0002") != 0) throw new Error("VMD形式ではありません");
			var model_name:String = vmd.readMultiByte(20, sjis);

			//------------------------------------
			// ボーン
			var bone_data_count:uint = vmd.readUnsignedInt();
			var bary:ByteArray = new ByteArray();
			for (i = 0; i < bone_data_count; i++)
			{
				var boneName:String = vmd.readMultiByte(15, sjis); // bone_name
				frame = vmd.readUnsignedInt(); // frame_no
				var move:Number3D = new Number3D(vmd.readFloat() * _scaling, vmd.readFloat() * _scaling, vmd.readFloat() * _scaling);
				var rotation:Quaternion = new Quaternion( -vmd.readFloat(), -vmd.readFloat(), -vmd.readFloat(), vmd.readFloat());
				rotation.normalize();

				var param:VMDBoneParam = new VMDBoneParam(move, rotation);

				// interpolations
				vmd.readBytes(bary, 0, 64);
				param.setInterpolationsByByteArray(bary);

				_controller.addBoneParam(boneName, frame, param);
			}

			//------------------------------------
			// Skin
			var skin_data_count:uint = vmd.readUnsignedInt();
			for (i = 0; i < skin_data_count; i++)
			{
				// (skinName, frame, weight)
				_controller.addSkinParam(vmd.readMultiByte(15, sjis), vmd.readUnsignedInt(), vmd.readFloat());
			}

			//------------------------------------
			// カメラや光源のデータが続くが、今回は必要ないので無視

			//------------------------------------
			// 終了処理
			_controller.fixFrameParams();

			trace("VMDロード完了");
		}
	}
}

