module kyojintati4d.component.komaview;

private import std.utf;

private import y4d_aux.widestring;
private import y4d_draw.screen;
private import y4d_draw.drawbase;
private import y4d_draw.texture;
private import y4d_draw.fontrepository;
private import y4d_draw.scenariodraw;
private import y4d_draw.transbltter;
private import y4d_timer.fixtimer;

private import kyojintati4d.component.komadata;

private import yamalib.draw.textdraw;
private import yamalib.counter;
private import yamalib.log.log;

/// 四コマのビュークラス
class KomaView {
	
	/// 指定されているコマデータを読み込みます 
	bool loadKoma() {
		try {
			this.m_frame = m_data.getFrameData()[this.m_curFrame];
			KomaObject[] komaObjs = m_data.getKomaObjects();
			// 再生フラグの初期化
			m_playAct = new bool[this.m_frame.getActions().length];
			
			foreach (int i, inout FrameAction act; m_frame.getActions() ) {
				m_playEffect ~= new bool[act.getEffects().length];
			}
			
			Texture tex;
			ViewObject vo;
			
			// コマオブジェクトの具現化
			foreach (inout KomaObject obj; komaObjs) {
				tex = new Texture();
				vo = new ViewObject(obj.x, obj.y, obj.alpha, obj.rote, obj.size );
				tex.load(m_subjectPath ~ obj.filename);
				vo.texture = tex;
				
				m_viewObjMap[obj.id] = vo;
			}
			
			TextData[] textData = m_data.getTextData();
			ViewTextObject vt;
			// テキストオブジェクトの生成
			foreach(inout TextData td; textData) {
				vt = new ViewTextObject(td.x, td.y, td.speed);
				
				m_tdc.setBaseFontSize(td.size);

				// 正規化
				char[] text = std.string.replace(td.text,"[", "<");	
				text = std.string.replace(text,"]", ">");			
				m_tdc.setText( .toUTF16(text) );
				
				m_sd.setTextDrawContext(m_tdc);
				m_sd.setTextOffset(0);	// バッファポジション
				m_sd.updateText();
				vt.setDrawChar( m_sd.getDrawChar() );
				vt.setTag( m_sd.getUnknownTagInfo() );
				
				m_viewTextMap[td.id] = vt;
			}
			
			return true;
		} catch (Exception e) {
			Log.printFatal("Exception %s#loadKoma : [%s][%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}
	
	/// 四コマの動作
	int onMove(Screen screen) {
		try {
			// タイマ更新
			m_timer.update();
			
			// 再生中の処理
			if (m_play) {
			
				FrameAction[] actions = this.m_frame.getActions();
				foreach(int i,inout FrameAction act; actions) {
			
					if ( m_playAct[i] ) {
						// 動作中のアクションの制御
						if (act.getId() in m_viewObjMap) {
							m_viewObjMap[act.getId()].onMove(screen);
						} else if (act.getId() in m_viewTextMap) {
							m_viewTextMap[act.getId()].onMove(screen);
						}
					
					} else {
						// 未動作のアクションの開始制御
						if (m_timer.get() >= act.getTime()) {
							// 開始する
							m_playAct[i] = true;
							
							// 対象ソースに識別子
							char[] srcId = act.getId();
							Log.print("PALY ACT %s",i);

							if ( srcId is null ) {
								// 無効Action警告
								Log.printWarn("%s#onMove ACTION no has id");
								
							} else {
								// 定義されているIDか
								
								if ( srcId in m_viewObjMap ) {
									if ( FrameAction.INVALID_VAL != act.getX() ) {
										m_viewObjMap[srcId].x = act.getX();
									}
									if ( FrameAction.INVALID_VAL != act.getY() ) {
										m_viewObjMap[srcId].y = act.getY();
									}
									if ( FrameAction.INVALID_VAL != act.getAlpha() ) {
										m_viewObjMap[srcId].alpha = act.getAlpha();
									}
									m_viewObjMap[srcId].show = act.isShow();
									
									if ( !(act.getTransData() is null) ) {
										m_backup = screen.backup();
										m_transPhase.set(255,0, act.getTransData().speed);
										m_transType = act.getTransData().type;
										m_trans = true;
										Log.print("TRANS");
									}
									
								} else if ( srcId in m_viewTextMap ) {
									Log.print("%s#onMove ACTION TEXT_OBJ id : %s", toString(), act.getId());
								} else {
									// 定義されていない対象オブジェクト
									Log.printWarn("%s#onMove ACTION unknown id : %s", toString(), act.getId());
								}
							}
						}
					} 
				}
				
				if (m_trans && m_transPhase.isEnd()) {
					m_trans = false;
				} else {
					m_transPhase++;
				}
			}
			
			return 0;
		} catch (Exception e) {
			Log.printFatal("Exception %s#onMove : [%s][%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}

	/// 四コマの描画処理
	int onDraw(Screen screen) {
		try {
			// 再生中の処理
			FrameAction[] actions = this.m_frame.getActions();
			foreach(int i,inout FrameAction act; actions) {
				if ( m_playAct[i] ) {
					// 動作中のアクションの制御
					if (act.getId() in m_viewObjMap) {
						m_viewObjMap[act.getId()].onDraw(screen);
					} else if (act.getId() in m_viewTextMap) {
						m_viewTextMap[act.getId()].onDraw(screen);
					}
				}
			}
			
			if (m_trans) {
				TransBltter.blt(m_transType, screen, m_backup, 0, 0, m_transPhase.get());
			}

			return 0;
		} catch (Exception e) {
			Log.printFatal("Exception %s#onMove : [%s][%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}
	
	/// このデータを再生する
	bool play() {
		if ( m_play ) {
			return true;
		}

		// TODO
		m_timer.reset();
		m_play = true;
		
		return true;
	}
	
	/// このデータ再生を終了する
	bool stop() {
		if ( !m_play ) {
			return true;
		}
		
		// TODO
		m_play = false;
		
		return true;
	}
	
	/// 次のコマに移動する
	bool nextKoma() {
		
		// 読み込んでいなければ終了
		if (m_data is null) {
			return false;
		}
		if (m_data.getFrameData() is null) {
			return false;
		}
		
		if ( m_curFrame >= m_data.getFrameData().length-1 ) {
			return false;
		}
		
		++m_curFrame;
		
		return loadKoma();
	}
	
	/// 前のコマに移動する
	bool prevKoma() {
		// 読み込んでいなければ終了
		if (m_data is null) {
			return false;
		}
		if (m_data.getFrameData() is null) {
			return false;
		}

		if ( m_curFrame <= 0 ) {
			return false;
		}
		
		--m_curFrame;
		
		return loadKoma();
	}
	
	/// ScenarnioDrawの初期化
	void initScenarnioDraw(FontLoader fl_, int no_) {
		m_sd = new ScenarioTextDraw();
		m_tdc = new TextDrawContext();
		FontRepository fr = m_sd.getFontRepository();
		fr.setLoader(fl_, no_);
		fr.setMax(300);
	}

	/// 四コマデータを設定する
	void setData(KomaData data) {
		m_data = data;
	}
	
	/// 状態の初期化
	void reset() {
		stop();
		
		m_curFrame = 0;
	}
	
	/// コンストラクタ
	this (char[] sbjPath) 
	in
	{
		assert( !(sbjPath is null) );
	}
	body
	{
		m_timer = new FixTimer();
		if (sbjPath[length-1] != '\\' || sbjPath[length-1] != '/') {
			sbjPath ~= "\\";
		} 
		m_subjectPath = sbjPath;
		m_transPhase = new RootCounterS();
	}
	
	
private:

	static final float SCALE_RATE = 100.0f;

	
	/// 基底クラス
	static abstract class ViewOjbectBase {
		bool show() {
			return m_show;
		}
		bool show(bool b) {
			return m_show = b;
		}
		
	protected:
		bool m_show = true;
	}

	/**
		描画するオブジェクトのクラス
	*/
	static class ViewObject : ViewOjbectBase {
		
		int x() {
			return m_offsetx.get();
		}
		int x(int x_) {
			defaultCounter(m_offsetx, x_);
			return x_;
		}

		int y() {
			return m_offsety.get();
		}
		int y(int y_) {
			defaultCounter(m_offsety, y_);
			return y_;
		}
	
		int alpha() {
			return m_alphaCnt.get();
		}
		int alpha(int alpha_) {
			defaultCounter(m_alphaCnt, alpha_);
			return alpha_;
		}
		
		int rote() {
			return m_rote;
		}
		int rote(int rote_) {
			return m_rote = rote_;
		}
		
		float rate() {
			return m_rate;
		} 
		float rate(float rate_) {
			return m_rate = rate_;
		}
		
		Texture texture() {
			return m_texture;
		}
		
		Texture texture(Texture texture_) {
			return m_texture = texture_;
		}
		
		/// エフェクトデータのセット
		void setEffectData(FrameEffectData effect) {
			clearEffect();
			addEffectData(effect);
		}
		/// エフェクトデータの追加
		void addEffectData(FrameEffectData effect) {
			m_effects ~= effect;
		}
		/// エフェクトデータの全クリア
		void clearEffect() {
			m_effects.length = 0;
		}
		
		/// 動作処理
		bool onMove(Screen screen) {
			if (!m_show) return true;

			m_offsetx.inc();
			m_offsety.inc();
			m_rateCnt.inc();
			m_roteCnt.inc();
			m_alphaCnt.inc();
			return true;
		}

		/// 描画処理
		bool onDraw(Screen screen) {
			if (!m_show) return true;

			Color4ub colorOrg = screen.getColor4ub();
			
			screen.setColor(255, 255, 255, m_alphaCnt.get());

//			screen.blt(m_texture, m_offsetx.get(), m_offsety.get());
			screen.bltRotate(m_texture, 
								m_offsetx.get(),
								m_offsety.get(),
								m_roteCnt.get(),
								m_rateCnt.get() / 100.0f,
								0 );
			
			screen.setColor(colorOrg);
			return true;
		}
		
		/// コンストラクタ
		this (int x, int y, int alpha, int rote, int rate) {
			m_x = x;
			m_y = y;
			m_alpha = alpha;
			m_rote = rote;
			m_rate = rate;
			
			m_offsetx = defaultCounter(new RootCounterS(), x );
			m_offsety = defaultCounter(new RootCounterS(), y );
			m_rateCnt = defaultCounter(new RootCounterS(), rate );
			m_roteCnt = defaultCounter(new RootCounterS(), rote );
			m_alphaCnt = defaultCounter(new RootCounterS(), alpha );
			
		}
		
	private:
		/// カウンターをデフォルトの値で初期化する
		ICounter defaultCounter(ICounter cnt, int def) {
			cnt.set(def,def,0);
			return cnt;
		}
		
		/// カウンターの現在の値を開始値として、終値とステップを上書きする
		void counterOverwrite(inout ICounter cnt, int end, int step) {
			int start = cnt.get();
			cnt.set(start, end, step);
		}
	
		/// エフェクトを動きに反映させる
		void setEffect(FrameEffectData effect) {
			switch (effect.type) {
			case FrameEffectData.TYPE.MOVE:
				counterOverwrite(m_offsetx, effect.x, effect.speed);
				counterOverwrite(m_offsetx, effect.y, effect.speed);
				break;

			case FrameEffectData.TYPE.ALPHA:
				counterOverwrite(m_alphaCnt, effect.end, effect.speed);
				break;

			case FrameEffectData.TYPE.SIZING:
				counterOverwrite(m_rateCnt, effect.end, effect.speed);
				break;

			case FrameEffectData.TYPE.ROTE:
				counterOverwrite(m_roteCnt, effect.end, effect.speed);
				break;

			default :
				Log.printWarn("unknown effecttye %s", cast(int) effect.type);
			}
		}
		
	private:
		Texture m_texture;
		int m_x;
		int m_y;
		int m_alpha;
		int m_rote;
		float m_rate = 1.0f;
		
		FrameEffectData[] m_effects;
		ICounter m_offsetx;
		ICounter m_offsety;
		ICounter m_rateCnt;
		ICounter m_roteCnt;
		ICounter m_alphaCnt;
	}
	
	/** テキスト描画用データクラス */
	static class ViewTextObject : ViewOjbectBase {
		
		/// 描画データをセットする
		void setDrawChar(ScenarioTextDraw.DrawChar[] dc) {
			m_drawchar = dc;
			m_textPhase.set(0, dc.length, m_speed);
			Log.print("DRAWCHAR size : %s", m_drawchar.length);
		}
		/// タグを設定する
		void setTag(ScenarioTextDraw.UnknownTagInfo[] tag) {
			m_tag = tag;
		}
		
		/// Xの取得
		int x() {
			return m_x;
		}
		int x(int x_) {
			return m_x = x_;
		}
		
		/// Yの取得
		int y() {
			return m_y;
		}
		int y(int y_) {
			return m_y = y_;
		}
	
		/// 速度値の設定／返却
		int speed() {
			return m_speed;
		}
		package int speed(int value) {
			return m_speed = value;
		}
	
		/// 動作処理
		void onMove(Screen screen) {
			if (!m_show) return;
			m_textPhase.inc();
		}
		
		/// 描画処理
		void onDraw(Screen screen) {
			if (m_drawchar is null) return;
			if (!m_show) return;
			
			int tx,ty;
			// メンバ変数をコピーして高速化する
			ScenarioTextDraw.DrawChar[] drawchar = this.m_drawchar;
			for(int i = 0; i < m_textPhase.get() && i < drawchar.length; ++i) {
				float size = drawchar[i].size; 
				// 影の描画
				screen.setColor(0,0,0);
				tx = m_x + cast(int) drawchar[i].pos.x + 2;
				ty = m_y + cast(int) drawchar[i].pos.y + 1;
				
				// 拡大ありで描画
				if (1.0f != size) {
					screen.bltRotate(
						drawchar[i].texture,	// テクスチャ 
						tx, ty, 	// x,y 位置
						0, 			// 回転角
						size, 		// 拡大率
						0			// 描画ベース位置（左上）
						);
				} else {
					screen.blt(drawchar[i].texture, tx, ty);
				}
	
				// 本文字の描画
				screen.setColor( drawchar[i].color );
				tx = m_x + cast(int) drawchar[i].pos.x;
				ty = m_y + cast(int) drawchar[i].pos.y;
				
				// 拡大ありで描画
				if (1.0f != size) {
					screen.bltRotate(
						drawchar[i].texture,	// テクスチャ 
						tx, ty, 	// x,y 位置
						0, 			// 回転角
						size, 		// 拡大率
						0			// 描画ベース位置（左上）
						);
				} else {
					screen.blt(drawchar[i].texture, tx, ty);
				}
			}
		}
		
		/// コンストラクタ
		this(int x_, int y_, int speed_) {
			m_textPhase = new RootCounterS();
			x = x_;
			y = y_;
			speed = speed_;
		}
	private:
		ScenarioTextDraw.DrawChar[] m_drawchar;	//!< 描画する矩形情報
		ScenarioTextDraw.UnknownTagInfo[] m_tag;	//!< タグ情報
		ICounter m_textPhase;
		int m_x;
		int m_y;
		int m_speed;
	}

private:
	final char[] m_subjectPath;	//! 四コマデータのパス

	KomaData m_data;
	OneFrameData m_frame;	//!< 今表示しているコマ
	bool[] m_playAct;
	bool[][] m_playEffect;
	int m_curFrame;	//!< 今表示しているコマ
	
	/*-- 画面遷移 --*/
	bool m_trans;		//!< 画面遷移エフェクト中か？
	RootCounterS m_transPhase;	//!< 画面遷移位相
	int m_transType;
	
	bool m_play;		//!< 今、データを再生しているか
	FixTimer m_timer;	//!< 時間制御につかうタイマ
	
	Texture m_backup;
	Texture m_baseTex;	//!< 基本テクスチャ
	ViewObject[char[]]	m_viewObjMap;		//!< 名前付きテクスチャの生成
	
	/*-- テキスト描画 --*/
	ScenarioTextDraw m_sd;
	TextDrawContext	 m_tdc;
	ViewTextObject[char[]] m_viewTextMap;	//!< 名前付きテクストオブジェクトの生成
	
	
	
}