module yamalib.draw.dropobject;

private import SDL;

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


private import yamalib.log.log;

alias objectPool!(DropObject) ObjectPool;


/// 雪一つをあらわすクラス
class DropObject : GameTaskBase {

	alias objectPool!(DropObjectInfo) SnowInfoPool;

	/// 落下オブジェクトの属性情報
	static class DropObjectInfo {
		int x,y,alpha;
		float depth = 1.0f;		//!< 奥行き

		/// 属性設定
		void set(int x,int y,int alpha,float depth) {
			this.x = x;
			this.y = y;
			this.alpha = alpha;
			this.depth = depth;
		}

		/// コンストラクタ
		this() { }
		
		/// 属性設定コンストラクタ
		this(int x,int y,int alpha,float depth) {
			this();
			set(x,y,alpha,depth);
		}
	}

	/// アルファフィルタの設定
	static void setAlphaFilter(Surface surface, int ox, int oy) {
		alphaFilter = surface;
		filter_ox = ox;
		filter_oy = oy;
	}

	/// 移動( not imprement )
	int onMove(Object o) {
		return 0;
	}
	
	/// 描画( not imprement )
	int onDraw(Object o) {
		return 0;
	}

	/// こいつは移動描画一括で動いているのだ...（なおさんと...
	int task(Object o) {
		// 前に死んでいたらここで殺す
		Screen screen = cast(Screen)o;
		screen.blendAddColorAlpha();

		// 角属性配列の要素数はすべて同じ長さである
		assert(count.length == anglespeed.length && count.length == amplitude.length);
		
		for (int i; i < count.length; ++i) {
			count[i] += anglespeed[i];
			
			if (count[i] > 512) count[i] = 0.0f;

			float ox = amplitude[i] * 128;
			float rx = 0.0f;
			if (x_rand != 0) {
				rx = rand.get( cast(uint)( x_rand * 128) ) / 128.0f;
			}
			x += st.sin( cast(int)count[i], cast(int)(ox * depth) ) / 128.0f;	// 振動
			x += rx - x_rand / 2.0;		// ランダム性
		}

		y += dropspeed * depth;		// 落下

		if (y > screen.getHeight) {
			// 自殺する
			// まだタスクをけしてはダメ
			manager.getPool().pushForced(this);
			return 1;
		}

		screen.setColor(255, 255, 255, 
			cast(int) (alpha*getFilteredAlhaRate(cast(int) x, cast(int) y) ) );
		screen.bltRotate(snow, cast(int)x, cast(int)y, 0, depth, 4);

		return 0;
	}

	/// 雨のスタンダードな設定
	void setRain() {
		static float[] amp = [0.0f];
		static float[] angle = [0.0f];
		
		setDropSpeed(10.0);
		setDepth( (rand.get(10)+5) / 10.0f );
		setRadAttribute( angle, amp );
		setRandX(0);
	}

	/// 雪のスタンダードな設定
	void setSnow() {
		setDropSpeed(1.0f);
		setDepth( (rand.get(10)+5) / 10.0f );
		setRandX(0.0);
		
		static float[] amp = [0.2f, 0.1f, 0.1f];
		static float[] angle = [3.5f, 1.0f, 0.5f];
		setRadAttribute( angle, amp );
	}

	/// 初期位置の設定
	void initPos() {
		int x = rand.get(640);
		if (dropspeed>0.0f) {
			setXY(x,0);
		} else {
			setXY(x,480);
		}
	}
	/// 雪テクスチャのセット
	void setTexture(Texture t) { snow = t; }

	/// 落下速度（重さは関係しない絶対値）
	void setDropSpeed(float f) { dropspeed = f; }
	/// 描画奥行きとなる拡大レート
	void setDepth(float f) {depth = f;}

	/// 描画位置の設定
	void setXY(float x, float y) { this.x = x; this.y = y; }
	void getXY(out float x,out float y) { x = this.x; y = this.y; }

	/// 角速度、振幅の設定(複数波形の合成値として表現する)
	void setRadAttribute(float[] angleSpeed_, float[] amplitude_) {
		assert( !(angleSpeed_ is null) && !( amplitude_ is null) );
		anglespeed = angleSpeed_;
		amplitude = amplitude_;
		
		//  カウンタの初期化
		count.length = 0;
		for (int i; i < angleSpeed_.length; ++i) {
			count ~= rand.get(512);
		}
	}

	/// オブジェクトの重さを設定（未使用）
	void setWeight(uint n) { weight = n; }
	/// 振幅ランダム幅の設定
	void setRandX(float f) { x_rand = f; }

	/// マネージャの参照をセット
	void setManager(DropObjectManager v) {
		manager = v;
	}

	/// コンストラクタ
	this() {
		alpha = 128;
	}
	
	/// 静的コンストラクタ
	static this() {
		st = SinTable.get();
		rand = new Rand;
		rand.randomize();
		sip = new SnowInfoPool;
		sip.setMax(500);
		foreach(inout DropObjectInfo si; sip) {
			si = new DropObjectInfo;
		}
	}

private:
	static float getFilteredAlhaRate(int x, int y) {
		if (alphaFilter is null) {
			return 1.0f;
		}
		
		int width = cast(int) alphaFilter.getWidth();
		int height = cast(int) alphaFilter.getHeight();
		// アルファ有効32bitデータ
		SDL_Color* pixels = cast(SDL_Color*) alphaFilter.getPixels();
		return pixels[ (y*width + x) ].unused / 255.0f;
	}

protected:
	static SinTable st;
	static Rand rand;
	static SnowInfoPool sip;		//!< オブジェクト描画値
	static Surface alphaFilter;
	static int filter_ox;
	static int filter_oy;

	Texture snow;
	float x,y;				//!< 描画位置
	int	alpha;				//!< 描画α値
	float dropspeed = 1.0f;	//!< 落下速度
	float depth = 1.0f;		//!< 深さ
	float[] amplitude;	//!< 振幅の配列
	float[] count;		//!< 現在の角度
	float[] anglespeed;	//!< 振動の角速度
	uint weight;			//!< 物体の重さ
	float x_rand = 1.5f;	//!< 振幅に加算するランダム幅

	DropObjectManager manager;
}

/// 雪の落下を管理するクラス
class DropObjectManager {
	// 雪タスクのオブジェクトプール
	this() {
		controller = new GameTaskController(2000);
		pool = new ObjectPool;
		rand = new Rand;
		tl = new TextureLoader;
		tl.loadDefRW(cast(char[]) "img/common/snow.png \n img/common/rain.png");
	}

	/// 初期化
	void init(uint n) {
		pool.setMax(800);
		foreach(inout DropObject s;pool) {
			s = new DropObject;
			if (snow) {
				s.setTexture(tl.get(0));
				s.setSnow();
			} else if (rain) {
				s.setTexture(tl.get(1));
				s.setRain();
			}
			s.setXY(rand.get(640),rand.get(480));
			s.setManager(this);
		}

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

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

		generatecount += adGen;
		if (generatecount > 1.0f) {
			for (int i=0; i<cast(int)generatecount; ++i) {
				DropObject s = pool.popForced();
				if (s is null) {
					Log.printWarn("poolfull!");
					break;
				}
				if (snow) {
					s.setSnow();
					s.initPos();
				} else if(rain) {
					s.setRain();
					s.initPos();
				} else {
					// TO DO
				}

				if (controller.addTask(s,0)!=0) {
					Log.printWarn("task add false!\n");
				}
			}
			// 小数部は生かす
			generatecount = generatecount - cast(int)generatecount;
		}

		controller.callTask(screen);

/*		GameTaskList tl = controller.getTaskList();
		foreach (inout GameTaskBase gt; tl) {
			DropObject s = (DropObject)gt;
			if (s.isDeath()) {
				pool.push(s);
			}
		}
*/
	}
	
	/// アルファフィルタの設定
	void setAlphaFilter(Surface surface, int offsetx, int offsety) {
		DropObject.setAlphaFilter(surface, offsetx, offsety);
	}
	
	/// これを呼び出しいこう、新規オブジェクトを生成しない
	void stop(int n=-1) {
		timer = new Timer();
		timer.reset();
		stopTime = n;
	}
	
	/// 描画終了したか？
	bool isFinish() {
		return false;
	}

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

	/// 雨のお手軽設定
	void presetRain() {
		rain = true;
		snow = false;
		generate = 5.5345f;
	}

	/// 雪のお手軽設定
	void presetSnow() {
		snow = true;
		rain = false;
		generate = 0.5345f;
	}
	
	/// デストラクタ
	~this() {
		if (!(m_alphaFilter is null)) {
			m_alphaFilter.release();
		}
	}

protected:
	ObjectPool pool;	//!< オブジェクトプール
	GameTaskController controller;	//!< タスクコントローラ
	Rand rand;	// ランダム
	TextureLoader tl;		// 雪テクスチャのローダ
	float generate = 0.5345f;	//!< フレーム間に生成する雪の数（等間隔はいやなので適当な端数をいれておく
	float generatecount = 0.0f;	//!< 生成する雪の数
	Timer timer;
	int stopTime = -1;
	
	Surface m_alphaFilter;

	bool snow;
	bool rain;
}

/** 
	環境風をエミュレートする
*/
class EnvironmentWind {
	
	/// 対象に影響を与える
	void onMove(Object o) {
		
		
	}
	
	/// コンストラクタ
	this() {
		rand = new Rand();
		rand.randomize();
	}

protected:
	static Rand rand;

	float strongX;	//!< 風の強さX
	float strongY;	//!< 風の強さY
	float strongZ;	//!< 風の強さZ
	
}
