/*
 *  Copyright 2008 tarotarorg
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
 package org.tarotaro.flash.pv3d {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.IDataInput;
	import nochump.util.zip.ZipEntry;
	import nochump.util.zip.ZipFile;
	
	/**
	* URLRequest#urlが、http://～～/*.zip://～～/.fileだった場合：
	* １．Zipファイルがキャッシュにあればキャッシュから、なければダウンロードして、保持状態にする
	* ２．Zipファイルの中からByteCode形式でデータを取り出す。
	* URLRequest#urlがhttp://～～/*.zipだった場合：
	* ZipファイルをByteCode形式で返しつつ、キャッシュにURLとの組で保存
	* 他の場合：
	* 通常のURLLoaderと同じ
	* @author 太郎
	*/
	public class ZipLoader extends URLLoader {
		
		private static var ZIP_CACHE:Dictionary = new Dictionary();
		
		public static function clearCache():void 
		{
			ZIP_CACHE = null;
			ZIP_CACHE = new Dictionary();
		}
		/**
		 * zipファイルのURLを指す正規表現
		 */
		private static const ZIP_FILE_REG:RegExp = /.*?\.zip$/;
		/**
		 * zipファイル内のエントリを指す正規表現
		 */
		private static const ZIP_ENTRY_REG:RegExp = /(.*?\.zip):\/\/(.*$)/;
		/**
		 * ロード対象のZipファイルのURL
		 */
		private var _zipUrl:String;
		/**
		 * ロードしたZipファイルの中に入っているエントリ部分のURL。
		 * Zipファイルの頂点が「/」となる。
		 */
		private var _entryUrl:String;
		
		private var _entryDataFormat:String;
		
		private var isCompleteProcessRequired:Boolean = false;
		/**
		 * zipファイルをロードし、さらに内部のファイルを直接取り出すことが可能なZipLoaderのインスタンスを作成
		 */
		public function ZipLoader() {
			super();
			this.addEventListener(Event.COMPLETE , zipFileLoadCompleted , false , int.MAX_VALUE);
			this._zipUrl = null;
			this._entryUrl = null;
		}

		/**
		 * @see flash.net.URLLoader
		 * @param	req
		 */
		public override function load(req:URLRequest):void {
			var url:String = req.url;
			this.isCompleteProcessRequired = false;
			switch (true) {
				case ZIP_ENTRY_REG.test(url)://zip内のファイルを探している
					var result:Object = ZIP_ENTRY_REG.exec(url);
					this._zipUrl = result[1];//TODO Zipファイル部分のURL
					this._entryUrl = result[2];//TODO Zip内部のパス
					var zip:ZipFile = ZIP_CACHE[this._zipUrl] as ZipFile;
					if ( zip ) {
						//キャッシュにZipファイル有
						this.dataFromZip(zip);
						dispatchEvent(new Event(Event.COMPLETE));
					} else {
						//キャッシュに無。新規にロードする
						this.isCompleteProcessRequired = true;
						this.load(new URLRequest(zipUrl));
					}
					break;
				case ZIP_FILE_REG.test(url)://zipファイル自体をロードしようとしている
					this.isCompleteProcessRequired = true;
					this._zipUrl = url;
					this._entryDataFormat = this.dataFormat;
					this.dataFormat = URLLoaderDataFormat.BINARY;
					super.load(req);
					break;
				default://他(そのままロードする)
					super.load(req);
					break;
			}
		}
		
		/**
		 * zipファイルをネットワーク上から取得完了時のイベント処理。
		 * データをZipFileに変換し、エントリ取り出し処理を呼び出す
		 * @param	e
		 */
		private function zipFileLoadCompleted(e:Event):void {
			if (this.isCompleteProcessRequired) {
				//Zipファイルの読み込みが完了したら、ZipEntry形式にしてキャッシュに入れておく
				var zipfile:ZipFile = new ZipFile(e.target.data);
				//zipファイルをキャッシュに格納
				ZIP_CACHE[this._zipUrl] = zipfile;
				this.dataFromZip(zipfile);
				this.dataFormat = this._entryDataFormat;
				if (!this.data) {
					//データが取得できなかった場合、this.dataFromZipでIOErrorEventをディスパッチしているので、
					//Event.COMPLETEは止める。
					e.stopImmediatePropagation();
				}
			}
		}
		
		/**
		 * zipファイルのロード及びエントリの取り出し処理完了時に呼ばれる
		 * @param	zip			zipファイル
		 */
		private function dataFromZip(zip:ZipFile):void 
		{
			if ( zip && this.entryUrl) {
				this.data = this.getZipEntry(zip, this.entryUrl);
			}
		}
		
		private function getZipEntry(zip:ZipFile, entryUrl:String):ByteArray
		{
			if (ZIP_ENTRY_REG.test(entryUrl)) {
				//zipファイル内のzipファイルを参照している(foo.zip://bar.txtなど)
				var result:Object = ZIP_ENTRY_REG.exec(entryUrl);
				var outerEntry:String = result[1];
				var innerEntry:String = result[2];
				var innerZip:ZipFile = new ZipFile(zip.getInput(zip.getEntry(outerEntry)));
				return this.getZipEntry(innerZip, innerEntry);
			} else {
				//zipファイルからエントリを探し出し、byteArrayにして返す
				var entry:ZipEntry = zip.getEntry(entryUrl);
				if (entry) {
					return zip.getInput(entry);
				} else {
					dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
					return null;
				}
			}
			
		}
		public function get zipUrl():String { return _zipUrl; }
		
		public function get entryUrl():String { return _entryUrl; }
	}
	
}