module kyojintati4d.taskending2;

private import std.string;

private import y4d;

private import kyojintati4d.myapp;
private import kyojintati4d.gameinfo;

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

private import yamalib.draw.dropobject;


/**
	エンディング２
*/
class TaskEnding2 : GameTaskBase {
	
	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return cast(char[]) KyojinConst.TASK_NAME[KyojinConst.Task.Task_Ending2];
		}
	}

	/// 毎回呼び出す
	override int onMove(Object o) {
		info = cast(GameInfo) o;
		m_timer.update();
		long now = m_timer.get();

		if (!init) {
			onInit();
			info.screen.blendSrcAlpha();
			init = true;
			m_timer.reset();
		}
		
		foreach(inout DrawInfo di; draws) {
			if ( now >= di.time && !di.finish) {
				di.drawobj.onMove(info.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;	
	}
	
	/// 描画処理
	override int onDraw(Object o) {
		info = cast(GameInfo) o;
		info.screen.clear();
		
		long now = m_timer.get();
		
		foreach(inout DrawInfo di; draws) {
			if ( now >= di.time && !di.finish) {
				di.drawobj.onDraw(info.screen);
			}
		}

		sm.onDraw(info.screen);
		return 0;
	}

	/// レガシーなやつ
	int task(Object o) {
		return 0;
	}

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

		Log.print("%s#destroy : destroyed.", super.toString());
	}

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

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

		/// 分解能の設定
		static void setResolution(int res) {
			if (res > 0) {
				RESOLUTION = res;
			}
		}
		
		/// 画像のロード
		void load(char[] filename) {
			// サーフェイスに読み込む
			Surface tmp = new Surface();
			tmp.load(filename);
			
			// 読み込んだ画像のコピー
			Surface tmp2 = TaskEnding2.copySurface(tmp);
			m_texture.setSurface(tmp2);
			
			m_surfacese ~= tmp;
			
			int[] index;
			float div = MAX_RESOLUTION / cast(float) RESOLUTION;
			index ~= 0;
			for(float f = 1.0f; f < MAX_RESOLUTION; f += div) {
				index ~= cast(int) f;
			}
			
			Surface effectTmp = TaskEnding2.copySurface( m_surfacese[length-1] );
			/// ぼかしの段階画像をラスタライズしておく
			for (int i = 1; i < MAX_RESOLUTION; ++i) {
				effectTmp = EffectSoft.excecute(effectTmp, SOFT_SIZE);
				if ( isEnable(index, i) ) {
					m_surfacese ~= TaskEnding2.copySurface(effectTmp);
				}
			}
			
			effectTmp.release();
			
Log.printLook("leng = %s", m_surfacese.length);		
			m_texture.subSurfaceFast( m_surfacese[length-1] );
//			m_texture.setSurface( m_surfacese[length-1] );
		}
		
		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_);
		}
		
		/// 描画位置の設定
		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) {
			if (m_surfacese.length == 0) {
				return;
			}
			
			int pre = m_index.get();
			m_index.inc();
			int now = m_index.get();
			if ( now != pre ) {
				if ( !m_reverse ) {
					m_texture.subSurfaceFast( m_surfacese[RESOLUTION-now-1] );
//					m_texture.setSurface( m_surfacese[RESOLUTION-now-1] );
				} else {
					if ( now >= m_surfacese.length ) {
						Log.printError("BoundError : now = %s", now);
					}
					m_texture.subSurfaceFast( m_surfacese[now] );
//					m_texture.setSurface( m_surfacese[now] );
				}
			}
			
			m_offsetx.inc();
			m_offsety.inc();
			m_alpha.inc();
		}

		/// 描画処理
		void onDraw(Screen screen) {
			if (m_surfacese.length == 0) {
				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 {
//					screen.bltRotate(m_texture, x + m_offsetx.get(), y + m_offsety.get(),0,2,4);
//					screen.bltRotate(m_texture, x - m_offsetx.get(), y - m_offsety.get(),0,2,4);
					if (m_reverse) {
						screen.setColor(255,255,255, 255 - m_alpha.get());
					} else {
						screen.setColor(255,255,255, m_alpha.get());
					}

					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() {
			foreach(Surface s; m_surfacese) {
				if (s) {
					s.release();
				}
			}
		}
		
		/// コンストラクタ
		this() {
			m_texture = new Texture();
			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;
		}
		
		/// デストラクタ
		~this() {
			release();
		}
		
	private:
		static const int MAX_RESOLUTION = 64;
		static int RESOLUTION = 96;
		static int SOFT_SIZE = 2;
		Surface[] m_surfacese;
		Texture m_texture;
		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;
	}
	
	/// 描画構造体
	struct DrawInfo {
		DrawObj.DRAW_TYPE drawtype;
		int moveto;
		long time;		//!< 開始ms
		long wait;		//!< 表示時間
		long wait_mark;	//!< 停止開始ms
		bool wait_flg;
		bool finish;
		DrawObj drawobj;
	}
	

	/// 引数に指定したサーフェイスのディープコピーを作成する
	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;
	}
		
	/// 初期化処理
	void onInit() {
		// メモリー状況をプリント
		GameInfo.printMemoryState();

		onLoad();

		Surface surface = new Surface();
		surface.load(cast(char[]) "img/ending2/filter.png");

		sm = new DropObjectManager();
		sm.presetSnow();
		sm.init(250);
		
		sm.setAlphaFilter(surface, 0, 0);
	}
	
	/// コンフィグから読み込み
	int onLoad() {
		try {
			Properties prop = Properties.getInstance(cast(char[]) CONF_FILE);
//			prop.load( CONF_FILE );
			
			char[][] objSig = cast(char[][]) std.string.split( 
					prop.getProperty(cast(char[]) PROP_KEY_OBJECTS), DELIM );
			assert(objSig.length != 0);
	
			int fadetime = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_FADETIME, -1);
			int res = cast(int) prop.getPropertyNum(cast(char[]) PROP_KEY_RESOLUTION, -1);
			DrawObj.setResolution(res);	// 分解能
			
			draws = null;
			char[] filename;
			int drawType;
			int posx;
			int posy;
			int sizex;
			int sizey;
			int time;
			int moveto;
			int wait;
			
			foreach(char[] sig; objSig) {
				DrawInfo di;
				DrawObj dobj = new DrawObj();
				
				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);
				
				dobj.load(filename);
				dobj.setSpeed(fadetime);
				dobj.setDrawPos(posx, posy, DrawObj.OFFSET_TYPE.CENTER);
				
				di.drawobj = dobj;
				di.time = time;
				di.wait = wait;
				di.drawtype = cast(DrawObj.DRAW_TYPE) drawType;
				di.moveto = moveto;
				
				draws ~= di;	
			}
		} catch (Exception e) {
			Log.printFatal("%s#onLoad : [%s]", this.toString(),e.toString());
			throw e;
		}
		
		Log.print("Load FINISH");
		return 0;
	}
	
private:
	static const char[] CONF_FILE = "img/ending2/conf.txt";
	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;
	
	GameInfo info;
	bool init;
	DropObjectManager sm;
	
}
