module kyojintati4d.taskbookmark;

private import std.ctype; // tolower
private import std.string;	// toString

private import y4d;
private import y4d_input.mouse;
private import y4d_draw.scenariodraw;
private import y4d_aux.lineparser;
private import y4d_aux.widestring;

private import kyojintati4d.val.kyojinconst;

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

private import yamalib.draw.spiralmove;
private import yamalib.draw.directivityobject;
private import yamalib.gui.dialog;
private import yamalib.gui.keygroup;

private import yamalib.counterfsp;
private import yamalib.gui.guibutton;
private import yamalib.log.log;

/**L
	ブックマークタスククラス
   ブックマークからセーブデータを読み込み表示、遷移する
 */
class TaskBookmark : GameTaskBase {

	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return KyojinConst.TASK_NAME[KyojinConst.Task.Task_Bookmark];
		}
	}

	/// 移動の処理を行います
	override int onMove(Object o) {
		
		try {
			info = cast(GameInfo)o;
			MouseInput mouse = getMouse();
			mouse.update();
	
			if (!init) {
				onInit();
				mouse.setGuardTime( 100 );
				init = true;
			}
	
			if (m_fadeIn || m_fadeOut) {
				m_alpha++;
				if (m_alpha.isEnd()) {
					m_fadeIn = false;
				}
			}
			// スロットの動作
			foreach(int i, GUIButton bt; m_slot) {
				if ( m_showDialog ) {
					break;
				}
				assert(!(bt is null));
				if ( m_fadeIn || m_fadeOut ) {
					continue;
				}
				
				bt.onMove(info.screen);
				if ( bt.isLClick() ) {
					m_targetBookmark = getBookmarkBySlotNo(i);
					showDialog();
					break;
				} else if ( bt.isIn() ) {
					m_focusSlotNo = i;
					m_targetBookmark = getBookmarkBySlotNo(i);
				}
			}
			
			// 戻るボタン
			m_btBack.onMove(info.screen);
			m_btReturn.onMove(info.screen);
			m_btFinish.onMove(info.screen);
			
			// ダイアログ値取得
			if (m_showDialog && TaskYesNo.CLICK.NO_CLICK != info.yesnoClick) {
				
				if ( DIALOG_TYPE.SAVABLE == m_dialogType ) {
					switch (cast(int) info.yesnoClick) {
					case 0:
						clickSave();
						Log.print("CLICK SAVE");
						// セーブ
						break;
					case 1:
						// ロード
						clickLoad();
						Log.print("CLICK LOAD");
						break;
					case 2:
						// 削除
						clickRemove();
						Log.print("CLICK REMOVE");
						break;
					case 3:
						// キャンセル
						Log.print("CLICK CANCEL");
						break;
					default:
						Log.printError("CLICK NO = %d", info.yesnoClick);
						assert(false);
					}
				} else if (DIALOG_TYPE.LOAD == m_dialogType) {
					switch (cast(int) info.yesnoClick) {
					case 0:
						// ロード
						clickLoad();
						Log.print("CLICK LOAD");
						break;
					case 1:
						// 削除
						clickRemove();
						Log.print("CLICK REMOVE");
						break;
					case 2:
						// キャンセル
						Log.print("CLICK CANCEL");
						break;
					default:
						Log.printError("CLICK NO = %d", info.yesnoClick);
						assert(false);
						break;
					}
				} else if (DIALOG_TYPE.SAVE == m_dialogType) {
					switch (cast(int) info.yesnoClick) {
					case 0:
						// セーブ
						clickSave();
						Log.print("CLICK LOAD");
						break;
					case 1:
						// キャンセル
						Log.print("CLICK CANCEL");
						break;
					default:
						Log.printError("CLICK NO = %d", info.yesnoClick);
						assert(false);
						break;
					}
				} else {
					switch (cast(int) info.yesnoClick) {
					case 0:
						// ロード
						clickLoad();
						Log.print("CLICK LOAD");
						break;
					case 1:
						// キャンセル
						Log.print("CLICK CANCEL");
						break;
					default:
						Log.printError("CLICK NO = %d", info.yesnoClick);
						assert(false);
						break;
					}
				}
				m_showDialog = false;
			}


			// キー操作によるフォーカシング
			if ( info.key.isPush(1) ) {
				m_keyGroup.prev();
			} else if ( info.key.isPush(2) ) {
				m_keyGroup.next();
			} else {
				// フォーカス移動
				foreach (GUIButton parts; m_slot) {
					if ( parts.isIn() ) {
						m_keyGroup.setSelectNo(parts);
						break;
					}
				}
				if ( m_btBack.isIn() ) {
					m_keyGroup.setSelectNo(m_btBack);
				}
				if ( m_btReturn.isIn() ) {
					m_keyGroup.setSelectNo(m_btReturn);
				}
				if ( m_btFinish.isIn() ) {
					m_keyGroup.setSelectNo(m_btFinish);
				}
			}

			// スフィアの移動
			m_dm.onMove(info.screen);
			
			if ( !m_fadeIn && !m_fadeOut) {
				if ( m_btBack.isLClick() && m_alpha.isEnd()) {
					// 何も選択されなかったので消してしまう
					Bookmark.setCurrentInfo(null);
					m_dm.breakOutAll();
					
					if ( !m_fadeIn && !m_fadeOut) {
						returnTask();
					}
	
					info.playSystemSE(SYS_SE.CLICK_TITLE_NEGATIVE);
				} else if ( m_btReturn.isLClick() ) {
					m_jumpTitle = true;
					returnTask();
					// タイトル画面に戻る
					Log.print("CLICK TO TITLE");
					
				} else if ( m_btFinish.isLClick() ) {
					// 確認ダイアログ表示
					info.showDialog(TaskYesNo.TYPE.FINISH);
					Log.print("CLICK TO FINISH");
				}
			} 
	
			int ret = 0;
			if (m_fadeOut && m_alpha.isEnd()) {
				// シーン遷移
				if ( Bookmark.getTitle() && m_jumpSenerio) {
					// しおりの動作モードを変更
					Bookmark.setTitle(false);
	
					// タイトルＢＧＭの停止
					info.bgmloader.get(0).stopFade(1000);

					// 全てのシーンスタックを破棄して今までのことはなかったことにする！8(ﾟДﾟ)8
					info.gameSceneTransiter.exitScene();
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_Scenario);
					
					Log.print("%s#onMove: bookmark -> scenario jump", super.toString());
				} else if (m_jumpTitle) {
					
					// すべてのサウンドを停止しておく
					Sound.stopAll();
					
					// 全てのシーンスタックを破棄して今までのことはなかったことにする！8(ﾟДﾟ)8
					info.gameSceneTransiter.exitScene();
					info.gameSceneTransiter.jumpScene(KyojinConst.Task.Task_Title);

					Log.print("%s#onMove: bookmark -> title jump", super.toString());
					
				} else {
					info.gameSceneTransiter.returnScene();
				}

				ret = -1;
				destroy();
				Log.print("%s#onMove: destroy finish!", super.toString());
			}
			return ret;

		} catch (Exception e) {
			Log.printFatal("Exception %s#onMove : [%s] [%s]", super.toString(), e.toString(), e.msg);
			throw e;
		}
		
	}

	/// 描画の処理を行います
	override int onDraw(Object o) {
		try {
			info = cast(GameInfo)o;
			if (!init) {
				return 0;
			}
			info.screen.clear();
			info.screen.blendSrcAlpha();
			info.screen.setColor(255,255,255,m_alpha.get());

			// 背景の描画
			drawBGMiddle(info.screen);
			
			info.screen.blt(m_bg,0,0);

			// スフィア描画
			m_dm.onDraw(info.screen);

			drawBGUpper(info.screen);
			darwBGUnder(info.screen);
			
			// ブックマーク番号描画
			info.screen.blt(m_bookmarkNo.get(m_focusSlotNo), BOOKMARK_NO_X, BOOKMARK_NO_Y);
			
			// スロットの描画
			foreach(GUIButton bt; m_slot) {
				bt.onDraw(info.screen);
			}
			// 戻るボタン
			m_btBack.onDraw(info.screen);
			
			// 詳細描画
			drawFocusSlotDetail(info.screen);
			
			// メニュー描画
			m_btFinish.onDraw(info.screen);
			if (!Bookmark.getTitle()) {
				m_btReturn.onDraw(info.screen);
			}
			
			
		} catch (Exception e) {
			Log.printFatal("Exception %s#onDraw : [%s] [%s]", super.toString(), e.toString(), e.msg);
			throw e;
		}

		return 0;
	}

	/// レガシーなやつ
	override int task(Object o) {
		return 0;
	}

	/// 占有しているメモリを解放する
	override void destroy() {
		if (m_dm) {
			m_dm.destroy();
			m_dm = null;
		}
		m_bookmarks = null;
		m_targetBookmark = null;
		m_slot = null;
		m_btFinish = null;
		m_btReturn = null;
		m_btBack = null;	
		m_keyGroup = null;
		Texture.safeRelease(m_bg);
		
		if ( !(m_bookmarkNo is null) ) {
			m_bookmarkNo.releaseAll();
			m_bookmarkNo.releaseFileList();
			m_bookmarkNo = null;
		}
		if ( !(m_scenarioName is null) ) {
			m_scenarioName.releaseAll();
			m_scenarioName.releaseFileList();
			m_scenarioName = null;
		}
		
		m_alpha = cast(RootCounterS) null;
		init = false;

		Log.print("%s#destroy : destroyed.", super.toString());
	}

	/// コンストラクタ
	this() {
		m_alpha = new RootCounterS;
		m_alpha.set(0, 255, FADE_SPEED);
		m_fadeIn = true;
		m_keyGroup = new KeyGroup();
	}
	
	/// 静的コンストラクタ
	static this() {
		fontRep = new FontRepository();
		int[int] tmp;
		tmp[KyojinConst.ScenarioID.STORY01] = 0;
		tmp[KyojinConst.ScenarioID.STORY02_0] = 1;
		tmp[KyojinConst.ScenarioID.STORY02_1] = 1;
		tmp[KyojinConst.ScenarioID.STORY02_2] = 1;
		tmp[KyojinConst.ScenarioID.STORY03] = 2;
		tmp[KyojinConst.ScenarioID.STORY04] = 3;
		tmp[KyojinConst.ScenarioID.STORY05] = 4;
		tmp[KyojinConst.ScenarioID.STORY06] = 5;
		tmp[KyojinConst.ScenarioID.STORY07] = 6;
		tmp[KyojinConst.ScenarioID.KYOJIN01] = 7;
		tmp[KyojinConst.ScenarioID.KYOJIN02] = 8;
		tmp[KyojinConst.ScenarioID.KYOJIN03] = 9;
		STORYNO_LISTNO = tmp;
	}
	
private:
	
	/// ScenarioInfoのstoryNoとしおり詳細のシナリオ名リスト番号とのマップ
	static final const int[int] STORYNO_LISTNO; 

	/// ダイアログのタイプ
	enum DIALOG_TYPE { LOAD, SAVABLE, LOAD_AUTO, SAVE  };
	
	/// 画面フェード速度
	static const int FADE_SPEED = 3;

	/// 描画位置定義
	static const int AUTO_X = 227;
	static const int AUTO_Y = 195;
	static const int SLOT1_X = AUTO_X;
	static const int SLOT1_Y = 246;
	static const int SLOT2_X = AUTO_X;
	static const int SLOT2_Y = 299;
	static const int SLOT3_X = AUTO_X;
	static const int SLOT3_Y = 351;
	
	static const int[] SLOT_X = [AUTO_X,SLOT1_X,SLOT2_X,SLOT3_X];
	static const int[] SLOT_Y = [AUTO_Y,SLOT1_Y,SLOT2_Y,SLOT3_Y];

	static const int TEXNO_AUTO = 0;
	static const int TEXNO_01 = 1;
	static const int TEXNO_02 = 2;
	static const int TEXNO_03 = 3;
	
	/// 栞番号描画位置定義
	static const int BOOKMARK_NO_X = 0;
	static const int BOOKMARK_NO_Y = 54;
	
	static const char[] BOOKMARKNO_MAIN_LST = "img/bookmark/bmno_main.lst";
	static const char[] BOOKMARKNO_KYOJIN_LST = "img/bookmark/bmno_kyojin.lst";

	static const char[] SCENARIO_NAME_LST = "img/bookmark/scenario_name.lst";

	static const char[] MENU_BUTTON_LST = "img/bookmark/menu_bt.lst";
	
	/// シナリオ名描画位置定義
	static const int SCENARIO_NAME_X = 254;
	static const int SCENARIO_NAME_Y = 10;

	/// メニュー描画位置定義
	static const int FINISH_X = 0;
	static const int FINISH_Y = 424;
	static const int RETURN_X = 156;
	static const int RETURN_Y = FINISH_Y;
	static const int BACK_X = 539;
	static const int BACK_Y = 425;
	
	/// しおり詳細日付描画位置
	static const int DATE_X = 410;
	static const int DATE_Y = 73;

	/// しおり詳細ページ数描画位置
	static const int PAGE_X = 585;
	static const int PAGE_Y = DATE_Y;
	
	
	enum TEXT_OFFSET {X=272, Y=95};
	static const int MAX_TEXT_LINE = 3;
	static const int FONTSIZE = 20;
	static const int TEXTWIDTH = 340;
	
	static const float TEXT_RATE = 0.7f;
	
	static const int PAGE_FULL_LEN = 3;
	
	/// 背景用切抜き矩形
	static const Rect BG_UPPER_RC = {left:0, top:0, right:640, bottom:180};
	static const Rect BG_MIDDLE_RC = {left:0, top:180, right:640, bottom:415};
	static const Rect BG_UNDER_RC = {left:0, top:415, right:640, bottom:480};


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

		m_fadeIn = true;
		m_fadeOut = false;

		// しおりの初期化
		foreach (inout Bookmark bm; info.bookmark) {
			if ( Bookmark.getTitle() ) {
				bm.setReadOnly(true);
			}
			bm.resetBtFlag();
		}
		Bookmark.removeCurrentInfo();
		
		// スフィアの生成
		initDirectivityObject();
		
		// リポジトリの設定
		fontRep.setLoader(info.fontloader, KyojinConst.C_FONT_NO_SEVE_TEXT);
		fontRep.setMax(500);
		
		// 新規画像
		m_bookmarks = info.bookmark;	// エイリアス使うのは汚いんか？
		// 表示モードの設定
		m_mainpartMode = isMainPartBookmark( getAutoBookmark() );

//debug
//m_mainpartMode = false;
		initSlotBt(getMouse());
		initTexture();
		
		// デフォルトフォーカス
		if (Bookmark.getTitle()) {
			m_keyGroup.focus(m_slot[0]);
		} else {
			m_keyGroup.focus(m_btBack);
		}
		
		Log.print("%s init finish!", super.toString() );
	}
	
	/// オートしおりを取得する
	Bookmark getAutoBookmark() {
		foreach (inout Bookmark bm; m_bookmarks) {
			if (bm.isAuto()) {
				return bm;
			}
		}
		assert(false);
	}
	
	/// ブックマークのシナリオ番号から去人かメインを判断する
	bool isMainPartBookmark(Bookmark bookmark) 
	in
	{
		assert( !(bookmark is null) );
	}
	body
	{
		return cast(bool) (KyojinConst.ScenarioID.KYOJIN01 > bookmark.getScenarioInfo.storyNo);
	}

	/// マウスの取得
	MouseInput getMouse() {
		return info.keyDecolateMouse;
	}

	/// スロットを初期化する
	void initSlotBt(MouseInput mouse_) 
	in
	{
		assert( !(mouse_ is null) );
		assert( SLOT_X.length == SLOT_Y.length);
	}
	body
	{
		m_slot =  new GUIButton[4];
		
		// スロットリストファイルを読み込む
		TextureLoader tl = new TextureLoader();
		tl.loadDefFile("img/bookmark/slot_bt.lst");
		
		foreach (int i,inout GUIButton bt; m_slot) {
			bt = new GUIButton();
			bt.setMouse(mouse_);
			bt.setXY(SLOT_X[i], SLOT_Y[i]);
			auto v = new GUINormalButtonListener();
			v.setTextureLader(tl,i*2);
			// on/off + focus
			v.setType(9);
			bt.setEvent(v);
			// キーグループに登録
			m_keyGroup.add(bt);
		}
		
	}
	
	
	/// 使用するテクスチャの初期化
	void initTexture() {
		// メニューボタン用TextureLoader生成		
		auto tl = new TextureLoader();
		tl.loadDefFile(MENU_BUTTON_LST);
		// シナリオ名
		m_scenarioName = new TextureLoader();
		// メイン、KYOJIN非依存
		m_scenarioName.loadDefFile(SCENARIO_NAME_LST);

		// 栞番号
		m_bookmarkNo = new TextureLoader();
		// 背景
		m_bg = new Texture();

		if (m_mainpartMode) {
			// メインパート
			m_bg.load("img/bookmark/bm_bg_a.png");
			m_bookmarkNo.loadDefFile(BOOKMARKNO_MAIN_LST);
		} else {
			// 去人パート
			m_bg.load("img/bookmark/bm_bg_b.png");
			m_bookmarkNo.loadDefFile(BOOKMARKNO_KYOJIN_LST);
		}

		m_btFinish = new GUIButton();
		auto v = new GUINormalButtonListener();
		v.setType(9);
		v.setTextureLader(tl,0);
		m_btFinish.setEvent(v);
		m_btFinish.setMouse(getMouse());
		m_btFinish.setXY(FINISH_X, FINISH_Y);

		m_btReturn = new GUIButton();
		v = new GUINormalButtonListener();
		v.setType(9);
		v.setTextureLader(tl,2);
		m_btReturn.setEvent(v);
		m_btReturn.setMouse(getMouse());
		m_btReturn.setXY(RETURN_X, RETURN_Y);
		
		static const GUIBlinkButtonListener.TYPE BT_TYPE_BACK = 
			GUIBlinkButtonListener.TYPE.DEFAULT	| GUIBlinkButtonListener.TYPE.BLINK_FIX;
		m_btBack = new GUIButton();
		auto vBack = new GUIBlinkButtonListener();
		vBack.setBlinkSpeed(4);
		vBack.setType(BT_TYPE_BACK);
		vBack.setTextureLader(tl,4);
		m_btBack.setXY(BACK_X, BACK_Y);
		m_btBack.setMouse(getMouse());
		m_btBack.setEvent(vBack);
		
		m_keyGroup.add(m_btBack);
		if (!Bookmark.getTitle()) {
			m_keyGroup.add(m_btReturn);
		}
		m_keyGroup.add(m_btFinish);
		

	}
	
	/// ダイアログボックスを表示する
	void showDialog() {
		if (Bookmark.getTitle() && !m_targetBookmark.isUse()) {
			info.playSystemSE(SYS_SE.CLICK_TITLE_NEGATIVE);
			return;
		}
		if ( m_targetBookmark.isAuto() ) {
			info.showDialog(TaskYesNo.TYPE.LOAD_AUTO);
			m_dialogType = DIALOG_TYPE.LOAD_AUTO;
		} else if ( !Bookmark.getTitle() && !m_targetBookmark.isUse() ) {
			info.showDialog(TaskYesNo.TYPE.SAVE_ONLY);
			m_dialogType = DIALOG_TYPE.SAVE;
		} else if ( !Bookmark.getTitle() ) {
			info.showDialog(TaskYesNo.TYPE.SAVE);
			m_dialogType = DIALOG_TYPE.SAVABLE;
		} else {
			info.showDialog(TaskYesNo.TYPE.LOAD);
			m_dialogType = DIALOG_TYPE.LOAD;
		}
		m_showDialog = true;
	}
	
	static int getListNoByStoryNo(int storyNo) {
		if (storyNo in STORYNO_LISTNO) {
			return STORYNO_LISTNO[storyNo];
		}
		return 0;
	}
	
	/// 現在フォーカスしているスロットの詳細を描画します1
	void drawFocusSlotDetail(Screen screen) {
		// 何も選択していないなら描画しない
		if (m_targetBookmark is null || !m_targetBookmark.isUse()) {
			return;
		}
		
		// シナリオ名描画
		screen.blt(m_scenarioName.get(
			getListNoByStoryNo(m_targetBookmark.getScenarioInfo.storyNo)), 
			SCENARIO_NAME_X, SCENARIO_NAME_Y);
		
		// ページ数描画
		drawDetailPageNo(screen);
		
		// 日付描画
		drawDetailDateText(screen);
		
		// テキスト描画
		drawDetailText(screen);
	}
	
	/// 詳細部の頁番号を描画する
	void drawDetailPageNo(Screen screen) {
		try {
			wchar[] pageStr = toWCS(std.string.toString(m_targetBookmark.getScenarioInfo.page+1) );
			
			// ゼロ埋めする
			if (PAGE_FULL_LEN > pageStr.length) {
				wchar[] zeroStr;
				for (int i = 0; i <PAGE_FULL_LEN - pageStr.length; ++i) {
					zeroStr ~= "0";
				}
				pageStr = zeroStr ~ pageStr;
			}
			
			auto pageText = fontRep.getTexture( pageStr );
			int x = PAGE_X;
			int y = PAGE_Y;
			foreach(inout TextureVector.TextureInfo ti; pageText.getTextureInfo()) {
				screen.bltRotate(ti.texture,
								 x,
								 y,
								 0, TEXT_RATE, 0);
				x += cast(int) (ti.texture.getWidth() * TEXT_RATE);
			}
			
			// 「頁」の表示
			screen.bltRotate(fontRep.getTexture( '頁' ),
							 x,
							 y,
							 0, TEXT_RATE, 0);
			
		} catch(Object e) {
			Log.printFatal("Exception %s#drawDetailPageNo : [%s]", super.toString(), e.toString());
			throw e;
		}
	}
	
	/// 詳細部の日付を描画する
	void drawDetailDateText(Screen screen) 
	in
	{
		assert( !(fontRep is null) );
		assert( !(m_targetBookmark is null) );
	}
	body
	{
		try {
			auto dateStr = fontRep.getTexture( toWCS(m_targetBookmark.getDateString()) );
			int x = DATE_X;
			int y = DATE_Y;
			foreach(inout TextureVector.TextureInfo ti; dateStr.getTextureInfo()) {
				screen.bltRotate(ti.texture,
								 x,
								 y,
								 0, TEXT_RATE, 0);
				x += cast(int) (ti.texture.getWidth() * TEXT_RATE);
			}
		} catch(Object e) {
			Log.printFatal("Exception %s#drawDetailDateText : [%s]", super.toString(), e.toString());
			throw e;
		}
	}
	
	/// 詳細部の文章部分を描画する
	void drawDetailText(Screen screen) {
		try {
			auto text = getDetailText(m_targetBookmark.getText(), TEXT_RATE);
			foreach (inout ScenarioTextDraw.DrawChar dc; text) {
				screen.bltRotate(dc.texture,
								 cast(int) dc.pos.x,
								 cast(int) dc.pos.y,
								 0, dc.size, 0);
			}
		} catch(Object e) {
			Log.printFatal("Exception %s#drawDetailText : [%s]", super.toString(), e.toString());
			throw e;
		}
	}
	
	/// スフィアの初期化
	void initDirectivityObject() {
		m_dm = new DirectivityObjectManager();
		m_dm.initDefFile( createTargets(50) );
		m_dm.randomizeAll(3.5f);
	}
	
	/// デコイを作成する
	IDirectivityTarget[] createTargets(int num) {
		IDirectivityTarget[] targets;
		SimpleDecoy sd;
		
		for (int i = 0; i < num; ++i) {
			sd = new SimpleDecoy();
			sd.setMouse( getMouse() );
			sd.setType( SimpleDecoy.TYPE.RANDOM_FIX );
			sd.setRandomFix( 640, 480, 68 );
			
			targets ~= sd;
		}
		
		return targets;
	}
	
	/// ロード選択時処理
	void clickLoad() 
	in
	{
		assert( !(m_targetBookmark is null) );
	}
	body
	{
		Log.print("SAVE DATA LOADING...");
		info.restart = false;
		info.selectedBookmark = getBookmarkIDBytarget();
		Log.print("SELECTED BOOKMARK IS %s", info.selectedBookmark);
		m_targetBookmark.doLoad();
		returnTask();
		m_jumpSenerio = true;
	}
	
	/// 現在ターゲットにしているブックマークからIDを取得する
	int getBookmarkIDBytarget() {
		foreach (int i, inout Bookmark bm; m_bookmarks) {
			if (m_targetBookmark is bm) {
				Log.print("FOUND : %s", i);
				return i;
			}
		}
		assert(false);
	}
	
	/// セーブ選択時処理
	void clickSave() {
		m_targetBookmark.doSave();
		// セーブデータを保存しとく
		info.saveAppData();
	}
	
	/// 削除選択時処理
	void clickRemove() {
		m_targetBookmark.doRemove();
		// セーブデータを保存しとく
		info.saveAppData();
	}
	

	/// タスクを終了して前のシーンに帰る準備
	void returnTask() {
		m_fadeOut = true;
		m_alpha.set(255, 0, FADE_SPEED);
	}
	
	/// スロットの番号から対象のブックマークを取得する
	Bookmark getBookmarkBySlotNo(int no_) 
	in
	{
		assert( !(m_bookmarks is null) );
	}
	body
	{
		if (TEXNO_AUTO == no_) {
			return m_bookmarks[3];
		}
		return  m_bookmarks[no_-1];
	}
	
	/// 表示テキストの設定
	ScenarioTextDraw.DrawChar[] getDetailText(wchar[] ww, float rate) {
		ScenarioTextDraw.DrawChar[] drawchar = null;
		LineParser lineparser = new LineParser();
		int pos;
		int line;
		Point drawpos;
		drawpos.x = TEXT_OFFSET.X;
		drawpos.y = TEXT_OFFSET.Y;
		
		if (ww is null) {
			Log.print("%s#setText ww is null",this.toString());
			return null;
		}
		
		while(true) {
			// 取得完了
			if (line == MAX_TEXT_LINE) break;
			if(ww.length <= pos) break;

			wchar c = ww[pos++];
			// タグの読み飛ばし
			if (c=='<') {
				int startPos = pos;
				while(true) {
					c = ww[pos];
					if(c=='>') break;
					pos++;
				}
				wchar[] tag = ww[startPos..pos];
				pos++;

				//	どのタグに該当するかを調べる
				static char[][] tags = [
					"br" 	// 改行
				];

				lineparser.setLine(y4d_aux.widestring.toMBS(tag));
				char[] cc = lineparser.getStr();

				// タグは小文字化して探査しよう
				foreach (inout char ch; cc) {
					ch = std.ctype.tolower(ch);
				}

				int found = -1; // not found marker
				for(int i = 0; i < tags.length; ++i){
					if (cc == tags[i]) { found = i; break; }
				}
				if (found == -1)  continue;

				//	このタグの処理
				switch(found) {
				case 0: // <BR> : 行送り処理
					if (cast(int) drawpos.x == TEXT_OFFSET.X) break;
					line++;
					if (line != 2) {
						drawpos.x = TEXT_OFFSET.X;
						drawpos.y += FONTSIZE;
					}
					break;
				default:
					assert(false);
				}
				continue;
			}

			if (c=='\t') continue;
			if (c=='\n') continue;
			if (c=='\r') continue;

			Texture t = fontRep.getTexture(c);
			if (t is null) {
				Log.printWarn("Bookmark Can't create Texture:%s", c);
				continue;
			}

			int tHeight = cast(int) t.getHeight();
			int tWidth = cast(int) t.getWidth();

			ScenarioTextDraw.DrawChar dc = new ScenarioTextDraw.DrawChar;
			dc.texture = t;
			dc.pos = drawpos;
			dc.size = rate;
			
			drawchar ~= dc;

			if (tWidth < 22) {
				// 半角ぽ
				drawpos.x += cast(int) ((FONTSIZE / 2) * rate);
			} else {
				drawpos.x += cast(int) (FONTSIZE * rate);
			}

			if(line==MAX_TEXT_LINE-1 && drawpos.x >= (TEXTWIDTH + TEXT_OFFSET.X) - FONTSIZE * 2) {
				break;
			} else if (drawpos.x >= TEXTWIDTH + TEXT_OFFSET.X) {
				drawpos.x = TEXT_OFFSET.X;
				drawpos.y += FONTSIZE;
				line++;
			}
		}

		// 付帯文字(....)の追加
		for(int i = 0; i < 4; ++i) {
			ScenarioTextDraw.DrawChar dc = new ScenarioTextDraw.DrawChar();
			dc.texture = fontRep.getTexture('.');
			dc.pos.x = drawpos.x + (FONTSIZE / 2) * i;
			dc.pos.y = drawpos.y;
			dc.size = rate;
			drawchar ~= dc;
		}
		
		return drawchar;
	}
	
	/// 指定のダイアログボックス定義ファイルより設定を読み込み、ダイアログを生成する
	DialogBox createSelection(char[] filename) {
		return DialogBox.createDialogBox(
				info.createDialogResources(),
				filename,
				getMouse() );
	}
	
	/// 背景の上部を描画する
	void drawBGUpper(Screen screen) {
		drawBG(screen, &BG_UPPER_RC);
	}
	
	/// 背景の中央部を描画する
	void drawBGMiddle(Screen screen) {
		drawBG(screen, &BG_MIDDLE_RC);
	}
	
	/// 背景の下部を描画する
	void darwBGUnder(Screen screen) {
		drawBG(screen, &BG_UNDER_RC);
	}
	
	/// 指定した矩形部分だけ背景を描画する
	void drawBG(Screen screen, Rect* rc) {
		screen.blt(m_bg, cast(int) rc.left, cast(int) rc.top, rc);
	}

private:
	static final const int OFFSET_X = 600;

	GameInfo info;
	bool init = false;
	bool m_fadeIn;
	bool m_fadeOut;
	
	RootCounterS m_alpha;

	// 刷新
	static FontRepository fontRep;
	DirectivityObjectManager m_dm;
	Bookmark[] m_bookmarks;
	Bookmark m_targetBookmark;
	GUIButton[] m_slot;
	GUIButton m_btFinish;
	GUIButton m_btReturn;
	GUIButton m_btBack;	
	KeyGroup m_keyGroup;
	Texture m_bg;
	TextureLoader m_bookmarkNo;
	TextureLoader m_scenarioName;
	
	int m_focusSlotNo;
	bool m_showDialog;
	DIALOG_TYPE m_dialogType;
	bool m_jumpSenerio;
	bool m_jumpTitle;
	bool m_keyPush;
	bool m_mainpartMode;
	


}