module yamalib.draw.slideshadedraw;

private import y4d_aux.filesys;
private import y4d_draw.screen;
private import y4d_draw.surface;
private import y4d_draw.texture;
private import y4d_draw.textureloader;
private import y4d_timer.fixtimer;

private import yamalib.auxil.properties;
private import yamalib.draw.effectsoft;
private import yamalib.counterfsp;
private import yamalib.log.log;


/** ぼかしスライドフェードイン・アウトを描画を行うクラス */
public class SlideShadeDraw {

	public static class DrawObj {
		/// オフセットのタイプ
		enum OFFSET_TYPE { LT, CENTER };
		/// 描画のタイプ
		enum DRAW_TYPE : int { NORMAL=0, DUAL_LR, DUAL_TB };

		/// 分解能の設定
		void setResolution(int res) {
			if (res > 0) {
				resolution = res;
			}
		}
		
		/// テクスチャローダを設定する
		void setTextureLoader(TextureLoader tl) {
			assert(tl !is null);
			imgTextures = tl;
//			m_texture =  tl.get(0);
			setResolution(imgTextures.getInfoList.size);
			for (int i = 0; i < imgTextures.getInfoList.size(); ++i) {
				auto tmp = imgTextures.get(i);
			}
		}
		
		bool isEnable(int[] index, int val) {
			foreach(int i; index) {
				if (i == val) {
					return true;
				}
			}
			return false;
		}
		
		/// 終了しているか
		bool isEnd() {
			return m_index.isEnd();
		}
		
		/// スピードの設定
		void setSpeed(int speed_) {
			m_index.set(0, resolution-1, speed_);
			m_alpha.set(0,255, speed_);
			m_alphaDiv.set(0, 255, cast(int) (255 / (speed_ / cast(float) resolution)) );
		}
		
		/// 描画位置の設定
		void setDrawPos(int x_, int y_, OFFSET_TYPE type) {
			m_x = x_;
			m_y = y_;
			m_type = type;
		}
		
		/// 描画タイプの設定
		void setDrawType(DRAW_TYPE type_, int dstPos) {
			m_drawType = type_;
			
			int startPos;
			int endPos;
		
			if ( !m_reverse ) {
				// 目標位置に近づく
				startPos = dstPos;
				endPos = 0;
			} else {
				// 目標位置から遠のく
				endPos = dstPos;
				startPos = 0;
			}
			
			switch(m_drawType) {
			case DRAW_TYPE.DUAL_TB:
				m_offsetx.set(0,0,0);
				m_offsety.set(startPos, endPos, m_index.getStep());
				break;
				
			case DRAW_TYPE.DUAL_LR:
				m_offsetx.set(startPos, endPos, m_index.getStep());
				m_offsety.set(0,0,0);
				break;
				
			default:
				m_offsetx.set(0,0,0);
				m_offsety.set(0,0,0);
				break;
			}
		}
				
		/// 再生方向の設定
		void setReverse(bool b) {
			if (m_reverse != b) {
				m_index.reset();
				m_offsetx.reset();
				m_offsety.reset();
				m_alpha.reset();
			}
			m_reverse = b;
			
		}
		
		/// 再生方向の取得
		bool isReverse() {
			return m_reverse;
		}
		
		/// 動作処理
		void onMove(Screen screen) {
			int pre = m_index.get();
//			m_alphaDiv.inc();
			m_index.inc();
			int now = m_index.get();
			if ( now != pre ) {
				m_textureSub = m_texture;
				if ( !m_reverse ) {
					m_texture = imgTextures.get(resolution-now-1);
				} else {
					m_texture = imgTextures.get(now);
				}
				m_alphaDiv.reset();
			}
			
			m_offsetx.inc();
			m_offsety.inc();
			m_alpha.inc();
		}

		/// 描画処理
		void onDraw(Screen screen) {
			if (m_texture is null) {
				return;
			}
			int x,y;
			switch(m_type) {
			case OFFSET_TYPE.CENTER:
				int w = cast(int) m_texture.getWidth();
				int h = cast(int) m_texture.getHeight();
				x = m_x - (w / 2);
				y = m_y - (h / 2);
				break;
				
			default:
				x = m_x;
				y = m_y;
				break;
			}
			
			// 描画タイプに合わせて描画
			switch (m_drawType) {
			case DRAW_TYPE.DUAL_TB :
			case DRAW_TYPE.DUAL_LR :
				if (m_offsetx.get() == 0 && m_offsety.get() == 0) {
					screen.setColor(255,255,255, m_alpha.get());
					screen.blt(m_texture, x, y);
				} else {
					int alpha; 
					if (m_reverse) {
						alpha = 255 - m_alpha.get();
					} else {
						alpha = m_alpha.get();
					}
					screen.setColor(255,255,255, alpha);

					screen.blt(m_texture, x + m_offsetx.get(), y + m_offsety.get());
					screen.blt(m_texture, x - m_offsetx.get(), y - m_offsety.get());
				}
				break;
				
			default :
				screen.setColor(255,255,255, m_alpha.get());
				screen.blt(m_texture, x, y);
//				screen.bltRotate(m_texture, x, y, 0,2,4);
				break;
			}
			
		}
		
		/// 解放
		void release() {
		}
		
		/// コンストラクタ
		this() {
			m_index = new InteriorCounter();
			m_index.set(0, resolution, resolution-1);
			
			m_offsetx = new InteriorCounter();
			m_offsetx.set(0,0,1);
			m_offsety = new InteriorCounter();
			m_offsety.set(0,0,1);
			
			m_alpha = new InteriorCounter();
			m_alpha.set(0,128,128);
						
			m_type = OFFSET_TYPE.LT;
			m_drawType = DRAW_TYPE.NORMAL;
			
			m_alphaDiv = new RootCounter();
		}
		
		/// デストラクタ
		~this() {
			release();
		}
		
	private:
		static const int MAX_RESOLUTION = 64;
		static int SOFT_SIZE = 2;
		int resolution = 96;
		TextureLoader imgTextures;
		Texture m_texture;
		Texture m_textureSub;
		RootCounter m_alphaDiv;
		bool m_reverse;		//!< 注意！！ ぼかし０からぼかしていくならture
		InteriorCounter m_index;
		int m_x;
		int m_y;
		InteriorCounter m_offsetx;
		InteriorCounter m_offsety;
		InteriorCounter m_alpha;
		OFFSET_TYPE m_type;
		DRAW_TYPE m_drawType;
	}
	
	/// 描画構造体
	public static class DrawInfo {
		DrawObj.DRAW_TYPE drawtype;
		int moveto;
		long time;		//!< 開始ms
		long wait;		//!< 表示時間
		long wait_mark;	//!< 停止開始ms
		bool wait_flg;
		bool finish;
		DrawObj drawobj;
	}


	/// 描画する情報オブジェクトを作成する	
	static DrawInfo createDrawInfo(char[] defFile, int drawType, int x, int y, int sizex,  
		int sizey, int time, int moveto, int wait, int fadeTime) {
		DrawObj dobj = new DrawObj();
		auto loader = new TextureLoader();
		loader.setCacheSize(-1);
		loader.loadDefFile(defFile);
//		dobj.load(imgFile);
		dobj.setTextureLoader(loader);
		dobj.setSpeed(fadeTime);
		dobj.setDrawPos(x, y, DrawObj.OFFSET_TYPE.CENTER);
		
		DrawInfo di = new DrawInfo();
		di.drawobj = dobj;
		di.time = time;
		di.wait = wait;
		di.drawtype = cast(DrawObj.DRAW_TYPE) drawType;
		di.moveto = moveto;
		return di;
	}
	
	/// コンフィグから読み込み
	static SlideShadeDraw createInstance(char[] configFile) {
		auto instance = new SlideShadeDraw();
		try {
			Properties prop = Properties.getInstance(configFile);
			auto objSig = std.string.split( prop.getProperty(cast(char[]) PROP_KEY_OBJECTS), DELIM );
			assert(objSig.length != 0);
	
			char[] filename;
			int drawType;
			int posx;
			int posy;
			int sizex;
			int sizey;
			int time;
			int moveto;
			int wait;
			int fadetime = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_FADETIME, -1);
			foreach(sig; objSig) {
				filename = prop.getProperty(sig ~ PROP_KEY_FILENAME);
				drawType = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_DRAWTYPE, -1);
				posx = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_POSX, -1);
				posy = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_POSY, -1);
				sizex = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_SIZEX, -1);
				sizey = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_SIZEY, -1);
				time = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_TIME, -1);
				moveto = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_MOVETO, -1);
				wait = cast(int) prop.getPropertyNum(sig ~ PROP_KEY_WAIT, -1);

				Log.printLook("NOW LOAD name=[%s], time=%s moveto=%s, wait=%s", filename, time, moveto, wait);

				instance.addDrawInfo( createDrawInfo(filename, drawType, posx, posy, sizex, sizey, 
					time, moveto, wait, fadetime) );	
			}
		} catch (Exception e) {
			Log.printFatal("SlideShadeDraw#onLoad : [%s]", e.toString());
			throw e;
		}
		
		Log.print("Load FINISH");
		return instance;
	}
	
	/// 描画オブジェクトを追加する
	void addDrawInfo(DrawInfo drawInfo) {
		if (drawInfo !is null) {
			draws ~= drawInfo;
		}
	}

	/// 毎回呼び出す
	int onMove(Screen screen) {
		m_timer.update();
		long now = m_timer.get();

		if (!init) {
			// タイマーをリセットする
			init = true;
			m_timer.reset();
		}

		foreach(inout DrawInfo di; draws) {
			if ( now >= di.time && !di.finish) {
				di.drawobj.onMove(screen);
				if ( di.drawobj.isEnd() ) {
					if ( !di.drawobj.isReverse() ) {
						if (!di.wait_flg) {
							di.wait_flg = true;
							di.wait_mark = now;
						}
						// 消えていく
						if (now >= di.wait_mark + di.wait) {
							di.drawobj.setReverse(true);
							di.drawobj.setDrawType(di.drawtype, di.moveto);
						}
					} else {
						// 消え終了
						di.finish = true;
					}
				}
			}
		}
		
		return 0;	
	}
	
	/// 描画処理
	int onDraw(Screen screen) {
		long now = m_timer.get();
		foreach(inout DrawInfo di; draws) {
			if ( now >= di.time && !di.finish) {
				di.drawobj.onDraw(screen);
			}
		}
		return 0;
	}
	
	bool isEnd () {
		if (draws is null) {
			return true;
		}
		foreach(inout DrawInfo di; draws) {
			if (!di.finish) {
				return false;
			}
		}		
		return true;
	}
	
	/// 状態をリセットする
	void reset() {
		destroy();
		draws = null;
		init = false;
	}
	
	/// 実データを取得する
	DrawInfo[] getDrawInfo() {
		return draws;
	}

	/// 占有しているメモリを解放する
	void destroy() {
		foreach(inout DrawInfo di; draws) {
			if (di.drawobj) {
				di.drawobj.release();
			}
		}
	}

	/// コンストラクタ
	this() {
		m_timer = new FixTimer();
	}
private:

	/// 引数に指定したサーフェイスのディープコピーを作成する
	static Surface copySurface(Surface surface_) {
		int width = cast(int) surface_.getWidth();
		int height = cast(int) surface_.getHeight();
		bool alpha = cast(bool) (surface_.getSurface().format.BitsPerPixel==32);

		// createDIBして転送したほうがはやいんでないかな...
		Surface surface = new Surface();
		surface.createDIB( width, height, alpha );

		surface.blt( surface_, 0, 0 );
		return surface;
	}
	
private:
	static const char[] DELIM = ",";
	// 共通
	static const char[] PROP_KEY_OBJECTS = "objects";
	static const char[] PROP_KEY_RESOLUTION = "resolution";
	static const char[] PROP_KEY_FADETIME = "fadetime";
	// 繰り返し項目
	static const char[] PROP_KEY_FILENAME = ".filename";
	static const char[] PROP_KEY_DRAWTYPE = ".drawtype";
	static const char[] PROP_KEY_POSX = ".posx";
	static const char[] PROP_KEY_POSY = ".posy";
	static const char[] PROP_KEY_SIZEX = ".sizex";
	static const char[] PROP_KEY_SIZEY = ".sizey";
	static const char[] PROP_KEY_TIME = ".time";
	static const char[] PROP_KEY_MOVETO = ".moveto";
	static const char[] PROP_KEY_WAIT = ".wait";

	FixTimer m_timer;
	DrawInfo[] draws;
	
	bool init = false;
}