module kyojintati4d.bookmark;

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

private import y4d;
private import y4d_aux.filesys;
private import y4d_draw.scenariodraw;
private import y4d_aux.lineparser;
private import y4d_aux.widestring;
private import ytl.vector;

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


/// シナリオデータの情報クラス
public class ScenarioInfo : Archive {
	
	int storyNo;
	int pos;			//!< シナリオファイル内の開始バッファポジション
	int textPos;		//!< 頁内の位置
	int analyzedTag;	//!< 頁内解析済みタグ数
	int page;
	int bgNo;
	int bgNoOld;
	int bgmNo = -1;
	int bgmVol;
	bool bgmLoop;
	int seNo = -1;
	int seVol;
	int seLoop;
	vector!(int) charpos;		//!< 立ち位置
	vector!(int) preset_pos;
	vector!(char[]) strCharId;
	vector!(char[]) vCharColor;
	
	invariant {
		assert( seVol <= 128 );
	}

	/// このクラスの文字列表現
	override char[] toString() {
		char[] buf;
		buf ~= "{" ~ "SEVE_DATA_VERSION" ~ "=" ~ std.string.toString(dataVersion) ~ "}";
		buf ~= "{" ~ "storyNo" ~ "=" ~ std.string.toString(storyNo) ~ "}";
		buf ~= "{" ~ "pos" ~ "=" ~ std.string.toString(pos) ~ "}";
		buf ~= "{" ~ "analyzedTag" ~ "=" ~ std.string.toString(analyzedTag) ~ "}";
		buf ~= "{" ~ "page" ~ "=" ~ std.string.toString(page) ~ "}";
		buf ~= "{" ~ "bgNo" ~ "=" ~ std.string.toString(bgNo) ~ "}";
		buf ~= "{" ~ "bgNoOld" ~ "=" ~ std.string.toString(bgNoOld) ~ "}";
		buf ~= "{" ~ "bgmNo" ~ "=" ~ std.string.toString(bgmNo) ~ "}";
		buf ~= "{" ~ "bgmVol" ~ "=" ~ std.string.toString(bgmVol) ~ "}";
		buf ~= "{" ~ "bgmLoop" ~ "=" ~ std.string.toString(bgmLoop) ~ "}";
		buf ~= "{" ~ "seNo" ~ "=" ~ std.string.toString(seNo) ~ "}";
		buf ~= "{" ~ "seVol" ~ "=" ~ std.string.toString(seVol) ~ "}";
		buf ~= "{" ~ "seLoop" ~ "=" ~ std.string.toString(seLoop) ~ "}";
		buf ~= "{" ~ "charpos" ~ "=" ~ charpos.toString() ~ "}";
		buf ~= "{" ~ "preset_pos" ~ "=" ~ preset_pos.toString() ~ "}";
		buf ~= "{" ~ "strCharId" ~ "=" ~ strCharId.toString() ~ "}";
		
		return buf;
	}

	/// シリアライズ
	override void serialize(inout Serialize s) {
		// 何にせよ、ここで最新バージョンにしておく
		dataVersion = SEVE_DATA_VERSION_LATEST;
		// このデータのバージョンを付与して、拡張性に備える
		s << dataVersion;
		if (SEVE_DATA_VERSION_LATEST == dataVersion ) {
			s << storyNo << pos << textPos << analyzedTag << page << bgNo << bgNoOld
				<< bgmNo << bgmVol<< bgmLoop << seNo << seVol << seLoop
				<< charpos << preset_pos << strCharId << vCharColor;
		} else if (SEVE_DATA_VERSION_LATEST > dataVersion ) {
			// 下位互換
			// ここで、それぞれの下位バージョンを判定し、追加されたデータにデフォルト値を設定
			Log.printError("Can't load this save data version : %s", dataVersion);
		} else if (SEVE_DATA_VERSION_LATEST < dataVersion ) {
			// 上位互換
			// たぶん、これは、おもわぬ動作を引き起こすので、バージョンアップを促す
			Log.printError("Can't load this save data version : %s", dataVersion);
		}
		Log.print("ScenarioInfo serialized");
	}

	/// コンストラクタ
	this() {
		charpos = new vector!(int);
		charpos.resize(4);
		preset_pos = new vector!(int);
		preset_pos.resize(4);
		strCharId = new vector!(char[]);
		strCharId.resize(4);
		vCharColor = new vector!(char[]);
		vCharColor.resize(4);
	}
private:
	static final const int SEVE_DATA_VERSION_LATEST = 100;
	/** 評価版リリース時のセーブバージョン */
	static final const int SEVE_DATA_VERSION100 = 100;
	static int dataVersion = SEVE_DATA_VERSION_LATEST;
}

/** 現在対象となっている栞の情報 */
class CurrentInfo {
	ScenarioInfo si;	//!< シナリオ情報
	wchar[] text;		//!< 栞表示用
	bool start=false;	//!< 先頭からか読むか
	
	Rect rc;			//!< 選択した画像の表示矩形（自由に使って）
}

/// １枚の栞を表すクラス
/**
   これはセーブデータとして保存するので<BR>
   Archive クラスを継承する
 */
class Bookmark : Archive {
	enum sioriPos { USE=0,UNUSE=120,AUTO=60 };
	enum ButtonID { READ,OVERWRITE,DELETE,INSERT,AUTO};

	/// フォントリポジトリを返す
	static FontRepository getFontRepository() { return fr; }
	
	/// フォントリポジトリを設定する
	static void setFontRepository(FontRepository fr_) 
	in 
	{
		assert( !(fr_ is null) );
	}
	body
	{ 
		fr = fr_; 
	}
	
	/// 現在のシナリオ情報の設定
	static void setCurrentInfo(CurrentInfo info) {
		curInfo = info;
	}
	
	/// 現在のシナリオ情報、もしくは選択栞の情報
	static CurrentInfo getCurrentInfo() {
		return selectedInfo;
	}

	/// 現在のシナリオ情報、もしくは選択栞の情報
	static void removeCurrentInfo() {
		selectedInfo = null;
	}

	/// タイトル画面かどうかを指定する
	static void setTitle(bool b) { title = b; }
	static bool getTitle()		 { return title; }
	
	/// このクラスの文字列表現を返却する
	override char[] toString() {
		char[] buf = super.toString() ~ "[";
		
		buf ~= "{" ~ "scenarioInfo" ~ "=" ~ scenarioInfo.toString() ~ "]"; 
		buf ~= "{" ~ "readOnly" ~ "=" ~ std.string.toString(readOnly) ~ "]"; 
		buf ~= "{" ~ "read" ~ "=" ~ std.string.toString(read) ~ "]"; 
		buf ~= "{" ~ "overwrite" ~ "=" ~ std.string.toString(overwrite) ~ "]"; 
		buf ~= "{" ~ "insert" ~ "=" ~ std.string.toString(insert) ~ "]"; 
		buf ~= "{" ~ "init" ~ "=" ~ std.string.toString(init) ~ "]"; 
		buf ~= "{" ~ "strDate" ~ "=" ~ strDate ~ "]"; 
		buf ~= "{" ~ "text" ~ "=" ~ std.utf.toUTF8(text) ~ "]"; 
		buf ~= "{" ~ "autoMark" ~ "=" ~ std.string.toString(autoMark) ~ "]"; 
		buf ~= "{" ~ "use" ~ "=" ~ std.string.toString(use) ~ "]"; 
		
//printf("#############INDEX TEXT [%*s]", toMBS(text) );

		return buf ~ "]";
	}
	
	/// 初期化
	void onInit() {
		setText( text.dup );
	}

	/// テキストの取得
	wchar[] getText() {
		return text;
	}
	
	/// 表示テキストの設定
	void setText(wchar[] ww) {
		text = ww;
//printf("%*s\n", toMBS(ww));
	}

	/// シナリオ情報のセット
	void setScenarioInfo(ScenarioInfo info) {
		scenarioInfo = info;
		// 日付を生成
		createDate();
		setUse(true);
	}

	/// シナリオ情報の取得
	ScenarioInfo getScenarioInfo() { return scenarioInfo; }
	
	/// セーブ日付文字列の取得
	char[] getDateString() {
		return strDate;
	}
	
	/// この栞を使用しているか
	void setUse(bool b) {
		if (use != b) {
			if (!b) {
				text = null;
				strDate = null;
			}
		}
		use = b;
	}
	
	/// 栞を読み取り専用に設定してセーブできなくする
	void setReadOnly(bool b) {
		readOnly = b;
	}

	/// 読み取り専用か？
	bool isReadOnly() {
		return readOnly;
	}

	/// このしおりを使っているか
	bool isUse() { return use; }
	/// このしおりから読むか
	bool getRead() { return read; }
	/// このシナリオのはじめから読むか
	bool getOverwrite() { return overwrite; }
	/// このしおりを挟むか
	bool getInsert() { return insert; }
	/// ボタンフラグの初期化
	void resetBtFlag() {
		read = overwrite = insert = false;
	}
	/// バグ対策
	/// 理由は不明だが、テキストが初期段階では表示できない。
	/// 使用する度にアップデートして、テクスチャを再生成すればとりあえず、表示はできる
	void updateText() {
		setText( text.dup );
	}

	/// オートセーブ栞に設定
	void setAuto() {
		autoMark = true;
//		use = true;
	}

	/// オートセーブ栞か
	bool isAuto() {
		return autoMark;
	}
	
	/// セーブする
	void doSave() {
		assert(!(curInfo is null));
		assert(!(curInfo.si is null));
		setText( curInfo.text );
		setUse(true);
		setScenarioInfo( curInfo.si );
		Log.print("Click SAVE");
		
	}
	/// ロードする
	void doLoad() {
		read = true;
		selectedInfo = new CurrentInfo();
		selectedInfo.si = getScenarioInfo();
		Log.print("Click READ");
	}
	/// 削除する
	void doRemove() {
		setUse(false);
		Log.print("Click REMOVED");
	}

	/// コンストラクタ
	this() {
		scenarioInfo = new ScenarioInfo();
	}
	
	/// シリアライズ
	override void serialize(inout Serialize s) 
		in
		{
			assert( !(s is null) );
		}
		body
		{
			s << text << strDate << use;
			scenarioInfo.serialize(s);
	
			Log.print("Bookmark serialized");
//			Log.print("BOOKMARK TEXT DUMP : %s", text);
//			Log.print("BOOKMARK SAVED DATE : %s", strDate);
	
			// 復元作業
			if (!s.isStoring()) {
				if (0 != text.length) {
					setText( text.dup );
				}
			}
		}

protected:
	static FontRepository fr;
	
	static CurrentInfo curInfo;			//!< 現在のシナリオ情報
	static CurrentInfo selectedInfo;	//!< 現在のシナリオ情報
	static bool title;					//!< タイトル画面か

	ScenarioInfo scenarioInfo;

	bool readOnly;	//!< 読み込み専用

	bool read;		//!< この栞から読む
	bool overwrite;	//!< 上書きか
	bool insert;	//!< この栞をはさむ

	bool init;
	wchar[] text;
	char[] strDate;
	bool autoMark;
	bool use;

	// 日付を生成する
	void createDate() {
		DateTime dt = DateUtil.getDateTime();
		strDate = DateUtil.format(dt, "yyyy/MM/dd   hh:mm:ss");
	}
	

private:
	static final const int FONTSIZE = 20;
	static final const int TEXTWIDTH = 340;
	static final const int MAX_TEXT_LINE = 7;
	static final const int MAX_TEXT_SIZE = 60;
	static final const int[] textureNo = [0,2,2,2,4,6,8,10,12,14,14,14,16,18,20,22,24,26];
}
