module yamalib.draw.spiralmove;

private import y4d;
private import y4d_thread.gametask;
private import ytl.objectpool;

private import yamalib.counter;
private import yamalib.log.log;


alias objectPool!(SpiralObject) ObjectPool;

/// 一つのオブジェクトを表す
class SpiralObject : GameTaskBase {
	
	/// 自分自身のマネージャを設定する
	static void setManager(SpiralObjectManager manager_) {
		manager = manager_;
	}

	/// テクスチャの設定
	void setTexture(Texture tex_) {
		this.m_texture = tex_;
	}
	/// テクスチャの取得
	Texture getTexture() {
		return this.m_texture;
	}
	/// 位置の設定
	void setXY(int x_, int y_) {
		this.m_x = x_;
		this.m_y = y_;
	}
	/// 位置の取得
	void getXY(out int x_, out int y_ ) {
		x_ = this.m_x;
		y_ = this.m_y;
	}
	/// 縦の増分を設定
	void setDy(float f_) {
		this.m_dy = f_;
	}
	/// 増分の取得
	float getDy() {
		return this.m_dy;
	}
	/// 回転半径の設定
	/// 半径をどんどん大きくしていくのならば、rn_に最終半径値、stepに拡大速度を設定する
	void setRadius(int rs_, int rn_, int step_ ) {
		this.m_radiusInc.set(rs_,rn_,step_);
	}
	/// 回転半径の取得
	int getRadius() {
		return this.m_radiusInc.getStart();
	}
	/// 各速度の設定
	void setRadSpeed(int n) {
		this.m_radSpeed.setStep(n);
	}
	/// アルファの増（減）分設定
	void setAlphaStep(int start, int end, int step) {
		m_alpha.set(start,end,step);
	}
	
	/// カラーの遷移
	void setColorStep(inout Color4ub start, inout Color4ub end, int step) {
		m_r.set(start.r, end.r, step);
		m_g.set(start.g, end.g, step);
		m_b.set(start.b, end.b, step);
	}
	
	/// 移動
	int onMove(Object o) {
		Screen screen = cast(Screen) o;
		
		// 角移動
		m_radSpeed++;
		int rad = m_radSpeed.get();
		// 縦移動
		m_addY += m_dy;
		// 半径拡大
		m_radiusInc++;
		// アルファ値
		m_alpha++;
		// カラー
		m_r++;
		m_g++;
		m_b++;
//		if (m_r.isEnd()) m_r.set(m_r.getEnd(), m_r.getEnd(), 1 );
//		if (m_g.isEnd()) m_g.set(m_g.getEnd(), m_g.getEnd(), 1 );
//		if (m_b.isEnd()) m_b.set(m_b.getEnd(), m_b.getEnd(), 1 );
		
		int radius = m_radiusInc.get();
		float z = st.sin(rad, radius);
		if ( z >= 0 ) {
			// 奥
			m_rate = (LC_Z_MAX-z) / cast(float) LC_Z_MAX;

		} else {
			// 手前
			m_rate = (-z + LC_Z_MAX) / cast(float) LC_Z_MAX;
		}
		
		// 画面外にでたらリサイクル
		if ( 0 < m_dy ) {
			// 下降
			if (m_y + m_addY > screen.getHeight) {
				// 自殺する
				// まだタスクをけしてはダメ
				manager.getPool().pushForced(this);
				return 1;
			}
		} else {
			// 上昇
			if (m_y + m_addY < 0) {
				// 自殺する
				// まだタスクをけしてはダメ
				manager.getPool().pushForced(this);
				return 1;
			}
		}
		
		return 0;
	}
	
	/// 描画
	int onDraw(Object o) {
		Screen screen = cast(Screen) o;
		int rad = m_radSpeed.get();
		int px =  m_x + st.cos(rad, m_radiusInc.get() );
		
		Color4ub colorOrg = screen.getColor4ub();
		
		screen.setColor(m_r.get(),m_g.get(),m_b.get(),m_alpha.get());
//		screen.setColor(255,0,0,m_alpha.get());
		screen.bltRotate( m_texture, px, m_y + m_addY, 0, m_rate, 0);
		
		
		screen.setColor(colorOrg);
		return 0;
	}

	/// こいつは移動描画一括で動いているのだ...（なおさんと...
	int task(Object o) {
		return 0;
	}
	
	/// オブジェクトプールにはいるのでリセットする。
	void reset() {
		// 必要な処理を追記すること
		m_addY = 0;
		m_radiusInc.reset();
		m_alpha.reset();
		m_r.reset();
		m_g.reset();
		m_b.reset();
	}
	
	/// コンストラクタ
	this() {
		m_radSpeed = new RootCounter();
		m_radSpeed.set(0, 512, 1);
		m_radSpeed.opAssign( rand.get(512) );
		
		m_radiusInc = new RootCounterS();
		m_radiusInc.set(0,0,0);
		
		m_alpha = new RootCounterS();
		m_alpha.set(255,255,0);
		
		m_r = new InteriorCounter();
		m_r.set(255,255,1);
		m_g = new InteriorCounter();
		m_g.set(255,255,1);
		m_b = new InteriorCounter();
		m_b.set(255,255,1);
	}
	
	/// 静的コンストラクタ
	static this() {
		st = SinTable.get();
		rand = new Rand;
		rand.randomize();
	}

	
private:

	static SinTable st;
	static Rand rand;
	static const float LC_Z_MAX = 200.0f; //! 奥行きの消失位置
	static SpiralObjectManager manager;

	Texture m_texture;
	int m_x;	//!< 描画開始位置
	int m_y;	//!< 描画開始位置
	int m_z;	//!< 仮想奥行き
	float m_rate = 1.0f;	//!< テクスチャの拡大率
	float m_dy = 1.0f;		//!< 縦方向の移動量
	int m_addY;				//!< 縦方向移動合計値
	RootCounter m_radSpeed;	//!< 各速度
	RootCounterS m_radiusInc;//!< 回転半径増長カウンタ
	RootCounterS m_alpha;
	
	InteriorCounter m_r;
	InteriorCounter m_g;
	InteriorCounter m_b;
}

/**
	マネージャ
*/
class SpiralObjectManager {
	
	/// 描画位置設定
	void setXY(int x_, int y_) {
		this.m_x = x_;
		this.m_y = y_;
		foreach(inout SpiralObject s; pool) {
			s.setXY( this.m_x, this.m_y );
		}
	}
	void getXY(out int x_, out int y_) {
		x_ = this.m_x;
		y_ = this.m_y;
	}
	

	/// 初期化
	void init(uint n, int x_, int y_) {
		
		// 描画位置の設定
		setXY(x_, y_);
		
		// 球画像の読込
		this.texSphere = new Texture();
		this.texSphere.load(cast(char[]) "img/bookmark/sphere.png");
		
		pool.setMax(800);
		Color4ub start;
		Color4ub end;
		start.setColor(196, 0, 0);
		end.setColor(0,0,196);
		foreach(inout SpiralObject s; pool) {
			s = new SpiralObject;
			s.setTexture( this.texSphere );
			
			// TODO ここの値を変えて、動作を制御する
			s.setRadius(5, 150, 2);
			s.setRadSpeed(5);
			s.setDy(-4);
			s.setAlphaStep(64, 255, 2);
			s.setColorStep(start, end, 96);
			
			s.setXY( this.m_x, this.m_y );
		}
		
		// マネージャの設定
		SpiralObject.setManager(this);

		for(int i=0; i<n; ++i) {
			SpiralObject s = pool.pop();
			if (s) {
				controller.addTask(s,0);
			}
		}
	}
	

	/// 毎回呼び出すなり
	void onDraw(Screen screen) {
		this.controller.callTaskOnDraw(screen);
		
	}
	
	/// 移動処理
	void onMove(Screen screen) {
		float adGen = generate;
		if ( this.stopTime != -1 ) {
			if ( this.timer.get() >= stopTime ) {
				adGen = 0;
			} else {
				adGen = adGen - ( (cast(float)timer.get()/cast(float)stopTime ) * adGen);
			}
		}

		this.generatecount += adGen;
		if (generatecount > 1.0f) {
			for (int i=0; i<cast(int)generatecount; ++i) {
				SpiralObject s = pool.popForced();
				if (s is null) {
					Log.printWarn("poolfull!");
					break;
				}
				
				s.reset();
				
				if (this.controller.addTask(s,0)!=0) {
					Log.printWarn("task add false!\n");
				}
			}
			// 小数部は生かす
			this.generatecount = this.generatecount - cast(int) this.generatecount;
		}

		this.controller.callTaskOnMove(screen);

	}
	
	/// これを呼び出しいこう、新規オブジェクトを生成しない
	void stop(int n=-1) {
		this.timer = new Timer();
		this.timer.reset();
		this.stopTime = n;
	}
	
	/// 描画終了したか？
	bool isFinish() {
		return false;
	}
	
	/// オブジェクトの破棄
	void destroy() {
		this.pool.release();
		this.pool = null;
		this.controller = null;
		this.rand = null;
	}

	/// オブジェクトプールを取得する（デバッグ
	ObjectPool getPool() { return pool; }

	/// コンストラクタ
	this() {
		this.controller = new GameTaskController(1000);
		this.pool = new ObjectPool;
		this.rand = new Rand;
		rand.randomize();
	}
	

protected:
	ObjectPool pool;	//!< オブジェクトプール
	GameTaskController controller;	//!< タスクコントローラ
	Rand rand;		//!< ランダム
	Timer timer;	//!< 停止用タイマ
	int stopTime = -1;
	
	int m_x;	//!< この効果の描画位置
	int m_y;	//"< この効果の描画位置

	Texture texSphere;		// 雪テクスチャのローダ
	float generate = 0.8345f;	//!< フレーム間に生成する雪の数（等間隔はいやなので適当な端数をいれておく
	float generatecount = 0.0f;	//!< 生成する雪の数
}