module kyojintati4d.taskscenario;

private import std.gc;

private import y4d;
private import y4d_aux.filesys;
private import y4d_thread.gamescenetransiter;
private import y4d_draw.sprite;
private import y4d_timer.fixtimer;

private import kyojintati4d.myapp;
private import kyojintati4d.gameinfo;
private import kyojintati4d.bookmark;
private import kyojintati4d.taskeyecatche;

private import yamalib.auxil.properties;
private import yamalib.counterfsp;
private import yamalib.log.log;
private import yamalib.gui.guibutton;
private import yamalib.gui.keygroup;

private import yamalib.draw.textdraw;;
private import yamalib.draw.menu;
private import yamalib.draw.scenariodraw;
private import yamablib.dict.dictcontroller;

/**
	シナリオタスク
	Bookmark.getCurrentInfo() にシナリオ情報が設定されていれば、
	その情報を使ってシナリオを生成します。
	さもなければ、GameInfo#selectedStoryを参照し、対象のシナリオを
	始めから生成します。
*/
/// ScenarioDrawテスト
class TaskScenario : GameTaskBase {

	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return KyojinConst.TASK_NAME[KyojinConst.Task.Task_Scenario];
		}
	}
	
	/// 毎回呼び出す
	override int onMove(Object o) {
		try {
			this.info = cast(GameInfo) o;
			
			if (!init) {
				onInit();
				init = true;
			}

			// オートセーブチェック
			m_autoSaveTimer.update();
			if ( m_autoSaveTimer.get() > AUTO_SAVE_SPAN ) {
				doAutoSave(info);
				info.saveAppData();
				m_autoSaveTimer.reset();
				Log.print("Execute auto save...");
			}
			
			// 一時的な入力キャンセルフラグ
			bool bInputCancel = false;
			bool bShowYesNo = info.isShowYesNo;
	
			bool selectWaiting = sd.isSelectWait();
	
			m_mouse.update();
			
			if (sd.isFastSkipToReadPos) {
				if (m_mouse.isPress(MouseInput.button.left) 
					|| m_mouse.isPress(MouseInput.button.middle)
					|| m_mouse.isPress(MouseInput.button.right)
					|| m_mouse.isPress(MouseInput.button.wheel_up)
					|| m_mouse.isPress(MouseInput.button.wheel_down) ) {
					sd.setFastSkipToReadPos(false);
				}
			}

			// 選択肢表示中は何もしない
			if (!sd.isSelectWait()) {
				// キー操作によるフォーカシング
				if ( info.key.isPush(3) ) {
					m_keyGroup.prev();
				} else if ( info.key.isPush(4) ) {
					m_keyGroup.next();
				}
				
				// 0番目は、NullButton
				int focusNo = m_keyGroup.getFocusNo();
				if (0 != focusNo) {
					m_menu.show();
					if ( !m_menu.isShow() ) {
						m_keyGroup.focus(focusNo);
					}
				}
			} else {
				info.hideDialog();
			}
			
			// しおり画面からもどってきた！
			if (jumpShiori) {
				
				CurrentInfo ci = Bookmark.getCurrentInfo();
				if ( !(ci is null) ) {
					// 選択されたしおりからScenarioDrawを再構築
					initSecectedInfo(ci);
					
					// ここでデータを消しておくといいな
					Bookmark.removeCurrentInfo();
					
					Log.print("ScenarioTask: bookmark selected!");
				}
				
				jumpShiori = false;
				bInputCancel = true;
			}
			
			if ( callTelop ) {
				this.callTelop = false;
				this.callWait = false;
				this.sd.setNext(true);
				Log.print("TELOP FLAG OFF");
			}

			// オプション画面からもどってきた！
			if (jumpOption) {
				sd.setOptionInf(info.optionInfo);
				jumpOption = false;
				bInputCancel = true;
			}
			
			// 終了ダイアログ時、入力キャンセル
			if ( !bShowYesNo ) {
				bInputCancel = info.isShowYesNo;
			}
			
			// 事典コントローラ
			if (showDict) {
				m_dictCtl.onMove(info.screen);
				if ( m_dictCtl.isClose() ) {
					showDict = false;
				}
			} else {
				bool drawObject = !(m_key.isPress(11) || m_key.isPress(12)); // 左右Ctrl 
				sd.setDrawText(drawObject);
				sd.setBackFillter(drawObject);
				sd.onMove(info.screen);
				
				if ( !m_menu.isInBar() && !bInputCancel) {
					if ( m_mouse.isLButtonUp() ) {
						sd.setNext(true);
					} else if ( m_mouse.isRButtonUp() ) {
						sd.setCancelNowText(true);
					}
				}
			}
			
			// ------------------------------------------------------------------------
			// リクエスト番号を取得
			int taskReq = sd.popTaskReq();
			if ( -1 != taskReq ) {
				Log.printLook("TASK REQ %s", taskReq);

				// 特殊分岐
				// メインパートをすべて読んでいて
				// シナリオ５、７であればエンディングに遷移				
				if ( checkMainEnding(taskReq) ) {
					taskReq = KyojinConst.Task.Task_Ending3;
					setReadFlag(info);
				}
				
				if ( KyojinConst.Task.Task_Scenario == taskReq ) {
					sd.nextScenario( sd.getCurrentScenarioInfo().storyNo+1 );
				} else if ( KyojinConst.Task.Task_Telop == taskReq ) {
					
					this.storyId = sd.getScenarioInfo().storyNo;

					// ヘッドラインタスクリクエスト！
					callHeadline( info, this.storyId );
					callTelop = true;
					return 0;
				} else if ( KyojinConst.Task.Task_EyeCatche == taskReq ) {

					int[] param = sd.getParamData();
					/*
					 * ここで引き渡すパラメータに３つ以上渡せば、“去人たち”アイキャッチ
					 * なにも渡さなければ、デフォルトアイキャッチ
					 */
					 Log.printLook("EyeCatch Request : now %s", this.nowStoryNo);
					 switch (this.nowStoryNo) {
					 case KyojinConst.ScenarioID.STORY04:
					 	{
							int[] v = new int[3];
							v[0] = KyojinConst.Task.Task_Title;	// 呼び出してほしいタスクね
							v[1] = -1;					// 呼び出してほしいシナリオ番号ね
							v[2] = TaskEyeCatche.SHI_TYPE.ALICE01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
							info.setData(v);
					 	}
					 	break;
					 case KyojinConst.ScenarioID.STORY05:
					 	{
							int[] v = new int[3];
							v[0] = KyojinConst.Task.Task_Title;	// 呼び出してほしいタスクね
							v[1] = -1;					// 呼び出してほしいシナリオ番号ね
							v[2] = TaskEyeCatche.SHI_TYPE.AYA01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
							info.setData(v);
					 	}
					 	break;
					 case KyojinConst.ScenarioID.STORY06:
					 	{
							int[] v = new int[3];
							v[0] = KyojinConst.Task.Task_Title;	// 呼び出してほしいタスクね
							v[1] = -1;					// 呼び出してほしいシナリオ番号ね
							v[2] = TaskEyeCatche.SHI_TYPE.MIDO01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
							info.setData(v);
					 	}
					 case KyojinConst.ScenarioID.STORY07:
					 	{
							int[] v = new int[3];
							v[0] = KyojinConst.Task.Task_Title;	// 呼び出してほしいタスクね
							v[1] = -1;					// 呼び出してほしいシナリオ番号ね
							v[2] = TaskEyeCatche.SHI_TYPE.AYA01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
							info.setData(v);
					 	}
					 	break;
					 case KyojinConst.ScenarioID.KYOJIN02:
					 	{
							int[] v = new int[3];
							v[0] = KyojinConst.Task.Task_Title;	// 呼び出してほしいタスクね
							v[1] = -1;					// 呼び出してほしいシナリオ番号ね
							v[2] = TaskEyeCatche.SHI_TYPE.MIDO01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
							info.setData(v);
					 	}
					 	break;
					 default:
					 	{
							int[] v = new int[2];
							v[0] = KyojinConst.Task.Task_Scenario;	// 呼び出してほしいタスクね
							v[1] = param[0];						// 呼び出してほしいシナリオ番号ね
							info.setData(v);
					 	}
						break;
					 }
					
					// 既読フラグをたてる
					setReadFlag(info);
					// すべてのスタックを破棄		
					info.gameSceneTransiter.exitScene();

					// 強制バックアップを行う
					info.gameSceneTransiter.setTransitType( GameSceneTransiter.C_NO_EFFECT, 1, true );
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_EyeCatche);
					
					return -1;
	
				} else if ( KyojinConst.Task.Task_Ending1 == taskReq ) {
					// 既読フラグをたてる
					setReadFlag(info);

					info.gameSceneTransiter.exitScene();
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_Ending1);
					
					return -1;
				} else if ( KyojinConst.Task.Task_Ending3 == taskReq ) {
					// メインパートエンディング

					// すべてのスタックを破棄		
					info.gameSceneTransiter.exitScene();
					// 強制バックアップを行う
					info.gameSceneTransiter.setTransitType( GameSceneTransiter.C_NO_EFFECT, 1, true );
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_Ending3);
					return -1;

				} else {
					// 不明リクエスト
					Log.printWarn("Unkown Request %s", taskReq);
				}
				
				Log.print("next scenario load");
			}
			// ------------------------------------------------------------------------
			
			if (sd.isSelectWait()) {
				// 選択肢中なら、マウスをNULLオブジェクトに
				disableAllInputDevice(info);
			}
			
			// メニューランチャーの動作
			// 終了ダイアログちゅうでない
			// 事典を表示していない
			// 選択肢中でない
			if (!bShowYesNo && !showDict) {
				m_menu.onMove(info.screen);
			}
			int bx,by;
			
			// メニューボタン判定
			foreach ( int i,MenuItem item; m_menu.getItems() ) {
				
				// 移動中は無視
				if ( (cast(PartialMenuItem) item).isMoving() ) {
					break;
				}
				
				if ( item.getButton().isLClick() ) {
					// 遷移する前にオートセーブ
					doAutoSave(info);
					switch (i) {
					case 2:		// SAVE						
						CurrentInfo ci = new CurrentInfo();
						ci.si = sd.getCurrentScenarioInfo();
						ci.text = sd.getCurrentText();
						
						Bookmark.setCurrentInfo(ci);
						
						jumpShiori = true;
						
						info.gameSceneTransiter.callScene(KyojinConst.Task.Task_Bookmark);
						// SE再生
						info.playSystemSE(SYS_SE.CLICK_TITLE_ACTIVE);
						break;
						
					case 1:		// OPTION
						info.gameSceneTransiter.callScene(KyojinConst.Task.Task_Option);
						jumpOption = true;
						// SE再生
						info.playSystemSE(SYS_SE.CLICK_TITLE_ACTIVE);
						break;
						
					case 0:	// スキップ
						onClidkTextFastSkip();
						break;

/*					
					case 0:	// 事典
						// TODO 評価版制限
//						showDict = true;
//						m_dictCtl.setClose(false);
//						m_menu.hideNow();
						// SE再生
//						info.playSystemSE(SYS_SE.CLICK_TITLE_NEGATIVE);
						break;
*/					
					default:
						break;				
					}

					break;
				}	// if
			}	// foreach
			
	
			return 0;
		} catch (Exception e) {
			Log.printFatal("RuntimeException %s#onMove : [%s] [%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		} finally {
			enableAllInputDevice(cast(GameInfo) o);
		}
	}
	
	/// 毎回呼び出すなり
	override int onDraw(Object o) {
		try {
			// 初期化されてなければなにもしない		
			if (!init || callTelop) {
				return 0;
			}
			
			GameInfo info = cast(GameInfo) o;
			
			info.screen.clear();
			info.screen.blendSrcAlpha();
			
			sd.onDraw(info.screen);
			
			if (showDict) {
				info.screen.blendSrcAlpha();
				m_dictCtl.onDraw(info.screen);
			}
			
			if (!showDict) {
				info.screen.blendSrcAlpha();
				m_menu.onDraw(info.screen);
			}
			
			if (sd.isFastSkipToReadPos) {
				info.screen.blendSrcAlpha();
				info.screen.blt(skipLayer, 0, 0);
			}
	
			return 0;
		} catch (Exception e) {
			Log.printFatal("RuntimeException %s#onDraw : [%s] [%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}
	
	/// オートセーブを実行する
	void doAutoSave(GameInfo info) {
		// オートセーブデータの更新
		GameInfo.bookmark[KyojinConst.BookmarkID.AUTO].setScenarioInfo(sd.getCurrentScenarioInfo());
		GameInfo.bookmark[KyojinConst.BookmarkID.AUTO].setText(sd.getCurrentText());
		doAutoReadPosSave(info);
	}
	
	void doAutoReadPosSave(GameInfo info) {
		auto readPos = info.userInfo.getReadPos( sd.getCurrentScenarioInfo().storyNo );
		if (context.getHeadTextOffset() > readPos) {
//			Log.printLook("SAVE READ POS NO:[%s] POS:[%s]", sd.getCurrentScenarioInfo().storyNo, );
			info.userInfo.setReadPos( sd.getCurrentScenarioInfo().storyNo, context.getHeadTextOffset());
		}
	}
	
	
	/// レガシーなやつ
	override int task(Object o) {
		return 0;
	}

	/// 占有しているメモリを解放する
	override void destroy() {
		// キャッシュクリア
		info.releaseAllCache();
		this.m_menu = null;				//!< メニューコントローラ
		this.m_dictCtl = null;	//!< 事典コントローラ
		this.context = null;	//!< 描画コンテキスト
		this.si = null;			//!< シナリオモデル
		this.sd = null;			//!< ScenarioDrawコンポーネント
		this.m_mouse = null;			//!< マウスデバイス
		this.m_keyGroup = null;
		
		Log.print("%s#destroy : destroyed.", super.toString());
	}
	
	/// コンストラクタ
	this() {
		m_keyGroup = new KeyGroup();
	}
	
	/// デストラクタ
	~this() {
	}
	
private:
	enum BUTTON_ID { DICT };
	
	static final const char[] PROP_KYE_USE_RUBI_SETTING = "scenario.rubi_enable";

	/// 去人パートのキャラクターイメージファイルリスト名	
	static final const char[] KYOJIN_CHAR_IMG_LIST = "img2/sil/list.lst";
	/// 去人パートのキャラクター定義ファイルマップリスト名	
	static final const char[] KYOJIN_CHAR_SDF_LIST = "img2/sil/chara_map.lst";
	/// 去人パートの背景イメージファイルリスト名	
	static final const char[] KYOJIN_BG_IMG_LIST = "img2/back/list.lst";
	
	/// メインパートのキャラクターイメージファイルリスト名	
	static final const char[] MAIN_CHAR_IMG_LIST = "img/sil/list.lst";
	/// 去人パートのキャラクター定義ファイルマップリスト名	
	static final const char[] MAIN_CHAR_SDF_LIST = "img/sil/chara_map.lst";
	/// メインパートの背景イメージファイルリスト名	
	static final const char[] MAIN_BG_IMG_LIST = "img/back/list.lst";

	/// オートセーブする間隔
	static final const uint AUTO_SAVE_SPAN = 3 * 60 * 1000;	// ３分
	
	/// 去人パートではキャラクター位置を左右に大きくふるので、実画面より大きく取る
	static final const uint KYOJIN_VIRTUAL_VIEW_WIDTH = 860;
	
	/// シナリオ２のページ数
	static final const int SCENARIO_2_PAGE_NUM = 547;

	/// シナリオIDよりメインパートか去人パートを判断します	
	static bool isMainPartScenarioId(int scenarioId) {
		switch (scenarioId) {
		case KyojinConst.ScenarioID.STORY01:
		case KyojinConst.ScenarioID.STORY02_0:
		case KyojinConst.ScenarioID.STORY02_1:
		case KyojinConst.ScenarioID.STORY02_2:
		case KyojinConst.ScenarioID.STORY03:
		case KyojinConst.ScenarioID.STORY04:
		case KyojinConst.ScenarioID.STORY05:
		case KyojinConst.ScenarioID.STORY06:
		case KyojinConst.ScenarioID.STORY07:
		case KyojinConst.ScenarioID.STORY08_0:
		case KyojinConst.ScenarioID.STORY08_1:
		case KyojinConst.ScenarioID.STORY08_2:
		case KyojinConst.ScenarioID.STORY08_3:
		case KyojinConst.ScenarioID.STORY09:
			return true;
		default:
			return false;
		}
	}
	
	void onClidkTextFastSkip() {
		if (sd.isFastSkipToReadPos) {
			sd.setFastSkipToReadPos(false);
		} else {
			if (isFastSkipable() ){
				sd.setFastSkipToReadPos(true);
			} else {
				info.playSystemSE(SYS_SE.CLICK_TITLE_NEGATIVE);
			}
		}
	}
	
	/// 現在のシナリオはスキップ可能か？
	bool isFastSkipable() {
		auto readPos = info.userInfo.getReadPos( sd.getCurrentScenarioInfo().storyNo );
		return context.getHeadTextOffset() < readPos;
	}
	
	
	/// フラグの状態をみて、メインエンディングかどうかを判定する
	bool checkMainEnding(int taskReq) {
		if (KyojinConst.Task.Task_EyeCatche == taskReq) {
			if ( KyojinConst.ScenarioID.STORY05 == this.nowStoryNo || 
				KyojinConst.ScenarioID.STORY07 == this.nowStoryNo ) {

				for (int i = 0; i <  KyojinConst.ScenarioID.STORY07; ++i) {
					if (i == this.nowStoryNo) {
						continue;
					}
					if ( !info.userInfo.isRead(i) ) {
						return false;
					}
				}
				return true;
			} 
		}
		return false;
	}

	/// 全てのデバイスの入力を無効に設定する
	void disableAllInputDevice(GameInfo info) {
		NullableMouseInput aMouse = cast(NullableMouseInput) info.mouse;
		NullableKeyInput aKey = cast(NullableKeyInput) info.key;
		NullableMouseInput aKeyMouse = cast(NullableMouseInput) info.keyDecolateMouse;
		JoyStick aJoystick = info.joystick;
		aMouse.disable();
		aKey.disable();
		aKeyMouse.disable();
		aJoystick.disable();
	}
	
	/// 全てのデバイスの入力を有効に設定する
	void enableAllInputDevice(GameInfo info) {
		NullableMouseInput aMouse = cast(NullableMouseInput) info.mouse;
		NullableKeyInput aKey = cast(NullableKeyInput) info.key;
		NullableMouseInput aKeyMouse = cast(NullableMouseInput) info.keyDecolateMouse;
		JoyStick aJoystick = info.joystick;
		aMouse.enable();
		aKey.enable();
		aKeyMouse.enable();
		aJoystick.enable();
	}

	/// 初期化処理
	void onInit() {
		// メモリー状況をプリント
		GameInfo.printMemoryState();

		Log.print("TaskScenario onInit start!");
		
		// 念のため全サウンドをここでストップ
		Sound.stopAll();
		
		// マウス情報
//		mouse = cast(MouseInputEx) info.mouse();
		m_mouse = info.keyDecolateMouse;
		m_key = info.key;
		CurrentInfo ci = Bookmark.getCurrentInfo();
		
		// スキップレイヤー
		skipLayer = new Texture();
		skipLayer.load("img/common/skip_layer.png");

		// メニューバー生成
		initMenu();
		// 事典コントローラ
		initDict(info.fontloader);
		m_dictCtl.onMove(info.screen);
		
		context = new TextDrawContext();
		si = new ScenarioInfo();

		bool loadFlg = false;		
		// TODO ストーリーＩＤ
		if (ci !is null) {
			this.nowStoryNo = ci.si.storyNo;
			loadFlg = true;
		} else {
			this.nowStoryNo = info.selectedStory;
			loadFlg = false;
		}
		
		if ( this.nowStoryNo < 0 || KyojinConst.scenarioFileName.length <= this.nowStoryNo ) {
			Log.printWarn("Request storyNo is invalid. : %s", this.nowStoryNo);
			this.nowStoryNo = 0;
		}
		Log.print("Request storyNo : %s", this.nowStoryNo);
		
		char[] strText = cast(char[]) FileSys.read( KyojinConst.scenarioFileName[this.nowStoryNo] );
		Log.printLook("TaskScenario#onInit scenario_script : %s size : ",
			 KyojinConst.scenarioFileName[this.nowStoryNo], strText.length);
		context.setText( toWCS(strText) );
		
		assert( strText.length != 0 );
		
		// Voice 読み込み
		if (KyojinConst.voicePath.length > this.nowStoryNo) {
			Log.print("load voice list:%s", KyojinConst.voicePath[this.nowStoryNo] ~ "voice.lst");
			try {
				info.voiceloader.stopBGM();
			} catch(Object o) {
				// どうしようもない
			}
			info.voiceloader.releaseAll();
			info.voiceloader.releaseFileList();
			info.voiceloader.loadDefRW( KyojinConst.voicePath[this.nowStoryNo] ~ "voice.lst");
		} 
		
		// ストーリー番号指定（マップ作成に使用するのでないとこまる
		si.storyNo = this.nowStoryNo;
		
		// シナリオ2の分岐ならば、ページ数初期値を設定
		if (KyojinConst.ScenarioID.STORY02_1 == nowStoryNo 
			|| KyojinConst.ScenarioID.STORY02_2 == nowStoryNo) {
			if (!loadFlg) {
				si.page = SCENARIO_2_PAGE_NUM;
			}
		}

		// 画像リソース、音楽リソースをわたして、対象ScenarioDrawを生成する
		if (isMainPartScenarioId(this.nowStoryNo)) {
			initMainPartScenarioDraw();
		} else {
			initKyojinPartScenarioDraw();
		}
		
		// ルビの使用／不要設定
		Properties prop = Properties.getInstance("setting.txt");
		if ("false" == prop.getProperty(PROP_KYE_USE_RUBI_SETTING,"true")) {
			this.sd.disenableRubiText();
		}

		// カーソル画像の読込
		Texture t = new Texture();
		t.load( FileSys.makeFullName("img/scenario/carsor.png") );
		sd.setPromptCarsor(t);

		// ユーザ既読位置の設定
		sd.setUserReadPos(info.userInfo.getReadPos(this.nowStoryNo));
		
		Log.printLook("USER READ POSITION: %s", info.userInfo.getReadPos(this.nowStoryNo));
		
		// フィルター画像の読込
		auto bgFillter = new Texture();
		bgFillter.load(FileSys.makeFullName("img/scenario/black_filter.png"));
		sd.setBackFillter(bgFillter);
		
		// 改ページスプライト画像の初期化
		initNewPageSprite();
		
		// オプション画面で、タイトルを戻るボタンを表示するには、これを設定
		info.fromTitle = false;

		// ロードであれば、ロード前の情報は初期化しておく		
		if ( ci !is null ) {
			sd.onMove( info.screen );
			Bookmark.removeCurrentInfo();
			initSecectedInfo(ci);
		}

		// オートセーブ用のタイマー
		m_autoSaveTimer = new FixTimer();
		m_autoSaveTimer.reset();

		Log.print("ScenarioDrawEx setting ok");
	}
	
	/// メインパート用のシナリオを生成
	void initMainPartScenarioDraw() {
		// リソースを読み込む
		loadScenarioResource(MAIN_CHAR_IMG_LIST, MAIN_BG_IMG_LIST);
		sd = new ScenarioDrawEx(MAIN_CHAR_IMG_LIST, MAIN_CHAR_SDF_LIST);
		context.viewWidth = info.screen.getWidth();
		initScenarioDraw();
	}
	
	/// 去人パート用のシナリオを生成
	void initKyojinPartScenarioDraw() {
		loadScenarioResource(KYOJIN_CHAR_IMG_LIST, KYOJIN_BG_IMG_LIST);
		sd = new ScenarioDrawEx(KYOJIN_CHAR_IMG_LIST, KYOJIN_CHAR_SDF_LIST);
		// 仮想画面サイズに設定
		context.viewWidth = KYOJIN_VIRTUAL_VIEW_WIDTH;
		initScenarioDraw();
	}
	
	/// 設定した状態でScenarioDrawを設定する
	void initScenarioDraw() {
		sd.setMouse( m_mouse );
		sd.setKey( info.key );
		sd.setScenarioInfo(si);
		sd.setTextDrawContext(context);
		sd.setBGLoader(info.tl_bg);
		sd.setSilhouetteLoader(info.tl_shil);
		sd.setBGMLoader(info.bgmloader);
		sd.setSELoader(info.seloader);
		sd.setVoiceLoader(info.voiceloader);
		sd.setFontLoader(info.fontloader,KyojinConst.C_FONT_MAIN, KyojinConst.C_FONT_NO_RUBI);
		sd.setOffset(45,28);
		sd.setDialogResources( info.createDialogResources() );
		// オプション情報の反映は、各Loaderが設定されたあと
		sd.setOptionInf(info.optionInfo);
	}
	
	/// シナリオで使用するリソースをロードする
	void loadScenarioResource(char[] charaList, char[] bgList) {
		info.tl_shil.releaseAll();
		y4d_result res = info.tl_shil.loadDefRW(FileSys.read(charaList));
		Log.print("shi_load: " ~ getErrorName(res));
		info.tl_bg.releaseAll();
		res = info.tl_bg.loadDefRW(FileSys.read(bgList));
		Log.print("BG_load: " ~ getErrorName(res));
	}
	
	/// 改ページ表示用のスプライトをロードする
	void initNewPageSprite() {
		SpriteLoader spriteLoader = new SpriteLoader();
		SpriteEx spriteEx = new SpriteEx();
		spriteLoader.load( FileSys.makeFullName("img/scenario/newpage.sdf") );
		spriteEx.setSprite( spriteLoader.getSprite() );
		sd.setNewPageSprite( spriteEx );
	}
	
	
	/// メニューバーを生成する
	void initMenu() {
		static const int MUNU_POS_X = 440;
		static const int MUNU_POS_Y = 454;
		static const int MUNU_SEVE_BT_OX = 4;
		static const int MUNU_OPT_BT_OX = 74;
		static const int MUNU_SIMP_OX = 125;
		static const int MUNU_IND_OX = 168;
		
		try {
			// メニューバーテスト
			m_menu = new PartialMenuBar(m_mouse);
			m_menu.setHideStep(30);
			
			// メニューボタンの生成
			TextureLoader tl = new TextureLoader();
			tl.loadDefRW( FileSys.read("img/menu/menu_bt.txt") );
			
			GUIButton btSaveLoad = new GUIButton();
			GUIButton btOp = new GUIButton();
			GUIButton btSd = new GUIButton();
			// ランチャーインディケータ
			GUIButton btInd = new GUIButton();
			
			GUINormalButtonListener v  = new GUINormalButtonListener;

			// インジケータボタンの設定
			v.setTextureLader(tl,8);
			v.setType(9);
			btInd.setMouse(m_mouse);
			btInd.setEvent(v);
			btInd.setXY(MUNU_POS_X + MUNU_IND_OX, MUNU_POS_Y);
			
			m_menu.setIndicator(btInd, 640-MUNU_POS_X, 0);

			// 各メニューボタンの生成
			v  = new GUINormalButtonListener;
			v.setTextureLader(tl,0);
			v.setType(9);
			btSaveLoad.setMouse(m_mouse);
			btSaveLoad.setEvent(v);
			btSaveLoad.setXY(MUNU_POS_X + MUNU_SEVE_BT_OX, MUNU_POS_Y);
			
			v  = new GUINormalButtonListener;
			v.setTextureLader(tl,3);
			v.setType(9);
			btOp.setMouse(m_mouse);
			btOp.setEvent(v);
			btOp.setXY(MUNU_POS_X + MUNU_OPT_BT_OX, MUNU_POS_Y);
	
			v  = new GUINormalButtonListener;
			// ボタンテクスチャの番号
			v.setTextureLader(tl,6);
			v.setType(9);
			btSd.setMouse(m_mouse);
			btSd.setEvent(v);
			// 描画位置
			btSd.setXY(MUNU_POS_X + MUNU_SIMP_OX, MUNU_POS_Y);
			
			// コンテナにつめる
			PartialMenuItem item;
			
			// 事典ボタン
			item = new PartialMenuItem();
			item.setButton( btSd );
			item.hide();
			
			m_menu.addItem( item );

			// オプション
			item = new PartialMenuItem();
			item.setButton( btOp );
			item.hide();

			m_menu.addItem( item );
	
			// seve load
			item = new PartialMenuItem();
			item.setButton( btSaveLoad );
			item.hide();

			m_menu.addItem( item );

			// キーグループ
			m_keyGroup.add( createNullButton() );
			m_keyGroup.add(btSaveLoad);
			m_keyGroup.add(btOp);
			// TODO 事典機能は中止
//			m_keyGroup.add(btSd);
			
		} catch (Exception e) {
			Log.printFatal("%s#initMenu : [%s]",this.toString(), e.toString());
			throw e;
		}
	
	}
	
	// nullボタンを生成する
	GUIButton createNullButton() {
		auto bt = new GUIButton();
		auto v = new GUINullButtonListener();
		bt.setMouse(this.m_mouse);
		bt.setEvent(v);
		bt.setXY(630,450);
		return bt;
	}
	
	/// 事典コンポーネント生成
	void initDict(FontLoader fl) {
		m_dictCtl = new DictController();
		m_dictCtl.setMouse( m_mouse );
		m_dictCtl.setFontLoader(fl); 
	}
	
	/// ブックマークで選択された情報からScenarioDrawを再構築します
	void initSecectedInfo(CurrentInfo ci) {
		this.nowStoryNo = ci.si.storyNo;
		char[] strText = cast(char[]) FileSys.read(KyojinConst.scenarioFileName[this.nowStoryNo]);
		context.setText( toWCS(strText) );
		
		sd.onLoad( ci.si, context, ci.start );
	}
	
	/// シナリオタスクに遷移する
	void callHeadline(GameInfo info, int sId) {
		info.selectedStory = sId;
		Log.print("EYECATCH REQUEST : %s", sId);
		info.screen.blendSrcAlpha();
		int[1] data;
		data[0] = sId;
		info.setData(data);

		// 強制バックアップを行う
		info.gameSceneTransiter.setTransitType( 21, 3 );
		info.gameSceneTransiter.callScene(KyojinConst.Task.Task_Telop);
	}
	
	/// 既読フラグを立てる
	void setReadFlag(GameInfo info) {
		Log.print("setReadFlag : %s", this.nowStoryNo);
		info.userInfo.setReadFlag( this.nowStoryNo, true );
	}
		
	GameInfo info;
	FixTimer m_autoSaveTimer;	//!< オートセーブに使うタイマー
	PartialMenuBar m_menu;				//!< メニューコントローラ
	bool init;
	DictController m_dictCtl;	//!< 事典コントローラ
	KeyGroup m_keyGroup;	//!< キーグループ
	
	bool showDict;				//! 事典の表示／非表示フラグ
	
	bool jumpShiori;	//!< 遷移フラグ
	bool jumpOption;
	bool callTelop;
	bool callWait;		//!< コールウェイト
	int storyId;
	
	int nowStoryNo;				//!< 今実行中のストーリー番号
	
	TextDrawContext context;	//!< 描画コンテキスト
	ScenarioInfo si;			//!< シナリオモデル
	
	ScenarioDrawEx sd;			//!< ScenarioDrawコンポーネント
	long pageHeaderOffset = 0;
	MouseInput m_mouse;			//!< マウスデバイス
	KeyInputBase m_key;
	
	Texture texTest;
	Texture skipLayer;
}