module kyojintati4d.gameinfo;

private import std.thread;
private import std.file;
private import std.string;
private import std.cpuid;

private import y4d;
private import y4d_input.joystick;
private import y4d_thread.gamescenetransiter;
private import y4d_aux.filesys;
private import y4d_aux.cacheloader;

private import kyojintati4d.userinfo;
private import kyojintati4d.bookmark;
private import kyojintati4d.taskoption;
private import kyojintati4d.mygametaskfactory;
private import kyojintati4d.taskyesno;
private import kyojintati4d.taskscenario;
private import kyojintati4d.kyeconfig;

private import yamalib.input.adaptedkey;
private import yamalib.input.adapter;
private import yamalib.input.multimouse;

private import yamalib.serialize;
private import yamalib.auxil.properties;
//private import yamalib.auxil.tempfilemanager;
private import yamalib.auxil.filechecker;
private import yamalib.auxil.encryption;
private import yamalib.util.systeminfo;
private import yamalib.draw.tiletexture;
private import yamalib.log.log;
private import yamalib.log.performancelog;

private import yamalib.gui.dialog;

/+ 定数クラスは公開インポート +/
public import kyojintati4d.val.kyojinconst;

// システムSEの名前付け
enum SYS_SE : int {
	DISABLE=0,
	ENABLE,
	CLICK_TITLE_ACTIVE,
	CLICK_TITLE_NEGATIVE,
}

/// ゲーム情報クラス
class GameInfo : SceneTransitable {
// ------------------- ↓↓↓↓↓↓↓↓↓↓↓↓↓変数↓↓↓↓↓↓↓↓----------------------------

	static Bookmark[] bookmark;		//!< ブックマークオブジェクト配列
	static OptionInfo optionInfo;	//!< ゲームオプション情報

	// コントローラ
	GameTaskController 	gameTaskController;
	GameSceneController gameSceneController;
	GameTaskFactoryBase gameTaskFactory;
	GameSceneTransiter 	gameSceneTransiter;

	SoundLoader seloader;		//!< シナリオで使用するSELoader
	SoundLoader sysSELoader;	//!< システムで使用するSELoader
	SoundLoader bgmloader;		//!< BGMLoader
	SoundLoader voiceloader;	//!< VoiceLoader
	FontLoader fontloader;	//!< フォントローダ

	TextureLoader tl_shil;		//!< シルエット用テクスチャローダ
	TextureLoader tl_bg;		//!< 背景用テクスチャローダ
	TextureLoader tl_2ch;		//!< 2chモード用テクスチャローダ

	
// -------------------↑↑↑↑↑↑↑↑↑↑↑ 変数↑↑↑↑↑↑↑↑↑↑↑↑↑ ---------------------
	
	/// 全てのキャッシュをクリア
	void releaseAllCache() {
		Sound.stopAll();
		seloader.releaseAll();
		sysSELoader.releaseAll();
		bgmloader.releaseAll();
		voiceloader.releaseAll();
		fontloader.releaseAll();
		tl_shil.releaseAll();
		tl_bg.releaseAll();
		tl_2ch.releaseAll();
	}
	
	private static const int COLOR_32BIT = 1;
	private static const int COLOR_16BIT = 2;
	private static const int COLOR_24BIT = 3;
	
	static int[] getFullScreenTestBitColor() {
		auto prop = Properties.getInstance(cast(char[]) "setting.txt");
		int bitDef = prop.getPropertyNum(cast(char[]) "fullscreen_color_bit", 1);
		switch (bitDef) {
		case COLOR_32BIT:
			return [32,16,24,0];
		case COLOR_16BIT:
			return [16,32,24,0];
		case COLOR_24BIT:
			return [24,32,16,0];
		default:
			return [32,16,24,0];
		}
	}

	/// セーブファイルが存在しているかどうか
	bool saveFileExist() {
		return this.m_saveFileExist;
	}
	bool saveFileExist(bool b_) {
		return this.m_saveFileExist = b_;
	}
	
	/// アイキャッチタスクで、自分が表示すべきアイキャッチのストーリー番号
	int selectedStory() {
		return this.m_selectedStory;
	}
	int selectedStory(int no_) {
		return this.m_selectedStory = no_;
	}
	
	/// 選択されたブックマークの番号
	int selectedBookmark() {
		return this.m_selectedBookmark;
	}
	int selectedBookmark(int no_) {
		return this.m_selectedBookmark = no_;
	}
	
	/// 初回起動ではない
	bool restart() {
		return this.m_restart;
	}
	bool restart(bool b_) {
		return this.m_restart = b_;
	}

	/// エンディングのオプションフラグを格納する
	bool[] endingOptionFlg() {
		return this.m_endingOptionFlg;
	}
	bool[] endingOptionFlg(bool[] value) {
		return this.m_endingOptionFlg = value;
	}
	
	/// ScenarioTask でタイトル画面から遷移してきたかどうかを判断する
	bool fromTitle() {
		return this.m_fromTitle;
	}
	bool fromTitle(bool b_) {
		return this.m_fromTitle = b_;
	}
	
	/// FSPタイマの取得
	FpsTimer getFpsTimer() {
		return fpstimer;
	}

	/// マウス取得
	MouseInput mouse() { return m_mouse; }

	/// キー取得
	KeyInputBase key() { return m_key; }

	/// ジョイスティックを取得する
	JoyStick joystick() { 
		return cast(JoyStick) (cast(MousedKey) (cast(NullableKeyInput) this.key).getOriginal()).getDevice(1); 
	}
	
	/// Joystickが使用可能か？	
	bool isEnableJoystick() {
		return KeyInputNullDevice.getInstance().getDeviceName() != joystick().getDeviceName();
	}

	/// 現在使用しているマウスと、現在使用しているキーを合成したマウス
	MouseInput keyDecolateMouse() { return m_keymouse; }

	/// エラー表記用のダイアログボックス
	DialogBox errorDialg() { return m_dlgErr; };
	DialogBox errorDialg(DialogBox dlg) { return m_dlgErr = dlg; };
	
	/// ユーザ情報取得
	UserInfo userInfo() { return m_userInfo; }
	
	/// KeyConfig情報の取得
	KeyConfigData keyConfig() { return this.m_keyConfig; }

	/// ダイアログでクリックされたボタンのIDを取得する	
	TaskYesNo.CLICK yesnoClick() {
		return m_yesnoClick;	
	}
	/// ダイアログでクリックされたボタンのIDをセットする	
	TaskYesNo.CLICK yesnoClick(TaskYesNo.CLICK click) {
		return m_yesnoClick = click;	
	}

	/// ダイアログを表示する
	void showDialog(TaskYesNo.TYPE type)  {
		(cast(TaskYesNo) yesNoTask).setType(type);
		showYesNo = true;
		this.yesnoClick = TaskYesNo.CLICK.NO_CLICK;
	}
	/// ダイアログを隠す
	void hideDialog() {
		showYesNo = false;
	}
	/// ダイアログを表示しているか
	bool isShowYesNo() {
		return showYesNo;
	}

	/// 有効ボタンを押したときの音を再生する
	void playSEActive() {
		y4d_result res;
		res = this.sysSELoader.playSE(SYS_SE.ENABLE);
		Log.print("playSEActive: " ~ getErrorName(res) );
	}
	
	/// 無効ボタンをおしたときの音を再生する
	void playSENegative() {
		y4d_result res;
		res = this.sysSELoader.playSE(SYS_SE.DISABLE);
		Log.print("playSEActive: " ~ getErrorName(res) );
	}
	/// 指定したシステムSEを再生する
	void playSystemSE(int num) {
		this.sysSELoader.playSE(num);
	}
	
	/// 汎用データ引き渡し
	int[] getData() {
		return this.m_data;
	}
	
	/// 汎用データ設定
	void setData(int[] v) {
		this.m_data = v.dup;
	}
	
	/// データ初期化
	void clearData() {
		this.m_data = null;
	}
	
	/// テスト用音源を使用するか
	bool useTestMusic() {
		return this.m_useTestMusic;
	}
	bool useTestMusic(bool value) {
		return this.m_useTestMusic = value;
	}
	
	/// スクリーンを返却する
	/**
		シーントランジッタは、遷移エフェクトとして
		自分で描画するために、スクリーンを必要とする
	*/
	Screen getScreen() {
		return m_screen;
	}
	
	Screen screen() {
		return this.m_screen;
	}
	
	/// ダイアログで使用するリソースコンテナを生成する
	DialogBoxResources createDialogResources() {
		if (dialogTile is null) {
			dialogTile = new TileTexture(TileTexture.TILE_TYPE.SQUARE9);
			dialogTile.load(cast(char[]) SPRITE_DEF_FILE_NAME);
		}
		if (cursorTile is null) {
			cursorTile = new TileTexture(TileTexture.TILE_TYPE.H_LINE3);
			cursorTile.load(cast(char[]) "img/dialog/sel_bg.sdf");
		}
		DialogBoxResources dlgRes = new DialogBoxResources();
		dlgRes.setButtonLabelFontLoader(this.fontloader, KyojinConst.C_FONT_NO_DIALOG_TEXT);
		dlgRes.messageFont = this.fontloader.get(KyojinConst.C_FONT_NO_DIALOG_TEXT);
		dlgRes.dialogTile = dialogTile;
		dlgRes.cursorTile = cursorTile;
		
		return dlgRes;
	}
	
	/// メモリーの利用状況をプリントする
	static void printMemoryState() {
		char[] formatNumCamma(int num) {
			char[] str = cast(char[]) std.string.toString(num);
			char[] buffer;
			for (int i = str.length-1, count = 0; i >= 0; --i, ++count) {
				if ((count % 3) == 0 && count != 0) {
					buffer = ("," ~ buffer);
				}
				buffer = (str[i] ~ buffer);
			}
			return buffer;
		}
		
		try {
			SystemInfo.updateMemoryInfo();
			Log.printLook("PHYSICAL MEMORY SIZE = %s KB", formatNumCamma(SystemInfo.getPhysMemory()/1024) );
			Log.printLook("FREE PHYSICAL MEMORY SIZE = %s KB", formatNumCamma(SystemInfo.getAvailPhysMemory()/1024) );
			Log.printLook("PAGING FILE SIZE = %s KB", formatNumCamma(SystemInfo.getTotalPageFile()/1024) );
			Log.printLook("FREE PAGING FILE SIZE = %s KB", formatNumCamma(SystemInfo.getAvailPageFile()/1024) );
			Log.printLook("VERTUAL MEMORY SIZE = %s KB", formatNumCamma(SystemInfo.getTotalVirtual()/1024) );
			Log.printLook("FREE VERTUAL MEMORY SIZE = %s KB", formatNumCamma(SystemInfo.getAvailVirtual()/1024) );
		} catch (Object o) {
			// error...
			Log.print("CAN'T PRINT MEMORY STATE : [%s]", o.toString);
		}
	}
	
	/// コンストラクタ
	this() {
		// FPSタイマ
		fpstimer = new FpsTimer;
		fpstimer.setFps(KyojinConst.C_FSP_NOW);
		fpslayer = new FpsLayer(fpstimer);

		// フォントローダの生成
		fontloader = new FontLoader();
		// オプション情報の生成
		optionInfo = new OptionInfo();
		/* ユーザデータ生成 */
		m_userInfo = new UserInfo();
		// KeyConfigデータクラスの生成
		m_keyConfig = new KeyConfigData();

		// input関連
		m_mouse = new NullableMouseInput(new MouseInputImpl());
		m_key = new NullableKeyInput(new MousedKey());
		
		auto keyOrigin = (cast(NullableKeyInput) m_key).getOriginal();
		assert(!(keyOrigin is null));
		auto map = (cast(MouseAdaptable) keyOrigin).getAdaptKeyMapping();
		assert(!(map is null));
		
		// KEY と MOUSE を合成し、一つのマウスとして使う！
		MultiMouse aKeymouse = new MultiMouse(m_mouse);
		aKeymouse.addMouse(
			new MouseInputAdapter(
				m_key, map ) );
		m_keymouse = new NullableMouseInput(aKeymouse);
		
		Log.print("ENABLE JOYSTICK : %s", this.isEnableJoystick());

		// サウンド関連
		seloader 	= new SoundLoader();	// SELoader
		sysSELoader	= new SoundLoader();	// SELoader
		bgmloader 	= new SoundLoader();	// BGMLoader
		voiceloader = new SoundLoader();	// VoiceLoader

		// 共通画像の生成
		tl_shil	 	= new TextureLoader();
		tl_bg 		= new TextureLoader();
		tl_2ch 		= new TextureLoader();
		
		//Serialize
		serialize = new Serialize();

		// コントローラ生成
		gameTaskController 	= new GameTaskController(KyojinConst.C_MAX_TASK_NUM);
		gameSceneController = new GameSceneController();
		gameTaskFactory 	= new MyGameTaskFactory();
		gameSceneTransiter 	= new GameSceneTransiter();
		gameSceneController.setGameTaskFactory(gameTaskFactory);

		// 全タスク共有 YesNoタスク
		yesNoTask 	= new TaskYesNo(this.mouse);
		m_yesnoClick = TaskYesNo.CLICK.NO_CLICK;
	}

	/// 初期化処理
	// 事前にloadSaveInfo を読んでおかないとセーブデータが正しく読み込まれないので注意！！
	void init() {
		y4d_result res;
		Log.print("CPU ID:%s",std.cpuid.toString());
		Log.print("GameInfo.init()");
		
		// TEXTURE の設定
		Properties prop = Properties.getInstance(cast(char[]) "setting.txt");
		Texture.globalOption.NPOT = Texture.globalOption.TxRc = 
			(prop.getProperty(cast(char[]) PROP_KEY_TEXTURE_COMP, cast(char[]) "false") == "true");

		Log.print("USE TEXTURE NPOT or TxRc: %s", Texture.globalOption.NPOT);
		
		// セーブデータのロード
		loadSaveInfo();
		
		// セーブデータの内容で、Window / Fullm_screen が決まるので、
		// ロードの後にセットアップする必要がある
		setupScreen();
		
		// 画像リソース読込
		loadResources();
		
		Log.print("GAME FSP = %d", KyojinConst.C_FSP_NOW);

		// ウィンドウキャプションの設定
//		m_screen.setCaption( toMBS(KyojinConst.strAppName), "icon.bmp" );
		// ↑あとでツールでアイコン上書きするからいいや...
		m_screen.setCaption(cast(char[]) KyojinConst.strAppName, null );

		// bookmark書き込み用フォントの設定
		FontRepository fr = new FontRepository();
		fr.setLoader(fontloader, KyojinConst.C_FONT_NO_RUBI);
		fr.setMax(300);
		Bookmark.setFontRepository(fr);
		
		for(int i = 0; i < 4; ++i) {
			Bookmark bm = new Bookmark();
			bm.onInit();
			bm.setReadOnly(true);
			if (KyojinConst.BookmarkID.AUTO == i) {
				bm.setAuto();
			}

			if (saveFileExist) {
				try {
					bm.serialize(serialize);
				} catch {
					Log.printError("SAVE DATA LOST!!");
					bm.setUse(false);
				}
			}

			bookmark ~= bm;
			
//			Log.print("INIT BOOKMARK DUMP : %s", bm.toString());
		}
		Log.print("INIT FINISH.");
	}
	
	
	/// エラーメッセージを表示する
	void showError(char[] msg) {
		
	}
	
	/// メインループ
	int run() {
		// メインスレッド設定
		Texture.setMainThreadId(cast(uint) Thread.getThis().hdl);
		
		try {
			y4d_result event;
			
			// メインループ
			while( true ) {
				
				// メッセージポンプ
				event = GameFrame.pollEvent();
				switch(event) {
					case y4d_result.no_error:
						break;
					case y4d_result.should_be_quit:
						setupFinishDialog();
						break;
					default:
						// something error..
						Log.printError("event Unkown Error : %s", event);
						break;
				}
	
				try {
					// 移動処理
					if (gameSceneTransiter.callTaskOnMove(this)!=0) {
						saveAppData();
						break;
					}
				} catch (Exception e) {
					Log.printFatal("Exception GameInfo#run - gameSceneTransiter.callTaskOnMove:[%s]:[%s]", e.toString(), e.msg);
					throw e;
				}
				
				// Ctrl + C で終了ダイアログショートカット
				if ( (key.isPress(11) || key.isPress(12)) && key.isPress(13) ) {
					setupFinishDialog();
//					break;
				}
				
				// スペースキーがおされたサウンド状況をダンプ
//				if ( key.isPress(5) ) {
//					Sound.dumpChunkAll();
//				}

				// 描画待機
				fpstimer.waitFrame();
				if (!fpstimer.toBeSkip()) {
					try {
						// 描画処理
						if (gameSceneTransiter.callTaskOnDraw(this)!=0) {
							saveAppData();
							break;
						}
					} catch (Exception e) {
						Log.printFatal("Exception GameInfo#run - gameSceneTransiter.callTaskOnDraw:[%s]:[%s]", e.toString(), e.msg);
						throw e;
					}

					// 終了ボタンが押された
					if (showYesNo) {
						/* ↓のタスクの中で、quite変数を直に操作している */
						if ( yesNoTask.task(this) ) {
							showYesNo = false;
							
							if ( yesNoTask.getType() == TaskYesNo.TYPE.FINISH && TaskYesNo.CLICK.YES == yesNoTask.getClick() ) {
								Log.print("UserInput : Quit!");
								saveAppData();
								break;							
							}
						}
					}
					// フレームバッファフリップ
					m_screen.update();
				} else {
					// 描画処理間に合わない...
//					Log.print("onDraw:skip!");
				}
	
				// update の許可
				this.key.updateMark();
				this.mouse.updateMark();
				this.keyDecolateMouse.updateMark();
				this.key.update();
				this.mouse.update();
				
				try {
					this.sysSELoader.updateSE();
				} catch (Exception e) {
					Log.printFatal("Error GameInfo#run(this.sysSELoader.updateSE):[%s]:[%s]", e.toString(), e.msg);
					throw e;
				}
//				fpslayer.onDraw(m_screen,400,20);

			}
			
		} catch (Error e) {
			Log.printFatal("Error GameInfo#run:[%s]:[%s]", e.toString(), e.msg);
			return -1;
		} catch (Exception e) {
			Log.printFatal("Exception GameInfo#run:[%s]:[%s]", e.toString(), e.msg);
			return -1;
		} finally {
			// 最後になんとかアプリケーション情報をセーブしてみよう
			Log.print("finally#SEVE");

//			saveAppData();
		}

		return 0;
	}
	

	/// キャッシュの利用状況を確認する
	void checkCacheEffective() {
		Log.print("SYSTEM_SE LOADER");
		printChaheEffect(sysSELoader);

		Log.print("SE LOADER");
		printChaheEffect(seloader);
		Log.print("SE LOADER");
		printChaheEffect(bgmloader);
		Log.print("VOICE LOADER");
		printChaheEffect(voiceloader);
		Log.print("TL_SHIL LOADER");
		printChaheEffect(tl_shil);
		Log.print("TL_BG LOADER");
		printChaheEffect(tl_bg);
		Log.print("TL_2CH LOADER");
		printChaheEffect(tl_2ch);
		Log.print("FONT LOADER");
		printChaheEffect(fontloader);
	}
	
	
	/// アプリケーション情報のセーブを行う
	synchronized void saveAppData() {
		// シリアライズが生成される前に終了されることもあるのか・・・
		if (serialize is null) return;

		// 栞がないということは、初期化処理に終了されたということなので、セーブすることないだろう
		if ( (bookmark is null) ) return;

		// 栞情報書き出し
		serialize.setStoring(true);

		// オプション情報の書き出し
		optionInfo.serialize(serialize);
		// ユーザ情報の書き出し
		m_userInfo.serialize(serialize);
		// KeyConf情報の書き出し
		m_keyConfig.serialize(serialize);
		
		foreach(inout Bookmark bm; bookmark) {
			if ( !(bm is null) ) {
				bm.serialize(serialize);
//				Log.printLook("SEVE BOOKMARK DATA:%s", bm.toString());
			}
		}

		if ( FileSys.isExist(cast(char[]) KyojinConst.saveFileName) ) {
			.remove(KyojinConst.saveFileName);	// FileSysは上書きできん...
		}

		// セーブデータ保存
		serialize.save(cast(char[]) KyojinConst.saveFileName);
		createSaveIdentFile();
		
		Log.print("USER DATA SAVED.");
	}	
	
private:

	/// 画面のセットアップを行う
	void setupScreen() {
		Log.print("%s#setupScreen START",super.toString);
		this.m_screen = new Screen();
		
		this.m_screen.beginScreenTest();			// スクリーンテストを開始する
	
		Log.print("%s#setupScreen SCREEN TEST...",super.toString);
		if ( !this.optionInfo.windowMode ) {
			auto testColorBit = getFullScreenTestBitColor();
			foreach (colorBit; testColorBit) {
				Log.print("TEST COLOR : %s", colorBit);
				this.m_screen.testVideoMode(640, 480, colorBit);
			}
		} else {
			this.m_screen.testVideoMode(640, 480, 0);
			this.m_screen.testVideoMode(640, 480, 32);
			this.m_screen.testVideoMode(640, 480, 16);
			this.m_screen.testVideoMode(640, 480, 24);
		}
		Log.print("%s#setupScreen SCREEN TEST FINISH",super.toString);
		this.m_screen.endScreenTest();			// スクリーンテスト完了
		Log.print("%s#setupScreen END",super.toString);
	}

	/// セーブ情報を読み込む
	void loadSaveInfo() {
		// セーブファイルの読み込み
		serialize.setStoring(false);
		bool saveFileOk = serialize.load(cast(char[]) KyojinConst.saveFileName);
		
		// ファイル正当性チェック
		if ( saveFileOk ) {
			try {
				bool b;
				ubyte[] digest = FileChecker.readMD5Digest(
						cast(char[]) KyojinConst.C_SAVE_IDENT_FILE);
				b = FileChecker.checkFileOrigin(cast(char[]) KyojinConst.saveFileName, digest, 
						Encryption.TYPE.XOR);
				
				if ( !b ) {
					Log.printError("SEVE FILE WAS DAMAGED!");
					saveFileOk = false;
				}
			} catch {
				// なんかエラーが発生
				Log.printWarn("GameInfo#loadSaveInfo : SEVE DATA CHECK ERROR!");
				saveFileOk = false;
			}
		}

		// セーブファイルの存在フラグ
		saveFileExist = saveFileOk;
		
		// セーブファイルが存在するならば、オプション情報を再現する
		if (saveFileOk)	{
			try {
				optionInfo.serialize(serialize);
				m_userInfo.serialize(serialize);
				m_keyConfig.serialize(serialize);
				// JoyPadKey マッピング反映			
				if ( !(m_keyConfig.getDeviceName() is null) ) {
					updateJoystickMapping( getKeyData() );
				}
			} catch {
				// シリアラズ中にエラー
				Log.printFatal("############ SAVE DATE LOAD FAULT!! ##############");
				optionInfo = new OptionInfo();
				m_userInfo = new UserInfo();
				m_keyConfig = new KeyConfigData();
				saveFileExist = false;
			}
		}
		
		// VOICE と SEはサポートしない
		optionInfo.seOn = false;
		optionInfo.voiceOn = false;
		optionInfo.setVolumeSE(0);
		optionInfo.setVolumeVoice(0);
	}

	/// 共通リソースを読み込む
	void loadResources() {
		y4d_result res;

		// ユーザ定義のキャッシュサイズを取得
		Properties prop = Properties.getInstance(cast(char[]) "setting.txt");
//		prop.load("setting.txt");
		int cache_se = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_SE, 2);
		int cache_bgm = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_BGM, 32);
		int cache_voice = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_VOICE, 0);
		int cache_sys_se = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_SYSTEM_SE, 2);
		int cache_shil = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_SHILHOUETTE, 4);
		int cache_bg = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_BG, 8);
		int cache_2ch = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_2CH, 0);
		int cache_title = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_TITLE_BT, 2);
		int cache_font = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_CACHE_FONT, 32);

		// デバッグプリント
		Log.print("RESIZE CACHE : SE     %s[MB]", cache_se);
		Log.print("RESIZE CACHE : BGM    %s[MB]", cache_bgm);
		Log.print("RESIZE CACHE : VOICE  %s[MB]", cache_voice);
		Log.print("RESIZE CACHE : SYS_SE %s[MB]", cache_sys_se);
		Log.print("RESIZE CACHE : SHILHO %s[MB]", cache_shil);
		Log.print("RESIZE CACHE : BG     %s[MB]", cache_bg);
		Log.print("RESIZE CACHE : 2CH    %s[MB]", cache_2ch);
		Log.print("RESIZE CACHE : TITLE  %s[MB]", cache_title);
		Log.print("RESIZE CACHE : FONT   %s[MB]", cache_font);
		
		// スレッドロードディング方式か？
		bool threadLoading = "true" == prop.getProperty(cast(char[]) PROP_KEY_THREAD_LOADING);
		Sound.useThreadLoading( threadLoading );
		Log.print("USE THREAD LOADING : %s", threadLoading);

		// SEはファイルが小さいから4MBぐらいあれば十分でないだろうか
		seloader.setCacheSize(1024*1024*cache_se);
		res = seloader.loadDefRW( FileSys.read(cast(char[]) "snd/se/list.lst") );
		Log.print("se_load: " ~ getErrorName(res) );

		// BGMが32MBであれば２、３曲入るだろうと目論む
		bgmloader.setCacheSize(1024*1024*cache_bgm);
		printf("%*s", FileSys.read(cast(char[]) "snd\\bgm\\list.lst"));
		res = bgmloader.loadDefRW(FileSys.read(cast(char[]) "snd\\bgm\\list.lst"));
		Log.print("BGM_load: " ~ getErrorName(res) );

		// ボイスは使いまわすことがないので、キャッシングしないのだ
		voiceloader.setCacheSize(1024*1024*cache_voice);

		// システムSE
		sysSELoader.setCacheSize(1024*1024*cache_sys_se);
		sysSELoader.setGuardTime(30);
		res = sysSELoader.loadDefRW( FileSys.read(cast(char[]) "snd/system/sys_se.lst") );
		Log.print("SYSEM_SE LOAD: " ~ getErrorName(res) );
		
		// キャッシュ調整
		tl_shil.setCacheSize(cache_shil*1024*1024);
		tl_2ch.setCacheSize(cache_2ch*1024*1024);
		tl_bg.setCacheSize(cache_bg*1024*1024);
		fontloader.setCacheSize(cache_font*1024*1024);
		
		res = tl_shil.loadDefRW(FileSys.read(cast(char[]) "img/sil/list.lst"));
		Log.print("shi_load: " ~ getErrorName(res));
		res = tl_2ch.loadDefRW(FileSys.read(cast(char[]) "img/back2ch/list.lst"));
		Log.print("2chBG load: " ~ getErrorName(res));
		res = tl_bg.loadDefRW(FileSys.read(cast(char[]) "img/back/list.lst"));
		Log.print("BG_load: " ~ getErrorName(res));
		
		// font ユーザフォント指定があればそれを使用する
		auto userFontName = getUserFontName();
		if ( userFontName is null ) {
			res = fontloader.loadDefRW( FileSys.read(cast(char[]) "data/font.lst") );
			Log.print("nomal font_load: " ~ getErrorName(res));
		} else {
			Log.printLook("USER DEFINE FONT : %s", userFontName);
			auto fileText = cast(char[]) FileSys.read(cast(char[]) "data/font.lst");
			fileText = cast(char[]) std.string.replace(fileText, USER_FONT_REPLACE_KEY, userFontName);
			res = fontloader.loadDefRW( fileText );
			KyojinConst.C_FONT_NO_MAIN_TEXT = 6;

			Log.print("font_load added USER DEF: " ~ getErrorName(res));
		}

		
		// エラー用ダイアログ
//		this.errorDialg = DialogBox.createDialogBox(
//						"img/dialog/dialog_exception.txt",
//						this.keyDecolateMouse,
//						this.fontloader, KyojinConst.C_FONT_NO_DIALOG_TEXT,
//						this.fontloader.get(KyojinConst.C_FONT_NO_DIALOG_TEXT) );
		Log.print("loadResources FINISH.");
		
	}
	
	/// ゲーム終了確認ダイアログ表示準備を行う
	void setupFinishDialog() {
		// 他でダイアログを表示しているのであれば何もしない
		if (isShowYesNo()) {
			return;
		}
		showDialog(TaskYesNo.TYPE.FINISH);
		/* 
			ここで終了することになったとき、ScenarioTaskが実行されていた場合、
			オートセーブされないないことになってしまうので、
			ダサいながらも、ここでなんとかセーブ情報を抜き出す 
		*/
		GameTaskBase nowTask = gameSceneController.getNowTask();
		/* 
			現在のタスクがシナリオタスクならばセーブを実行する
			どこかにスタックされているとしても、それはシナリオタスクが
			タスク移行時にデータをストアすべきである
		 */
		if ( KyojinConst.TASK_NAME[KyojinConst.Task.Task_Scenario] == nowTask.getTaskName() ) {
			Log.printLook("Quite Event. ScenarioTask save data.");
			// セーブ情報蓄積！
			(cast(TaskScenario) nowTask).doAutoSave(this);
		}
	}
	
	int[] getKeyData() {
		int[] result;
		for (int i = 0; i < 5; ++i) {
			result ~= this.keyConfig.getKeyData(i)[0];
		}
		return result;
	}
	
	/// 設定したKEY IDで アップデートする
	void updateJoystickMapping(int[] buttonIDs) {
		try {
			auto aMouse = cast(MousedKey) (cast(NullableKeyInput) this.key).getOriginal();
			assert( !(aMouse is null) );
			assert(buttonIDs.length >= 5);
			aMouse.addJoyKeyLeft(buttonIDs[0]);
			aMouse.addJoyKeyRight(buttonIDs[1]);
			aMouse.addJoyKeyWheelUp(buttonIDs[2]);
			aMouse.addJoyKeyWheelDown(buttonIDs[3]);
			aMouse.addJoyKeyExit(buttonIDs[4]);
			Log.print("JOYKEY MAPPING UPDATED");
		} catch (Object o) {
			Log.printFatal("Exception %s#updateJoystickMapping : [%s]", 
				super.toString(), o.toString());
			throw o;
		}
	}
	
/+
	/// アプリケーション情報のセーブを行う
	synchronized void saveAppData() {
		// シリアライズが生成される前に終了されることもあるのか・・・
		if (serialize is null) return;

		// 栞がないということは、初期化処理に終了されたということなので、セーブすることないだろう
		if ( (bookmark is null) ) return;

		// 栞情報書き出し
		serialize.setStoring(true);

		// オプション情報の書き出し
		optionInfo.serialize(serialize);
		// ユーザ情報の書き出し
		m_userInfo.serialize(serialize);
		// KeyConf情報の書き出し
		m_keyConfig.serialize(serialize);
		
		foreach(inout Bookmark bm; bookmark) {
			if ( !(bm is null) ) {
				bm.serialize(serialize);
				Log.printLook("SEVE BOOKMARK DATA:%s", bm.toString());
			}
		}

		if ( FileSys.isExist(KyojinConst.saveFileName) ) {
			.remove(KyojinConst.saveFileName);	// FileSysは上書きできん...
		}

		// セーブデータ保存
		serialize.save(KyojinConst.saveFileName);
		createSaveIdentFile();
		
		Log.print("USER DATA SAVED.");
	}
+/
	
	/// ユーザ指定のフォントが存在するか
	char[] getUserFontName() {
		Properties prop = Properties.getInstance(cast(char[]) USER_FONT_DEF_NAME);
//		prop.load(USER_FONT_DEF_NAME);
		auto fontName = prop.getProperty(cast(char[]) PROP_KEY_USER_FONT);
		if (fontName is null) {
			return null;
		}
		
		if (FileSys.isRealExist("fonts/" ~ fontName)) {
			return "fonts/" ~ fontName;
		}
		Log.printWarn("USER FONT : %s is not found!!", fontName);
		return null;
		
	}
	
	/// オブジェクト破棄処理
	void destroy() 
	/**
		明示的にオブジェクトを破棄してやることで
		ＧＣを助けてやる
	*/
	{
		Log.print( "GameInfo destroy..." );
		
		serialize = null;		//!< シリアライズクラス
		bookmark = null;		//!< ブックマークオブジェクト配列
		optionInfo = null;	//!< ゲームオプション情報
		// コントローラ
		gameTaskController = null;
		gameSceneController = null;
		gameTaskFactory = null;
		gameSceneTransiter = null;
		m_screen = null;
		seloader = null;		//!< SELoader
		bgmloader = null;		//!< BGMLoader
		voiceloader = null;	//!< VoiceLoader
		tl_shil = null;	//!< シルエット用テクスチャローダ
		tl_bg = null;	//!< 背景用テクスチャローダ
		tl_2ch = null;	//!< 2chモード用テクスチャローダ
		fontloader = null;	//!< フォントローダ
	}


private:
	static const int SE_BT_ACTIVE = 0;
	static const int SE_BT_NEGATIVE = 1;
	
	static const char[] PROP_KEY_CACHE_SE 		= "cache_se";
	static const char[] PROP_KEY_CACHE_BGM 		= "cache_bgm";
	static const char[] PROP_KEY_CACHE_VOICE 	= "cache_voice";
	static const char[] PROP_KEY_CACHE_SYSTEM_SE = "cache_system_se";
	static const char[] PROP_KEY_CACHE_SHILHOUETTE = "cache_silhouette";
	static const char[] PROP_KEY_CACHE_BG 		= "cache_bg";
	static const char[] PROP_KEY_CACHE_2CH 		= "cache_2ch";
	static const char[] PROP_KEY_CACHE_TITLE_BT = "cache_title_bt";
	static const char[] PROP_KEY_CACHE_SHIORI 	= "cache_shiori";
	static const char[] PROP_KEY_CACHE_FONT 	= "cache_font";
	static const char[] PROP_KEY_THREAD_LOADING	= "music_load_thread";
	static const char[] PROP_KEY_TEXTURE_COMP = "use_texture_comp";
	
	
	static const char[] SPRITE_DEF_FILE_NAME = "img/dialog/dialog.sdf";
	static const char[] TITLE_DEF_FILE_NAME = "img/dialog/title.sdf";
	
	/// ユーザフォント指定ファイル名
	static const char[] USER_FONT_DEF_NAME = "fonts/main_font.txt";
	static const char[] PROP_KEY_USER_FONT = "main_font";
	static const char[] USER_FONT_REPLACE_KEY = "{USER_FONT}";
	
	/// キャッシュの効果をログ出力する
	static void printChaheEffect(CacheLoader cl) {
		int hit = cl.getCacheHitCount();
		int miss = cl.getCacheMisshitCount();
		float use = hit + miss;
		if (use == 0) {
			Log.print("NEVER USE");
		} else {
			Log.print("USE : %s HIT : %s(%s), MISSHIT : %s(%s)", 
				use, hit, (hit/use)*100, miss, (miss/use)*100);
		}
	}

	/// セーブファイル整合性ファイルの生成
	static void createSaveIdentFile() {
		FileChecker.createMD5File( 
			cast(char[]) KyojinConst.saveFileName,		// セーブファイル 
			cast(char[]) KyojinConst.C_SAVE_IDENT_FILE, 	// 整合ファイル
			Encryption.TYPE.XOR );
	}
	
	static TileTexture dialogTile;	//!< ダイアログの基本タイル
	static TileTexture cursorTile;
	static Serialize serialize;		//!< シリアライズクラス

	Screen m_screen;

	MouseInput m_mouse;	//!< マウスオブジェクト
	KeyInputBase m_key;		//!< キーボードオブジェクト
	MouseInput m_keymouse;	//!< マウスオブジェクト
	
	DialogBox m_dlgErr;	// エラーダイアログボックス
	
	bool[] m_endingOptionFlg;	// エンディングオプション設定フラグ
	bool m_useTestMusic = false;
	
	TaskYesNo	 	yesNoTask;	//!< 終了しますか？タスク（最優先タスク）
	bool showYesNo = false;		//!< 終了しますかダイアログボックスの表示フラグ
	TaskYesNo.CLICK m_yesnoClick;
	
	bool m_saveFileExist = false;	//!< 初回起動フラグ
	// アイキャッチタスクで使用
	int m_selectedStory = KyojinConst.ScenarioID.NOT_DATA;
	// bookmarkで使用
	bool m_restart = false;		//!< ２回目以降フラグ
	int m_selectedBookmark = KyojinConst.ScenarioID.NOT_DATA;	//!< 選択したブックマーク番号
	// ScenarioTaskで使用
	bool m_fromTitle; //!< タイトルからの遷移フラグ
	int[]			m_data;	//!< タスク間でデータのやりとりを行う場合に使用するコンテナ
	FpsTimer 		fpstimer;	//!< FPSタイマ
	FpsLayer 		fpslayer;	//!< FPSレイヤー
	UserInfo 		m_userInfo;		//!< ユーザデータクラス
	KeyConfigData	m_keyConfig;	//!< KeyConfig情報
}
