module kyojintati4d.taskloader;

private import SDL_thread;
private import std.thread;
private import std.string;	// toString
private import std.conv;

private import y4d;
private import y4d_aux.filesys;
private import y4d_aux.lineparser;

private import kyojintati4d.gameinfo;

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

/// ローダークラス
class TaskLoader : GameTaskBase, IThreadCaller {
	
	/// タスク名を返却する
	/**
		このタスク名称を使って、コントローラ部でタスク判別を行う可能性があるので
		ユニークな名前で、実装しておくべきである
	*/	
	override char[] getTaskName() {
		with (kyojintati4d.val.kyojinconst) {
			return KyojinConst.TASK_NAME[KyojinConst.Task.Task_Loader];
		}
	}
	
	/// スレッドとして呼び出すデリゲート
	void setDelegate(inout int delegate() dg) {
		this.dg = dg;
	}
	
	/// 呼び出す対象のスレッドを設定する
	void setThread(ProgressiveThread th) {
		this.targetThread = th;
	}

	/// 毎回呼び出すなり
	override int onMove(Object o) {
		try {
			info = cast(GameInfo)o;
	
			if (!init) {
				// 呼び出すスレッドが設定されていない
				if ( (dg is null) && (targetThread is null) ) {
					info.gameSceneController.returnScene();
					return -1;
				}
				
				onInit();
			}
	
			// スレッドが終了したか..
			if (thread.getState() == Thread.TS.TERMINATED) {
				Log.printLook("Loader Thread finish!");
				Log.printLook("createAllTexture");
				Texture.createTextureAll();
				info.gameSceneController.returnScene();
				info.getFpsTimer().setFps(60);
				
				return -1;
			}
	
			// アルファマックスに達したときあるフレーム間は完全にスレッドに制御を渡す
			if (alpha.get()==255) {
				Thread.yield();
				count++;
	
				if (count == LC_WAIT_COUNT) {
					alpha++;
					count = 0;
				}
			} else {
				alpha++;
			}
			
			if (this.progressive) {
				char[] num = std.string.toString( targetThread.getProgress() );
				index = null;
				foreach(char c; num) {
					char[] tmp;
					tmp ~= c;
					index ~= std.conv.toInt(tmp) % 10;
				}
			}
	
			return 0;
		} catch (Exception e) {
			Log.printFatal("RuntimeException %s#onMove : [%s] [%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}

	/// 適宜呼び出すなり
	override int onDraw(Object o) {
		try {
			info = cast(GameInfo)o;
	
			if (dgDraw is null) {
				info.screen.enableBlend();
				info.screen.blendSrcAlpha();
				info.screen.clear();
				info.screen.setColor(255,255,255,alpha.get());
				info.screen.blt(bg,250,420);
				
				// 進捗率を表示する...
				if ( this.progressive ) {
					int ox,oy;
					foreach(inout int i; index) {
						info.screen.setColor(255,255,255);
						if (i >= numbers.length) {
//							printf("wong : %d\n", i);
						}
						info.screen.blt(numbers[i], ox, oy);
						ox += numbers[i].getWidth();
					}
				}
				
				info.screen.resetColor();
			} else {
				// デリゲードで処理せい！
				dgDraw(info.screen);
			}
	
			return 0;
		} catch (Exception e) {
			Log.printFatal("RuntimeException %s#onDraw : [%s] [%s]", 
				super.toString(), e.toString(), e.msg);
			throw e;
		}
	}
	
	/// onMove+onDraw
	override int task(Object o) {
		return 0;
	}

	/// 占有しているメモリを解放する
	override void destroy() {
		this.bg = null;
		this.alpha = cast(RootCounter) null;
		
		Texture.safeRelease(bg);
		foreach( Texture t; numbers) {
			Texture.safeRelease(t);
		}
		
		this.thread = null;
		this.dg = null;
		this.dgDraw = null;
		targetThread = null;

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

	/// コンストラクタ
	this() {
		alpha = new RootCounter;
		bg = new Texture;
		alpha.set(0,255,10);
		alpha.setReverse(true);
	}
	
private :

	/// 初期化関数
	void onInit() {
		// メモリー状況をプリント
		GameInfo.printMemoryState();

		init = true;
		Log.print("onInit()");

		info.getFpsTimer().setFps(LC_FLAME_SPEED);
		
		// 進捗表示可能か？
		this.progressive = cast(bool) !(targetThread is null); 
		
		if ( this.progressive ) {
			thread = new Thread(targetThread.getDelegate());

			// 表示用デリゲード
			this.dgDraw = targetThread.getDrawDelegate();
			
			// 描画を担当タスクでおこなうなら不要な読み込みはしない
			if (this.dgDraw is null) {
				initNumbers();
			}
		} else {
			thread = new Thread(dg);
		}
		
		if (this.dgDraw is null) {
			bg.load(FileSys.makeFullName("img/load/loading.png"));
		}
		thread.start();
		Log.printLook("Loader Thread start!");
	}
	
	/// 番号テクスチャのロード
	void initNumbers() {
		numbers = null;
		ubyte[] mem = cast(ubyte[]) FileSys.read("img/load/num/num.lst");
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		LineParser lp = new LineParser;
		while (!m.eof) {
			char[] linebuf = m.readLine();
			lp.setLine(linebuf);
			char[] filename = lp.getStr();
			if (!filename) continue;
			
			Texture t = new Texture();
			t.load(filename);
			numbers ~= t;
		}
	}

private :
	static const int LC_WAIT_COUNT = 20;
	static const int LC_FLAME_SPEED = 15;

	GameInfo info;
	bool init;
	bool progressive;
	
	Texture[] numbers;
	int[] index;

	Texture bg;
	Thread thread;
	int delegate() dg;				//!< スレッドとして呼び出すデリゲート
	int delegate(Screen) dgDraw;	//!< スレッドとして呼び出すデリゲート
	ProgressiveThread targetThread;

	RootCounter alpha;
	int count;
}
