module kyojintati4d.component.komadata;

private import dtmpl.dom;

private import std.string;
private import std.path;
private import std.conv;

private import y4d_aux.filesys;
private import y4d_aux.widestring;

private import yamalib.log.log;

/// 四コマのデータクラス
class KomaData {
	
	/// このクラスの文字列表現
	override char[] toString() {
		char[] buffer;
		buffer ~= super.toString() ~ std.string.toString(super.toHash());
		buffer ~= "DOC_ROOT=[" ~ DOC_ROOT ~ "]";
		foreach (int i, inout OneFrameData data; m_data) {
			buffer ~= "m_data[" ~ std.string.toString(i) ~ "]=[" ~ data.toString() ~ "]";
		}
		return buffer;
	}

	/// ファイルよりデータを読み込む
	bool load() {
		try {
			
			int count = 1;
			while (true) {
				char[] realName = DOC_ROOT ~ 
					std.string.replace(XML_FILE_NAME, "{0}", std.string.toString(count));
				
				// 定義XMLを読み込む
				char[] str = cast(char[]) FileSys.read(realName);
				if ( str is null ) {
					Log.printError("%s#load FileNotFound : [%s]",super.toString(), realName);
					break;
				}
				Log.printError("%s#load file : [%s]",super.toString(), realName);
				
				Dom domRoot = Dom.parse( clearText(str) );
				
				foreach( int i, inout Dom d; domRoot.array ) {
					Log.print("VAL[%d] = [%s]", i, d.value);
					if ( NODE_LAYER == d.value ) {
						// このdomはlayerノードトップ
						m_data ~= createFrameData( d );
						
					} else if ( NODE_OBJECT == d.value ) {
						KomaObject obj = createKomaObj( d );
						if ( !obj.isInvalid() ) {
							m_objects ~= obj;
						}
						
					} else if ( NODE_TEXT == d.value ) {
						TextData data = createTextData( d );
						if ( !data.isInvalid() ) {
							m_textData ~= data;
						}
						
					}
				}
				
				++count;
				break;
			}
			
			return true;
		} catch (Exception e) {
			Log.printFatal("Exception %s#load : [%s][%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
		
		return false;
	}
	
	/// 読み込んだデータを返却する。
	OneFrameData[] getFrameData() {
		return m_data;
	}
	
	/// 描画オブジェクト情報を取得する
	KomaObject[] getKomaObjects() {
		return m_objects;
	}
	
	/// テキストデータの設定
	TextData[] getTextData() {
		return m_textData;
	}
	
	/// データの破棄
	void clear() {
		m_data = null;
		m_objects = null;
	}
	
	/// コンストラクタ
	this(char[] sbjRoot) {
		if ( 0 == sbjRoot.length ) {
			sbjRoot = cast(char[]) "error";
		} 
		if ( '\\' != sbjRoot[length-1] && '/' != sbjRoot[length-1] ) {
			sbjRoot ~= std.path.sep;
		} 
		DOC_ROOT = sbjRoot;
	}
	
private:
	static const char[] XML_FILE_NAME = "layer.xml";
	
	// オブジェクトノート
	static const char[] NODE_OBJECT = "object";
	static const char[] NODE_FILE = "file";
	static const char[] NODE_ID = "id";
	static const char[] NODE_SIZE = "size";
	static const char[] NODE_ROTE = "rote";
	
	// テキストノード
	static const char[] NODE_TEXT = "text";
	static const char[] NODE_DATA = "data";
	static const char[] NODE_STYLE = "style";
	static const char[] NODE_COLOR = "color";
	
	// TOPノード
	static const char[] NODE_LAYER = "layer";
	static const char[] NODE_SOURCE = "source";
	// ACTIONノード
	static const char[] NODE_ACTION = "action";
	static const char[] NODE_TIME = "time";
	static const char[] NODE_POSX = "posx";
	static const char[] NODE_POSY = "posy";
	static const char[] NODE_ALPHA = "alpha";
	static const char[] NODE_SHOW = "show";

	// SCENE_TRASノード
	static const char[] NODE_SCENETRANS = "scenetrans";
	static const char[] NODE_SPEED = "speed";

	// EFFECTノード
	static const char[] NODE_EFFECT = "effect";
	static const char[] NODE_TYPE = "type";
	static const char[] NODE_OPT1 = "option1";
	static const char[] NODE_OPT2 = "option2";
	static const char[] NODE_OPT3 = "option3";
	static const char[] NODE_OPT4 = "option4";
	
	/// xmlファイルから改行、タブ文字を取り除く
	static char[] clearText(char[] str) {
		str = cast(char[]) std.string.replace(str, cast(char[]) "\t", cast(char[]) "");
		return cast(char[]) std.string.replace(str, cast(char[]) "\r\n", cast(char[]) "");
	}
	
	/// テキストデータの生成
	static TextData createTextData(inout Dom domTxt) {

		TextData data = new TextData();

		foreach (int i,inout Dom d; domTxt.array) {

			if ( NODE_DATA == d.value ) {
				data.text = getTopData(d);
				// ファイル名
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_ID == d.value) {
				// オブジェクトID
				data.id = getTopData(d);
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_POSX == d.value) {
				// X位置
				data.x = toInt( getTopData(d) );
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_POSY == d.value) {
				// Y位置
				data.y = toInt( getTopData(d) );
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_SIZE == d.value) {
				// 大きさ
				data.size = toInt( getTopData(d) );
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_STYLE == d.value) {
				// スタイル
				data.style = cast(TextData.TYPE) toInt( getTopData(d) );
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_SPEED == d.value) {
				// スピード
				data.speed = toInt( getTopData(d) );
				Log.print("TEXT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("UNK OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		return data;
	}
	
	/// オブジェクトデータの生成
	static KomaObject createKomaObj(inout Dom domObj) {

		KomaObject obj = new KomaObject();

		foreach (int i,inout Dom d; domObj.array) {

			if ( NODE_FILE == d.value ) {
				obj.filename = getTopData(d);
				// ファイル名
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_ID == d.value) {
				// オブジェクトID
				obj.id = getTopData(d);
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_POSX == d.value) {
				// X位置
				obj.x = toInt( getTopData(d) );
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_POSY == d.value) {
				// Y位置
				obj.y = toInt( getTopData(d) );
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_ALPHA == d.value) {
				// アルファ値
				obj.rote = toInt( getTopData(d) );
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_SIZE == d.value) {
				// 大きさ
				obj.size = toInt( getTopData(d) );
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_ROTE == d.value) {
				// 回転角
				obj.rote = toInt( getTopData(d) );
				Log.print("OBJ_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("UNK OBJ_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		return obj;
	}
	
	/// １コマデータ生成
	static OneFrameData createFrameData(inout Dom domLayler) {
		
		OneFrameData frameData = new OneFrameData();
		foreach (int i,inout Dom d; domLayler.array) {
			if ( NODE_ACTION == d.value ) {
				// これはアクションノード
				frameData.addAction( createFrameAction( d ) );
				
			} else if (NODE_SOURCE == d.value) {
				frameData.setSourceName( getTopData(d) );
				Log.print("LAYER_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_TIME == d.value) {
				frameData.setTime( toInt( getTopData(d) ) );
				Log.print("LAYER_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("UNK LAYER_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		
		return frameData;
	}
	
	/// １コマ内のアクションデータ生成
	static FrameAction createFrameAction(inout Dom domAction) {
		FrameAction frameAction = new FrameAction();
		foreach (int i,inout Dom d; domAction.array) {

//			Log.print("ACTION_VAL[%d] = [%s]", i, d.value);

			if ( NODE_EFFECT == d.value ) {
				// これはエフェクトノード
				frameAction.addEffect( createFrameEffectData( d ) );
				
			} else if (NODE_SCENETRANS == d.value) {
				// これは遷移データノード
				frameAction.setTransData( createSceneTransData( d ) );

			} else if (NODE_SOURCE == d.value) {
				frameAction.setId( getTopData(d) );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_TIME == d.value) {
				frameAction.setTime( toInt( getTopData(d) ) );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_POSX == d.value) {
				frameAction.setX( toInt( getTopData(d) ) );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_POSY == d.value) {
				frameAction.setY( toInt( getTopData(d) ) );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_ALPHA == d.value) {
				frameAction.setY( toInt( getTopData(d) ) );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_SHOW == d.value) {
				frameAction.setShow( cast(bool) (toInt( getTopData(d) ) > 0)  );
				Log.print("ACTION_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("LAYER_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		
		return frameAction;
	}
	
	/// シーン遷移エフェクトの設定
	static SceneTransData createSceneTransData(inout Dom domTrans) {
		SceneTransData trasData = new SceneTransData();
		foreach (int i,inout Dom d; domTrans.array) {

			if ( NODE_TYPE == d.value ) {
				trasData.type = toInt( getTopData(d) );
				Log.print("TRANS_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_SPEED == d.value) {
				trasData.speed = toInt( ( getTopData(d) ) );
				Log.print("TRANS_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("TRANS_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		
		return trasData;
	}
	
	/// エフェクトデータの生成
	static FrameEffectData createFrameEffectData(inout Dom domEffect) {
		
		FrameEffectData effectData = new FrameEffectData();
		int[10] data;
		int type = 0;
		foreach (int i,inout Dom d; domEffect.array) {

//			Log.print("ACTION_VAL[%d] = [%s]", i, d.value);

			if ( NODE_TYPE == d.value ) {
				type = toInt( getTopData(d) );
				Log.print("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));
				
			} else if (NODE_OPT1 == d.value) {
				data[0] = toInt( ( getTopData(d) ) );
				Log.print("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_OPT2 == d.value) {
				data[1] = toInt( ( getTopData(d) ) );
				Log.print("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_OPT3 == d.value) {
				data[2] = toInt( ( getTopData(d) ) );
				Log.print("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else if (NODE_OPT4 == d.value) {
				data[3] = toInt( ( getTopData(d) ) );
				Log.print("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));

			} else {
				Log.printWarn("EFFECT_VAL[%s] = [%s]", d.value, getTopData(d));
			}
		}
		
		// タイプに添ったデータの設定
		effectData.set(cast(FrameEffectData.TYPE) type, data);
		
		return effectData;
	}
	
	/// 対象のDomよりトップのデータを取得する
	static char[] getTopData(inout Dom d) {
		foreach (int i,inout Dom dd; d.array) {
			if ( Dom.TEXT == dd.type ) {
				return dd.value;
			}
		}
		
		return null;
	}
	
	/// 文字列から数値型を取得する 失敗すると-1が返却される
	static int toInt(char[] str) {
		int num = 0;
		try {
			num = std.conv.toInt( str );
		} catch (ConvError e) {
			num = -1;
		}
		return num;
	}
	
private:
	final char[] DOC_ROOT;
	OneFrameData[] m_data;
	KomaObject[] m_objects;
	TextData[] m_textData;
	
}

/// 一つのコマのデータ
class OneFrameData {

	/// このクラスの文字列表現
	override char[] toString() {
		char[] buffer;
		buffer ~= super.toString() ~ std.string.toString(super.toHash());
		buffer ~= "m_sourceNm=[" ~ m_sourceNm ~ "]";
		buffer ~= "m_time=[" ~ std.string.toString(m_time) ~ "]";
		foreach (int i, inout FrameAction data; m_actions) {
			buffer ~= "m_actions[" ~ std.string.toString(i) ~ "]=[" ~ data.toString() ~ "]";
		}
		return buffer;
	}
	
	/// ソースファイル名の設定／取得
	void setSourceName(char[] nm) {
		m_sourceNm = nm;
	}
	char[] getSourceName() {
		return m_sourceNm;
	}
	
	/// 時間の設定／取得
	void setTime(int i) {
		m_time = i;
	}
	/// 時間の設定／取得
	void getTime() {
		return m_time;
	}
	
	/// アクション情報の追加
	void addAction(FrameAction action) {
		m_actions ~= action;
	}
	
	/// アクションの取得
	FrameAction[] getActions() {
		return m_actions;
	}
	
private:
	FrameAction[] m_actions;
	char[] m_sourceNm;
	int m_time;
}

/// 一つのコマにおける、一つのアクション情報
class FrameAction {
	
	/// 無効な値の定義(こんなんダサー
	static const int INVALID_VAL = int.max;

	/// このクラスの文字列表現
	override char[] toString() {
		char[] buffer;
		buffer ~= super.toString() ~ std.string.toString(super.toHash());
		buffer ~= "m_sourceNm=[" ~ m_filename ~ "]";
		buffer ~= "m_time=[" ~ std.string.toString(m_time) ~ "]";
		buffer ~= "m_x=[" ~ std.string.toString(m_x) ~ "]";
		buffer ~= "m_y=[" ~ std.string.toString(m_time) ~ "]";
		buffer ~= "m_alpha=[" ~ std.string.toString(m_y) ~ "]";
		foreach (int i, inout FrameEffectData data; m_effects) {
			buffer ~= "m_actions[" ~ std.string.toString(i) ~ "]=[" ~ data.toString() ~ "]";
		}
		return buffer;
	}
	
	/// ソースファイル名の設定
	void setId(char[] filename) {
		m_filename = filename;
	}
	/// ソースファイル名の取得
	char[] getId() {
		return m_filename;
	}
	
	/// 時間の設定／取得
	void setTime(int i) {
		m_time = i;
	}
	/// 時間の設定／取得
	int getTime() {
		return m_time;
	}
	
	/// アルファの設定／取得
	void setAlpha(int alpha) {
		m_alpha = alpha;
	}
	int getAlpha() {
		return m_alpha;
	}

	/// X位置の設定／取得
	void setX(int x) {
		m_x = x;
	}
	int getX() {
		return m_x;
	}
	
	/// Y位置の設定／取得
	void setY(int y) {
		m_y = y;
	}
	int getY() {
		return m_y;
	}
	
	/// エフェクトデータの追加
	void addEffect(FrameEffectData effect) {
		m_effects ~= effect;
	}
	
	/// 表示か非表示の設定
	void setShow(bool b) {
		m_show = b;
	}
	
	/// 表示か非表示か
	bool isShow() {
		return m_show;
	}
	
	/// エフェクト情報の取得
	FrameEffectData[] getEffects() {
		return m_effects;
	}
	
	/// 画面遷移情報の設定
	void setTransData(SceneTransData data) {
		m_transData = data;
	}
	
	/// 画面遷移情報の取得
	SceneTransData getTransData() {
		return m_transData;
	}
	
	/// データの破棄
	void clear() {
		m_effects = null;
	}
	
	/// コンストラクタ
	this () {
		m_x = INVALID_VAL;
		m_y = INVALID_VAL;
		m_alpha = INVALID_VAL;
	}

private:
	FrameEffectData[] m_effects;
	SceneTransData m_transData;
	char[] m_filename;	//!< 必須
	bool m_show;	//!< 必須
	int m_time;	//!< 必須
	int m_x;	//!< 任意
	int m_y;	//!< 任意
	int m_alpha;	//!< 任意
}

/// コマ遷移間でのエフェクトデータ
class SceneTransData {
	int type() {
		return m_type;
	}
	int type(int type_) {
		return m_type = type_;
	}
	int speed() {
		return m_speed;
	}
	int speed(int speed_) {
		return m_speed = speed_;
	} 
private:
	int m_type;
	int m_speed;
}

/// 一つのコマで動作するエフェクトのデータ
class FrameEffectData {
	enum TYPE : int { MOVE=1, SIZING, ROTE, ALPHA, TRANS  };

	/// このクラスの文字列表現
	override char[] toString() {
		char[] buffer;
		buffer ~= super.toString() ~ std.string.toString(super.toHash());
		buffer ~= "m_type=[" ~ std.string.toString(cast(int) m_type) ~ "]";
		buffer ~= "m_startVal=[" ~ std.string.toString(m_startVal) ~ "]";
		buffer ~= "m_endVal=[" ~ std.string.toString(m_endVal) ~ "]";
		buffer ~= "m_speedVal=[" ~ std.string.toString(m_speedVal) ~ "]";
		buffer ~= "m_time=[" ~ std.string.toString(m_time) ~ "]";
		buffer ~= "m_x=[" ~ std.string.toString(m_x) ~ "]";
		buffer ~= "m_y=[" ~ std.string.toString(m_y) ~ "]";
		return buffer;
	}

	/// タイプに合わせたデータ設定
	void set(TYPE type, int[10] data) {
		switch ( type ) {
		case TYPE.MOVE :
			x = data[0];
			y = data[1];
			speed = data[2];
			break;
			
		case TYPE.SIZING :
			start = data[0];
			end = data[1];
			speed = data[2];
			break;
			
		case TYPE.ROTE :
			start = data[0];
			end = data[1];
			speed = data[2];
			break;
			
		case TYPE.ALPHA:
			start = data[0];
			end = data[1];
			speed = data[2];
			break;
			
		default :
			Log.printError("FrameEffectData#set : No such type %d", type);
			break;
		}
 	}
	
	/// エフェクトタイプの設定
	TYPE type() {
		return m_type;
	}
	package TYPE type(TYPE type_) {
		return m_type = type_;
	}
	
	/// 座標Xの設定／取得
	int x() {
		return x;
	}
	int x(int x_) {
		return this.m_x = x_;
	}

	/// 座標Xの設定／取得
	int y() {
		return y;
	}
	int y(int y_) {
		return this.m_y = y_;
	}
	
	/// 開始値の設定／返却
	int start() {
		return m_startVal;
	}
	package int start(int value) {
		return m_startVal = value;
	}
	
	/// 終了値の設定／返却
	int end() {
		return m_endVal;
	}
	package int end(int value) {
		return m_endVal = value;
	}
	
	/// 速度値の設定／返却
	int speed() {
		return m_speedVal;
	}
	package int speed(int value) {
		return m_speedVal = value;
	}
	
	/// 時間の設定／取得
	int time() {
		return m_time;
	}
	int time(int value) {
		return time = value;
	}

private:
	TYPE m_type;	
	int m_startVal;
	int m_endVal;
	int m_speedVal;
	int m_time;
	int m_x;
	int m_y;

}

/// 四コマで使う描画オブジェクトの定義
class KomaObject {
	
	static const int RATE_SCALER = 100;
	
	/// このデータが無効なデータか
	bool isInvalid() {
		return cast(bool) (m_filename is null || m_id is null);
	}
	
	/// ファイル名の設定／取得
	char[] filename() {
		return m_filename;
	}
	char[] filename(char[] filenm) {
		return m_filename = filenm;
	}
	
	/// IDの設定／取得
	char[] id() {
		return m_id;
	}
	char[] id(char[] id_ ){
		return m_id = id_;
	}
		
	
	/// 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_;
	}
	
	/// Xの取得
	int alpha() {
		return m_alpha;
	}
	int alpha(int alpha_) {
		return m_alpha = alpha_;
	}

	/// 角度の取得
	int rote() {
		return m_rote;
	}
	int rote(int rote_) {
		return m_rote = rote_;
	}

	/// 拡大サイズの取得
	int size() {
		return m_size;
	}
	int size(int size_) {
		return m_size = size_;
	}
	
	/// コンストラクタ
	this() {
		m_alpha = 255;
		m_size = RATE_SCALER;
	}
	
private:
	char[] m_filename;
	char[] m_id;
	int m_x;
	int m_y;
	int m_alpha;
	int m_size;
	int m_rote;	
}

/** テキストデータクラス */
class TextData {
	
	enum TYPE : int { HORIZONTAL, VERTICAL };
	
	/// このデータが無効なデータか
	bool isInvalid() {
		return cast(bool) (m_text is null || m_id is null);
	}

	char[] text() {
		return m_text;
	}
	char[] text(char[] text_) {
		return m_text = text_;
	}
	char[] id() {
		return m_id;
	}
	char[] id(char[] id_) {
		return m_id = id_;
	}
	
	/// 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_;
	}

	/// sizeの取得
	int size() {
		return m_size;
	}
	int size(int size_) {
		return m_size = size_;
	}
	
	/// 描画タイプの設定
	TYPE style() {
		return m_style;
	}
	package TYPE style(TYPE type_) {
		return m_style = type_;
	}

	/// 速度値の設定／返却
	int speed() {
		return m_speed;
	}
	package int speed(int value) {
		return m_speed = value;
	}
	
	/// コンストラクタ
	this () {
		m_style = TYPE.VERTICAL;
	}
	
private:
	char[] m_text;
	char[] m_id;
	TYPE m_style;
	int m_x;
	int m_y;
	int m_speed;
	int m_size;
	
}

