///<reference path="all.ts"/>
module jg {
	/**
	 * 画像やサウンドなどのリソースを管理するクラス。
	 * このクラスはjgame.jsでは例外的な単一インスタンスのクラスであるため、複数ゲームを1ページに読み込む場合は注意が必要
	 */
	export class Resource {
		/** 読み込み済みの画像 */
		images: {[key:string]: HTMLImageElement; };
		/** 読み込み済みのスクリプト */
		scripts: {[key:string]: HTMLScriptElement; };
		/** 読み込み済みのサウンド */
		sounds: {[key:string]: any; };
		/** 読み込み予定の識別子リスト */
		requests: string[];
		/**
		 * 読み込みが完了するたびに発火されるイベント。
		 * パラメータで残りリクエスト数を受け取るため、全読み込み完了はloaded.handle((e) => {if (e == 0) console.log("completed");});などで判定可能 */
		loaded: Trigger;
		/**
		 * リソースの読み込みリクエストが追加される度に発火されるイベント。 
		 * パラメータとして、{name: リソース識別子, url: リソースのURL, loader: ResourceLoaderクラス}を持つ
		 */
		added: Trigger;
		/** 
		 * このクラスが管理しているResourceLoader。拡張子ごとのResourceLoaderのマップになっている。
		 * デフォルトでは以下の通り。"default"は登録されていないすべての拡張子が該当する。
		 * "js": ScriptResourceLoader
		 * "default": ImageResourceLoader
		 * "mp3": SoundResourceLoader
		 * "ogg": SoundResourceLoader
		 * "wav": SoundResourceLoader
		 * "mid": SoundResourceLoader
		 */
		loaders: {[key:string]: ResourceLoader; };
		/** このリソースの現在の構造 */
		structure: ResourceStructure;

		/** 単一のインスタンス */
		static instance: Resource;

		/**
		 * 唯一のインスタンスを取得する。
		 */
		static getInstance():Resource {
			return () => {
				if (! Resource.instance)
					Resource.instance = new Resource();
				return Resource.instance;
			}();
		}

		/**
		 * 新しいResourceクラスを生成するが、この方法でResourceクラスを生成するべきではなく、jgame.jsではgetInstanceによる単一インスタンスでの利用を推奨している
		 */
		constructor() {
			this.requests = [];
			this.loaded = new Trigger();
			this.added = new Trigger();
			this.clear();
			this.loaders = {};
			this.loaders["js"] = new ScriptResourceLoader(this);
			this.loaders["default"] = new ImageResourceLoader(this);
			this.loaders["mp3"] = new SoundResourceLoader(this);
			this.loaders["ogg"] = this.loaders["mp3"];
			this.loaders["wav"] = this.loaders["mp3"];
			this.loaders["mid"] = this.loaders["mp3"];
			this.structure = ResourceStructure.Default;
		}

		/**
		 * すべてのリソースを解放する
		 */
		clear() {
			this.images = {};
			this.scripts = {};
			this.sounds = {};
		}

		/**
		 * 画像リソースを取得する
		 */
		get(name:string) {
			return this.images[name];
		}

		/**
		 * サウンドリソースを取得する
		 */
		sound(name:string) {
			return this.sounds[name];
		}

		/**
		 * リクエストの完了を通知する
		 * @param name リクエストが完了したリソースの識別子
		 */
		requestCompleted(name:string) {
			for (var i=0; i<this.requests.length; i++) {
				if (this.requests[i] == name) {
					this.requests.splice(i, 1);
					break;
				}
			}
			this.loaded.fire(this.requests.length);
		}

		/**
		 * リソースを読み込む
		 * @param name リソースの識別子。url省略時は、urlと同じ値になる
		 * @param url リソースのURL
		 */
		load(name:string, url?:string) {
			if (! url)
				url = name;

			this.requests.push(name);

			var dot = url.split(/\./g);
			var ext;
			if (dot.length == 0)
				ext = "";
			else
				ext = dot[dot.length - 1];

			ext = ext.toLowerCase();
			var loader = this.loaders[ext] ? this.loaders[ext] : this.loaders["default"];
			loader.load(url, name);

			this.added.fire({
				name: name,
				url: url,
				loader: loader
			});
		}

		/**
		 * 手動で読み込むを行う。
		 * このメソッド内でResourceクラスは何も処理をせず、ただリクエストを登録する。
		 * ロードが完了したリソースは、別途completeManualを通じてResourceクラスに読み込み完了を手動通知する必要がある。
		 * 主に、外部ライブラリでの読み込み処理での利用を想定している。
		 * @param name リソース識別子
		 */
		loadManual(name:string) {
			this.requests.push(name);
			this.added.fire({
				name: name,
				url: null,
				loader: null
			});
		}

		/**
		 * 手動で読み込み完了を行う。通常はloadManualとセットで利用する
		 * @param name リソース識別子
		 */
		completeManual(name:string) {
			this.requestCompleted(name);
		}
	}
}