module kyojintati4d.taskscenario;

private import std.gc;
private import std.regexp;

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 y4d_draw.surfacefontrepository;
private import y4d_draw.surfaceloader;
private import y4d_draw.scenariodraw;
private import y4d_aux.lineparser;

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 yamalib.draw.scenariothumbnail;
private import yamablib.dict.dictcontroller;

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

	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return cast(char[]) 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 (!selectWaiting) {
				// キー操作によるフォーカシング
				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);
					}
				}
				
				if (!drawThumbnail) {
					// Ctrl + TAB
					if ((m_key.isPress(11) || m_key.isPress(12)) && m_key.isPress(14)) {
						// サムネール描画開始！
						drawThumbnail = true;
						auto nowPageNo = sd.getPageCount();
						if (nowPageNo > thumbnailDraw.getThumbnailLimitNo()) {
							thumbnailDraw.setThumbnailLimitNo(nowPageNo);
						}
						thumbnailDraw.setThumbnailNo( nowPageNo - 1 );
Log.print("=========thumbnail page:%d", nowPageNo - 1);
						thumbnailDraw.fadeIn(5, 10);
						thumbnailFadeIn = true;
					}
				}
				
			} 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 if (drawThumbnail) {
				// サムネール動作
				onMoveThumbnail(info.screen);			
	
			} else {
				// ScenarioDrawの動作
				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 ) {

					setEyeCatchTaskParam();
					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 if ( KyojinConst.Task.Task_EndingKyojin == taskReq ) {
					// 既読フラグ
					setReadFlag(info);
					// メインパートエンディング
					jumpEnding = true;

					// すべてのスタックを破棄		
					info.gameSceneTransiter.exitScene();
					// 強制バックアップを行う
					info.gameSceneTransiter.setTransitType( 19, 1, true, true );
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_EndingKyojin);
					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);
			}
			
			// メニューランチャーの動作
			onMoveMenu(info.screen);

			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);
		}
	}
	
	
	/// EyeCatch タスクリクエストを処理する
	private void setEyeCatchTaskParam() {

		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_TelopKyojin;	// 呼び出してほしいタスクね
				v[1] = -1;					// 呼び出してほしいシナリオ番号ね
				v[2] = TaskEyeCatche.SHI_TYPE.MIDO01;		// 呼び出してほしいアイキャッチ（シナリオに対応したやつってこと）ね
				info.setData(v);
		 	}
		 	break;
*/
/*		 	
		 case KyojinConst.ScenarioID.KYOJIN03:
		 	{
				int[] v = new int[3];
				v[0] = KyojinConst.Task.Task_TelopKyojin;	// 呼び出してほしいタスクね
				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_TelopKyojin);
	}
	
	
	/// サムネールの動作
	private void onMoveThumbnail(Screen screen) {
		if (thumbnailDraw.isFinishSelect()) {
			drawThumbnail = false;
			thumbnailDraw.resetSelect();
			jumbByThumbnail(thumbnailDraw.getActiveInfo()[0]);
		} else if (thumbnailFadeIn) {
			if (!thumbnailDraw.isFadeIn()) {
				// フェードアウト完了
				thumbnailFadeIn = false;
			}
		} else if (thumbnailFadeOut) {
			if (!thumbnailDraw.isFadeOut()) {
				// フェードアウト完了
				thumbnailFadeOut = false;
				drawThumbnail = false;
			}
		} else {
			// TAB
			if (info.key.isPush(14)) {	// TAB で移動
				thumbnailDraw.next();
			} else if (m_key.isPress(6)) {	// Enterで選択
				thumbnailDraw.selectPane(0, 20);
			} else if (m_key.isPress(0)) {	// Escでキャンセル
				thumbnailDraw.fadeOut(5, 10);
				thumbnailFadeOut = true;
			}
		}
		thumbnailDraw.onMove(info.screen);
	}

	/// メニュー動作判定処理
	void onMoveMenu(Screen screen) {
		// メニューボタン判定
		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
	}
	
	
	/// 毎回呼び出すなり
	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);
			}

			if (drawThumbnail) {
				thumbnailDraw.onDraw(info.screen);
			}
			
			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 const char[] PROP_KYE_USE_RUBI_SETTING = "scenario.rubi_enable";

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

	/// オートセーブする間隔
	static const uint AUTO_SAVE_SPAN = 3 * 60 * 1000;	// ３分
	
	/// 去人パートではキャラクター位置を左右に大きくふるので、実画面より大きく取る
	static const uint KYOJIN_VIRTUAL_VIEW_WIDTH = 860;
	
	/// シナリオ２のページ数
	static 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(cast(char[]) "img/common/skip_layer.png");

		// メニューバー生成
		initMenu();
		// 事典コントローラ
		initDict(info.fontloader);
		m_dictCtl.onMove(info.screen);
		
		context = new TextDrawContext();
		// 行間変更ひろげとく
		context.blankHeight = 4;
		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[] scenarioFilename = cast(char[]) KyojinConst.scenarioFileName[this.nowStoryNo];
		if (!loadFlg) {
			// シナリオの音源モードを判定
			if (info.useTestMusic) {
				// テストモード
				info.bgmloader.loadDefRW(FileSys.read(cast(char[]) "snd\\bgm_ex\\list.lst"));
				scenarioFilename = cast(char[]) KyojinConst.scenarioFileName[this.nowStoryNo + 3];
			} else {
				info.bgmloader.loadDefRW(FileSys.read(cast(char[]) "snd\\bgm\\list.lst"));
			}
		}
		
		char[] strText = cast(char[]) FileSys.read( scenarioFilename );
		Log.printLook("TaskScenario#onInit scenario_script : %s size : ",
				scenarioFilename, strText.length);
		context.setText( toWCS(strText) );
		
		
		{
			// ページごとのテキストをいただく
			wchar[][] pageTexts;
			int[] textOffsets;
			while(true) {
				try {
					wchar[] pageText = context.getNextText();
					if (pageText is null) {
						break;
					}
					pageTexts ~= pageText;
					textOffsets ~= context.getHeadTextOffset();
				} catch {
					break;
				}
			}
			// 元に戻す
			context.setTextOffset(0);
			
			// todo test
			thumbnailTest(extractThumbnailInfoTag(strText), pageTexts, textOffsets);
		}
		
		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(cast(char[]) "setting.txt");
		if ("false" == prop.getProperty(cast(char[]) PROP_KYE_USE_RUBI_SETTING,cast(char[]) "true")) {
			this.sd.disenableRubiText();
		}

		// カーソル画像の読込
		Texture t = new Texture();
		t.load( FileSys.makeFullName(cast(char[]) "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(cast(char[]) "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();
		
/*
		// テキスト高さチェック
		{
			while (!sd.isEnd) {
				sd.updateText();
			}
		}
*/
		Log.print("ScenarioDrawEx setting ok");
	}
	
	/// メインパート用のシナリオを生成
	void initMainPartScenarioDraw() {
		// リソースを読み込む
		loadScenarioResource(cast(char[]) MAIN_CHAR_IMG_LIST, cast(char[]) MAIN_BG_IMG_LIST);
		sd = new ScenarioDrawEx(cast(char[]) MAIN_CHAR_IMG_LIST, cast(char[]) MAIN_CHAR_SDF_LIST);
		context.viewWidth = info.screen.getWidth();
		initScenarioDraw();
	}
	
	/// 去人パート用のシナリオを生成
	void initKyojinPartScenarioDraw() {
		loadScenarioResource(cast(char[]) KYOJIN_CHAR_IMG_LIST, cast(char[]) KYOJIN_BG_IMG_LIST);
		sd = new ScenarioDrawEx(cast(char[]) KYOJIN_CHAR_IMG_LIST, cast(char[]) 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_NO_KYOJIN_MAIN_TEXT, KyojinConst.C_FONT_NO_RUBI);
		sd.setOffset(TEXT_OX, TEXT_OY);
		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(cast(char[]) "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(cast(char[]) "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(
				cast(char[]) 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 );
	}

	void thumbnailTest(char[][] thumbnailTags, wchar[][] pageTexts, int[]  textOffsets) {
		// サムネール用にもキャラマップをロード
		ThumbnailInfo.setCharMap(cast(char[]) KYOJIN_CHAR_IMG_LIST);
		// サムネール画像のサーフェイスローダーのセットアップ
//		setupThumbnailLoader(KYOJIN_CHAR_IMG_LIST, MAIN_BG_IMG_LIST);
		setupThumbnailLoader(cast(char[]) KYOJIN_CHAR_IMG_LIST, cast(char[]) KYOJIN_BG_IMG_LIST);
		thumbnailCreator = new ThumbnailCreator(bgLoader, charaLoader, 
				createThumbnailTextDraw(context.clone));
		thumbnailCreator.viewWidth = KYOJIN_VIRTUAL_VIEW_WIDTH;

		int readPos = info.userInfo.getReadPos(nowStoryNo);
		int readPageNo = -1;
		ThumbnailInfo[] thumbnailInfos;
		foreach (i, tagStr; thumbnailTags) {
			auto thumbnailInfo = createThumbnailInfo(tagStr);
			thumbnailInfo.text = pageTexts[i];
			thumbnailInfo.pageNo = i;
			thumbnailInfo.textOffset = textOffsets[i];
			thumbnailInfos ~= thumbnailInfo;
			
			if (-1 == readPageNo && thumbnailInfo.textOffset > readPos) {
				// このページの前まで既読
				readPageNo = i;
			}
		}
		
		if (readPageNo == -1) {
			readPageNo = thumbnailTags.length;
		}

		thumbnailDraw = new ThumbnailDraw(info.screen.getWidth(), info.screen.getHeight());
		thumbnailDraw.thumbnailCreator = thumbnailCreator;
		thumbnailDraw.thumbnailInfos = thumbnailInfos;
		thumbnailDraw.setThumbnailLimitNo(readPageNo);
		Log.print("READ PAGE NO:%d", readPageNo);
printf("setup finish\n");
	}

	/// サーフェイス版の ScenarioDraw を構築する
	ScenarioTextDrawSurface createThumbnailTextDraw(TextDrawContext context) {
		auto textDraw = new ScenarioTextDrawSurface();
		textDraw.setTextDrawContext(context);
		
		// フォント
		auto mainFontRep = textDraw.getSurfaceFontRepository();
		mainFontRep.setLoader(info.fontloader, KyojinConst.C_FONT_NO_KYOJIN_MAIN_TEXT);
		mainFontRep.setMax(300);
		
		auto rubiFontRep = textDraw.getRubiSurfaceFontRepository();
		rubiFontRep.setLoader(info.fontloader, KyojinConst.C_FONT_NO_RUBI);
		rubiFontRep.setMax(300);

		return textDraw;
	}

	/// サムネール情報タグだけ拾う
	static char[][] extractThumbnailInfoTag(char[] scenarioText) {
		char[][] result;
		foreach(regRes; RegExp("<thuminfo.*?>").search(scenarioText)) {
			result ~= cast(char[]) regRes.match(0);
		}
		return result;
	}
	
	/// サムネール情報から指定したページにジャンプする
	void jumbByThumbnail(ThumbnailInfo thumbInfo) {
Log.print("=========thumbnail page:%s", thumbInfo.pageNo);
		sd.onLoad( createScenarioInfoFromThumbnailInfo(thumbInfo), context, false );
	}
	
	/// サムネール情報からシナリオ情報を構築する
	ScenarioInfo createScenarioInfoFromThumbnailInfo(ThumbnailInfo thumbInfo) {
		auto scenarioInfo = new ScenarioInfo();

		scenarioInfo.storyNo = nowStoryNo;	// シナリオ番号
		scenarioInfo.bgNo = thumbInfo.bgNo;
		scenarioInfo.bgmNo = thumbInfo.bgmNo;
		scenarioInfo.bgmVol = 128;
		scenarioInfo.bgmLoop = 1;
		scenarioInfo.seNo = thumbInfo.seNo;
		scenarioInfo.seLoop = 0;
		scenarioInfo.seVol = 128;
		scenarioInfo.page = thumbInfo.pageNo + 1;	// １ページからはじまる
		scenarioInfo.pos = thumbInfo.textOffset;
		scenarioInfo.textPos = int.max;
		scenarioInfo.analyzedTag = int.max;
		
		if (thumbInfo.charaLeft !is null) {
			scenarioInfo.charpos[0] = 0;
			scenarioInfo.preset_pos[0] = 0;
			scenarioInfo.strCharId[0] = thumbInfo.charaLeft;
//			scenarioInfo.vCharColor[0] = ;
		}

		if (thumbInfo.charaCenter !is null) {
			scenarioInfo.charpos[1] = 0;
			scenarioInfo.preset_pos[1] = 1;
			scenarioInfo.strCharId[1] = thumbInfo.charaCenter;
//			scenarioInfo.vCharColor[0] = ;
		}

		if (thumbInfo.charaRight !is null) {
			scenarioInfo.charpos[2] = 0;
			scenarioInfo.preset_pos[2] = 2;
			scenarioInfo.strCharId[2] = thumbInfo.charaRight;
//			scenarioInfo.vCharColor[0] = ;
		}

		return scenarioInfo;
	}
	
	
	/// スクリプトタグ文字列から、データクラスを構築し返却する
	static ThumbnailInfo createThumbnailInfo(char[] thumbTagStr) {
		auto thumbInfo = new ThumbnailInfo();
		char[][] splitStr = cast(char[][]) std.string.split(std.string.chomp(thumbTagStr, ">"));
		foreach (str; splitStr) {
			if (-1 != std.string.find(str, "=")) {
				char[][] keyValue = cast(char[][]) std.string.split(str, "=");
				char[] key = cast(char[]) std.string.toupper(keyValue[0]);
				char[] value = keyValue[1];
				switch(key) {
				case "BG":
					thumbInfo.bgNo = std.conv.toInt(value);
					break;
				case "CHARA0":
					thumbInfo.charaLeft = value;
					break;
				case "CHARA1":
					thumbInfo.charaCenter = value;
					break;
				case "CHARA2":
					thumbInfo.charaRight = value;
					break;
				case "BGM":
					thumbInfo.bgmNo = std.conv.toInt(value);
					break;
				case "BGM_LOOP":
					thumbInfo.bgmLoop = std.conv.toInt(value);
					break;
				case "SE":
					thumbInfo.seNo = std.conv.toInt(value);
					break;
				case "SE_LOOP":
					thumbInfo.seLoop = std.conv.toInt(value);
					break;
				default:
					Log.print("undefined thumbnail tag key:%s", key);
					break;
				}
			}
		}
		
		return thumbInfo;
	}
	
	/// サムネールが使用する各種リソースローダーを準備する
	void setupThumbnailLoader(char[] charaList, char[] bgList) {
		if (bgLoader is null) {
			bgLoader = new SurfaceLoader();
		} else {
			bgLoader.releaseAll();
		}
		if (charaLoader is null) {
			charaLoader = new SurfaceLoader();
		} else {
			charaLoader.releaseAll();
		}
		if (mainFontRepository is null) {
		}
		y4d_result res = charaLoader.loadDefRW(FileSys.read(charaList));
		Log.print("charaLoader: " ~ getErrorName(res));
		res = bgLoader.loadDefRW(FileSys.read(bgList));
		Log.print("bgLoader: " ~ getErrorName(res));
	}
	
	
	static const int TEXT_OX = 45;
	static const int TEXT_OY = 28;
	
	GameInfo info;
	FixTimer m_autoSaveTimer;	//!< オートセーブに使うタイマー
	PartialMenuBar m_menu;				//!< メニューコントローラ
	bool init;
	DictController m_dictCtl;	//!< 事典コントローラ
	KeyGroup m_keyGroup;	//!< キーグループ
	
	bool showDict;				//! 事典の表示／非表示フラグ
	
	bool jumpShiori;	//!< 遷移フラグ
	bool jumpOption;
	bool jumpEnding;
	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 skipLayer;
	
	bool drawThumbnail = false;
	bool thumbnailFadeOut = false;
	bool thumbnailFadeIn = false;
	ThumbnailDraw thumbnailDraw;
	ThumbnailCreator thumbnailCreator;
	Texture thumbImg;
	SurfaceLoader bgLoader;
	SurfaceLoader charaLoader; 
	SurfaceFontRepository mainFontRepository;
	
}