module yamalib.draw.scenariothumbnail;

private import y4d_aux.filesys;

private import y4d_draw.drawbase;
private import y4d_draw.screen;
private import y4d_draw.surface;
private import y4d_draw.texture;
private import y4d_draw.surfaceloader;
private import y4d_draw.scenariodraw;
private import y4d_aux.lineparser;

private import yamalib.counterfsp;
private import yamalib.log.log;

/** サムネールデータクラス */
class ThumbnailInfo {
public:
	/// 文字列と番号のマップ
	static void setCharMap(char[] filename) {
		ubyte[] memory = cast(ubyte[])FileSys.read(filename);
		std.stream.MemoryStream m = new std.stream.MemoryStream(memory);
		int count = 0;
		auto lineParser = new LineParser();
		while(!m.eof) {
			lineParser.setLine(cast(char[]) m.readLine());
			// filename はすてる
			lineParser.getStr();
			// charID
			char[] str = lineParser.getStr();
			if ( str is null ) continue;
			charMap[str] = count++;
		}
		
		m.close();
	}

	int bgNo() {
		return m_bgNo;
	}
	int bgNo(int value) {
		return m_bgNo = value;
	}
	
	char[] charaLeft() {
		return m_charaLeft;
	}
	char[] charaLeft(char[] value) {
		return m_charaLeft = value;
	}
	int charaLeftNo() {
		if ( m_charaLeft is null || !(m_charaLeft in charMap) ) {
			return -1;
		}
		return charMap[m_charaLeft];
	}

	char[] charaCenter() {
		return m_charaCenter;
	}
	char[] charaCenter(char[] value) {
		return m_charaCenter = value;
	}
	int charaCenterNo() {
		if ( m_charaCenter is null || !(m_charaCenter in charMap) ) {
			return -1;
		}
		return charMap[m_charaCenter];
	}

	char[] charaRight() {
		return m_charaRight;
	}
	char[] charaRight(char[] value) {
		return m_charaRight = value;
	}
	int charaRightNo() {
		if ( m_charaRight is null || !(m_charaRight in charMap) ) {
			return -1;
		}
		return charMap[m_charaRight];
	}

	int bgmNo() {
		return m_bgmNo;
	}
	int bgmNo(int value) {
		return m_bgmNo = value;
	}
	
	int bgmLoop() {
		return m_bgmLoop;
	}
	int bgmLoop(int value) {
		return m_bgmLoop = value;
	}

	int seNo() {
		return m_seNo;
	}
	int seNo(int value) {
		return m_seNo = value;
	}
	
	int seLoop() {
		return m_seLoop;
	}
	int seLoop(int value) {
		return m_seLoop = value;
	}
	
	wchar[] text() {
		return m_text;
	}
	wchar[] text(wchar[] value) {
		return m_text = value;
	}
	
	int textOffset() {
		return m_textOffset;
	}
	int textOffset(int value) {
		return m_textOffset = value;
	}

	int pageNo() {
		return m_pageNo;
	}
	int pageNo(int value) {
		return m_pageNo = value;
	}


private:
	// キャラクター番号とキャラクターＩＤ文字列との連想記憶
	static int[char[]] charMap;		
	int m_bgNo;
	char[] m_charaLeft;
	char[] m_charaCenter;
	char[] m_charaRight;
	int m_bgmNo;
	int m_bgmLoop;
	int m_seNo;
	int m_seLoop;
	wchar[] m_text;
	int m_textOffset;
	int m_pageNo;
}
	
/** サムネール情報よりサムネールイメージを作成する */
public class ThumbnailCreator {
	/// コンストラクタ
	this(SurfaceLoader bgLoader_, SurfaceLoader charaLoader_, ScenarioTextDrawSurface scenarioDraw) {
		bgLoader = bgLoader_;
		charaLoader = charaLoader_;
		m_scenarioDraw = scenarioDraw;
	}
	
	/// 論理画面サイズを取得(キャラクタープリセット位置算出に使用)
	int viewWidth() {
		return m_viewWidth;
	}
	/// 論理画面サイズを設定(キャラクタープリセット位置算出に使用)
	int viewWidth(int value) {
		return m_viewWidth = value;
	}
	
	Surface create(int width, int height, ThumbnailInfo thumbInfo) {
		assert(bgLoader !is null);
		assert(charaLoader !is null);
		assert(thumbInfo !is null);
		auto surface = new Surface();
		surface.createDIB(width, height, true);

		// 背景描画
//printf("thumb bg no %d\n", thumbInfo.bgNo);
		if (0 <= thumbInfo.bgNo) {
			surface.blt(bgLoader.get(thumbInfo.bgNo), 0,0);
		}

		// キャラクター
		int chImgNo = thumbInfo.charaLeftNo;
		if (-1 != chImgNo) {
//printf("chara left %d\n",thumbInfo.charaLeftNo);
			auto charaImg = charaLoader.get(chImgNo);
			surface.bltSrcAlpha(charaImg, 
				getCharaPresetPos(width,0,charaImg.getWidth()), 0);
		}

		chImgNo = thumbInfo.charaCenterNo;
		if (-1 != chImgNo) {
//printf("chara center %d\n",thumbInfo.charaCenterNo);
			auto charaImg = charaLoader.get(chImgNo);
			surface.bltSrcAlpha(charaImg, 
				getCharaPresetPos(width,1,charaImg.getWidth()), 0);
		}
		
		chImgNo = thumbInfo.charaRightNo;
		if (-1 != chImgNo) {
//printf("chara right %d\n",thumbInfo.charaRightNo);
			auto charaImg = charaLoader.get(chImgNo);
			surface.bltSrcAlpha(charaImg, 
				getCharaPresetPos(width,2,charaImg.getWidth()), 0);
		}
		
		// 文字
		m_scenarioDraw.setText(thumbInfo.text);
		m_scenarioDraw.updateText();
		m_scenarioDraw.onDraw(surface, 45, 28);
			
		return surface;
	}
private:

	/// キャラクターのプリセット位置を取得する	
	int getCharaPresetPos(int screenWidth, int presetNumber, int textureWidth) {
		int halfScreenWidth = screenWidth / 2;
		switch (presetNumber) {
			case 0: 
				return (halfScreenWidth - (m_viewWidth / 4)) - (textureWidth / 2);
			case 1: 
				return halfScreenWidth - (textureWidth / 2);
			case 2:
				return (halfScreenWidth + (m_viewWidth / 4)) - (textureWidth / 2);
			default:
				Log.printError("ThumbnailCreator#getCharaPresetPos : undefined pos :%s", presetNumber);
				assert(false);
		}
	}

	SurfaceLoader bgLoader;
	SurfaceLoader charaLoader; 
	ScenarioTextDrawSurface m_scenarioDraw;
	
	int m_viewWidth = 640;	//default
	
}

/** サムネール表示クラス */
public class ThumbnailDraw {

	ThumbnailCreator thumbnailCreator() {
		return m_thumbnailCreator;
	}
	ThumbnailCreator thumbnailCreator(ThumbnailCreator value) {
		return m_thumbnailCreator = value;
	}

	ThumbnailInfo[] thumbnailInfos() {
		return m_thumbnailInfos;
	}
	ThumbnailInfo[] thumbnailInfos(ThumbnailInfo[] value) {
		return m_thumbnailInfos = value;
	}
	
	/// 現在表示中のサムネール情報 (frontを先頭に)
	ThumbnailInfo[] getActiveInfo() {
		return activeInfo;
	}
	
	/// 先頭のサムネール番号を設定する
	void setThumbnailNo(int value) {
		if (value < 0) {
			value = 0;
		}
		if (value < m_thumbnailInfos.length) {
			infoIndex = value;
		}
		foreach(inout t;textures) {
			if (t !is null) {
				t.release();
				t = null;
			}
		}
		textures.length = 0;
		activeInfo.length = 0;
		fillPaneTexture();
	}
	int getThumbnailNo() {
		return infoIndex;
	}
	
	/// インデックスの制限値を指定できる (default int.max)
	void setThumbnailLimitNo(int value) {
		infoIndexLimit = value;
	}
	int getThumbnailLimitNo() {
		return infoIndexLimit;
	}
	
	/// フェードインする
	void fadeIn(int speed, int delay) {
		if (selecting || fadeInFlg || fadeOutFlg || moving) {
			return;
		}
		
		m_fadeCounters.length = 0;
		for(int i = 0; i < textures.length; ++i) {
			auto counter = new RootCounterS();
			counter.set(-((textures.length - 1 - i) * speed * delay), 255, speed);
			m_fadeCounters ~= counter;
		}
		
		fadeInFlg = true;
	}
	/// フェードアウトする
	void fadeOut(int speed, int delay) {
		if (selecting || fadeInFlg || fadeOutFlg || moving) {
			return;
		}
		m_fadeCounters.length = 0;
		for(int i = 0; i < textures.length; ++i) {
			auto counter = new RootCounterS();
			counter.set(255 + (i * speed * delay), 0, speed);
			m_fadeCounters ~= counter;
		}
		fadeOutFlg = true;
	}
	
	bool isFadeIn() {
		return fadeInFlg;
	}
	bool isFadeOut() {
		return fadeOutFlg;
	}
	
	
	// 進む
	void next() {
		if (selecting || fadeInFlg || fadeOutFlg || moving) {
			return;
		}
		if (textures is null) {
			return;
		}
		if (textures.length >= 2 && textures[1] is null) {
			// 最後に到達しているので無理
			return;
		}
		
		fadeInTexture = createNextTexture();

		fadeOutTexture = textures[0];
		
		moving = true;
		foreach(painCounters; slideCounters) {
			foreach (counter; painCounters) {
				counter.reset();
			}
		}
		fadeInAlpha.reset();
		fadeOutAlpha.reset();
	}
	
	// 戻る
	void prev() {
	}
	
	void selectPane(int paneNo, int speed) {
		if (moving) {
			return;	
		}
		if (0 > paneNo || textures.length <= paneNo) {
			// 引数エラー
			return;
		}

		if (textures[paneNo] !is null) {
			selectedPane = paneNo;
			auto targetTexture = textures[paneNo];
			selectPaneCounter[0].set(cast(int) DRAW_POINT[paneNo][0].x, 0, speed);
			selectPaneCounter[1].set(cast(int) DRAW_POINT[paneNo][0].y, 0, speed);
			selectPaneCounter[2].set(cast(int) DRAW_POINT[paneNo][1].x, cast(int) targetTexture.getWidth(), speed);
			selectPaneCounter[3].set(cast(int) DRAW_POINT[paneNo][1].y, 0, speed);
			selectPaneCounter[4].set(cast(int) DRAW_POINT[paneNo][2].x, cast(int) targetTexture.getWidth(), speed);
			selectPaneCounter[5].set(cast(int) DRAW_POINT[paneNo][2].y, cast(int) targetTexture.getHeight(), speed);
			selectPaneCounter[6].set(cast(int) DRAW_POINT[paneNo][3].x, 0, speed);
			selectPaneCounter[7].set(cast(int) DRAW_POINT[paneNo][3].y, cast(int) targetTexture.getHeight(), speed);
			selecting = true;
			endSlecting = false;
		}
	}
	
	/// 選択アクションが完了したかどうか
	bool isFinishSelect() {
		return endSlecting;
	}
	
	/// 選択状態をキャンセルする
	void resetSelect() {
		selecting = false;
		endSlecting = false;
	}
	
	
	/// 動作処理
	void onMove(Screen screen) {
		if (textures is null) {
			fillPaneTexture();
		}
		if (moving) {
			bool endFlg = false;
			foreach(painCounters; slideCounters) {
				foreach (counter; painCounters) {
					assert(counter !is null);
					counter.inc();
					endFlg = counter.isEnd();
				}
			}
			fadeInAlpha.inc();
			fadeOutAlpha.inc();
			
			// 移動終了
			if (endFlg) {
				moving = false;
				// 先頭を削除
				textures = textures[1..length];
				activeInfo = activeInfo[1..length];
				// 後方追加
				textures ~= fadeInTexture;
				activeInfo ~= m_thumbnailInfos[infoIndex-1];
				fadeOutTexture.release();
				fadeOutTexture = null;
			}
		} else if (selecting) {
			foreach(counter; selectPaneCounter) {
				counter.inc();
				endSlecting = counter.isEnd();
			}
		} else if (fadeInFlg || fadeOutFlg) {
			bool fadeEnd = true;
			foreach(counter; m_fadeCounters) {
				counter.inc();
				if (!counter.isEnd()) {
					fadeEnd = false;
				}
			}
			if (fadeEnd) {
				fadeInFlg = fadeOutFlg = false;
			}
		}
	}
	
	/// 描画処理
	void onDraw(Screen screen) {
		if (moving) {
			Color4ub colorOrg = screen.getColor4ub();
			// フェードインテクスチャ
			screen.setColor(255,255,255,fadeInAlpha.get());
			screen.blt(fadeInTexture, null, DRAW_POINT[textures.length - 1]);
			
			screen.setColor(255,255,255,255);
			Point[4] points;
			for (int i = textures.length - 1; i > 0; --i) {
				points[0].x = slideCounters[i][0].get();
				points[0].y = slideCounters[i][1].get();
				points[1].x = slideCounters[i][2].get();
				points[1].y = slideCounters[i][3].get();
				points[2].x = slideCounters[i][4].get();
				points[2].y = slideCounters[i][5].get();
				points[3].x = slideCounters[i][6].get();
				points[3].y = slideCounters[i][7].get();
				
				screen.blt(textures[i], null, points);
			}
			
			// フェードアウトテクスチャ
			screen.setColor(255,255,255,fadeOutAlpha.get());
			screen.blt(fadeOutTexture, null, DRAW_POINT[0]);

			screen.setColor(colorOrg);
		} else if (selecting) {
			// 選択
			Point[4] points;
			for (int i = textures.length - 1; i >= 0; --i) {
				if (i != selectedPane) {
					screen.blt(textures[i], null, DRAW_POINT[i]);
				} else {
					// 選択したペイン
					points[0].x = selectPaneCounter[0].get();
					points[0].y = selectPaneCounter[1].get();
					points[1].x = selectPaneCounter[2].get();
					points[1].y = selectPaneCounter[3].get();
					points[2].x = selectPaneCounter[4].get();
					points[2].y = selectPaneCounter[5].get();
					points[3].x = selectPaneCounter[6].get();
					points[3].y = selectPaneCounter[7].get();
					
					screen.blt(textures[i], null, points);
				}
			}
			
		} else if (fadeInFlg || fadeOutFlg) {
			Color4ub colorOrg = screen.getColor4ub();
			for (int i = textures.length - 1; i >= 0; --i) {
				auto alpha = m_fadeCounters[i].get();
				if (alpha < 0) {
					alpha = 0;
				} else if (alpha > 255) {
					alpha = 255;
				}
				screen.setColor(255,255,255,alpha);
				screen.blt(textures[i], null, DRAW_POINT[i]);
			}
			screen.setColor(colorOrg);
			
		} else {
			for (int i = textures.length - 1; i >= 0; --i) {
				screen.blt(textures[i], null, DRAW_POINT[i]);
			}
		}
	}
	
	
	/// コンストラクタ
	this(int screenWidth_, int screenHeight_) {
		// ペインを選択したときに移動変数
		for(int i = 0; i < FADE_OUT_POINT.length; ++i) {
			selectPaneCounter ~= new InteriorCounter();
			selectPaneCounter ~= new InteriorCounter();
		}
		screenWidth = screenWidth_;
		screenHeight = screenHeight_;
	}
	
	/// staticイニシャライザ
	static this() {
		int speed = 40;
		Point[4][] allPoint;
		allPoint ~= FADE_OUT_POINT;
		foreach (pts;DRAW_POINT) {
			allPoint ~= pts;
		}
		
		for (int i = 0; i < allPoint.length - 1; ++i) {
			InteriorCounter[] counters;
			for(int j = 0; j < allPoint[i].length; ++j) {
				{
					auto counter = new InteriorCounter();
					counter.set(cast(int) (allPoint[i+1][j].x), 
						cast(int) (allPoint[i][j].x), speed);
					counters ~= counter;
				}
				{
					auto counter = new InteriorCounter();
					counter.set(cast(int) allPoint[i+1][j].y, 
						cast(int) (allPoint[i][j].y), speed);
					counters ~= counter;
				}
			}
			slideCounters ~= counters;
		}
		
		fadeInAlpha = new InteriorCounter();
		fadeInAlpha.set(0, 255, speed);
		fadeOutAlpha = new InteriorCounter();
		fadeOutAlpha.set(255, 0, speed);
		
	}
	
private:
	static const Point[4] FADE_OUT_POINT = [
		{x:412,  y:280},	{x:600, y:266},	{x:599, y:423},	{x:416,y:452},
	];
	static const Point[4] FADE_IN_POINT = [
		{x: 30,  y: 99},	{x:162, y:101},	{x:166, y:198},	{x: 40,y:200}
	];

	/// シーセンス図のパーツの描画位置
	static const Point[4][] DRAW_POINT = [
		[{x:412,  y:280},	{x:600, y:266},	{x:599, y:423},	{x:416,y:452}],
		[{x:304,  y:214},	{x:483, y:206},	{x:485, y:351},	{x:311,y:370}],
		[{x:219,  y:168},	{x:389, y:165},	{x:394, y:299},	{x:228,y:310}],
		[{x:154,  y:137},	{x:314, y:137},	{x:319, y:259},	{x:164,y:265}],
		[{x:104,  y:117},	{x:252, y:118},	{x:256, y:233},	{x:113,y:235}],
		[{x: 62,  y:105},	{x:202, y:107},	{x:203, y:210},	{x: 72,y:213}],
		[{x: 30,  y: 99},	{x:162, y:101},	{x:166, y:198},	{x: 40,y:200}]
	];
	
	static InteriorCounter[][] slideCounters; 
	static InteriorCounter fadeInAlpha;
	static InteriorCounter fadeOutAlpha;
	
	InteriorCounter[] selectPaneCounter;
	
	void fillPaneTexture() {
		while(textures.length < maxPain) {
			textures ~= createNextTexture();
			activeInfo ~= m_thumbnailInfos[infoIndex-1];
		}
	}
	
	Texture createNextTexture() {
		if (infoIndex >= m_thumbnailInfos.length || infoIndex >= infoIndexLimit) {
			return null;
		}
		auto surface = m_thumbnailCreator.create(screenWidth, screenHeight, m_thumbnailInfos[infoIndex]);
		auto texture = new Texture();
		texture.setSurface(surface);
		++infoIndex;
		return texture;
	}
	
	final int screenWidth;
	final int screenHeight;

	// サムネール構築クラス
	ThumbnailCreator m_thumbnailCreator;
	ThumbnailInfo[] m_thumbnailInfos;
	// 表示数
	int maxPain = 7;
	int infoIndex;

	int infoIndexLimit = 10;
//	int infoIndexLimit = int.max;
	// 
	bool moving = false;
	int selectedPane = 0;
	bool selecting = false;
	bool endSlecting = false;
	bool fadeInFlg = false;
	bool fadeOutFlg = false;
	ICounter[] m_fadeCounters;
	
	Texture[] textures;
	ThumbnailInfo[] activeInfo;
	Texture fadeInTexture;
	Texture fadeOutTexture;
	
}