module yamalib.headlinear;

private import std.stream;

private import y4d;
private import ytl.vector;
private import ytl.y4d_result;
private import y4d_draw.sprite;
private import y4d_aux.filesys;
private import y4d_aux.lineparser;

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

/**
 タイトルテロップ等での表題表示を行います
 */
abstract class Headlinear {
	alias int style;

	/// コンストラクタ
	this () {
		vx = new vector!(int);
		vy = new vector!(int);

		vt = new vector!(Texture);
		vAlpha = new vector!(RootCounterS);
		vRate = new vector!(InteriorCounter);

		vMoveX = new vector!(InteriorCounter);
		vMoveY = new vector!(InteriorCounter);

		vRotation = new vector!(InteriorCounter);
	}

	/// 表示スタイルを設定する
	void setStyle(style style_) {
		if (styleType != style_) {
			styleType = style_;
			init = false;
			finish = false;
		}
	}

	/// 最後尾から描画を始めるか
	void setReverse(bool b) {
		reverse = b;
	}

	/// 特殊描画処理のステップ数
	void setDisposeStep(int step_) {
		disposeStep = step_;
	}

	/// ロードされたテクスチャを追加します
	void addTexture(Texture t_) {
		vt.push_back(t_);

		// 属性も増やしとく
		vx.push_back(0);
		vy.push_back(0);
		vAlpha.push_back(new RootCounterS(0,255,1));
		vRate.push_back(new InteriorCounter());
		vMoveX.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
		vMoveY.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
		vRotation.push_back(new InteriorCounter());
		roteFinish ~= false;
	}

	/// 非推奨メソッド（Texture のコピーが発生します）
	/// テクスチャローダーからテクスチャを追加します
	void addTexture(TextureLoader tl_) {
		foreach(Texture t; tl_) {
			vt.push_back(t);

			// 属性も増やしとく
			vx.push_back(0);
			vy.push_back(0);
			vAlpha.push_back(new RootCounterS(0,255,1));
			vRate.push_back(new InteriorCounter());
			vMoveX.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vMoveY.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vRotation.push_back(new InteriorCounter());
			roteFinish ~= false;
		}
	}

	/// リストファイルからテクスチャを追加します
	y4d_result addTexture(char[] filename) {
		ubyte[] mem = cast(ubyte[])(FileSys.read(filename));
		if (!mem) return y4d_result.file_not_found; // 読み込みエラー
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		LineParser lp = new LineParser;
		while (!m.eof) {
			char[] linebuf = m.readLine();
			lp.setLine(linebuf);
			char[] imgFileName = lp.getStr();
			if (!imgFileName) continue; // ダメやん

			Texture t = new Texture;
			t.load( FileSys.makeFullName(imgFileName) );
			vt.push_back(t);

			// 属性も増やしとく
			vx.push_back(0);
			vy.push_back(0);
			vAlpha.push_back(new RootCounterS(0,255,1));
			vRate.push_back(new InteriorCounter());
			vMoveX.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vMoveY.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vRotation.push_back(new InteriorCounter());
			roteFinish ~= false;
		}

		return y4d_result.no_error;
	}

	/// 最初に定義されている SpriteVector を表示文字列都市
	/// それ以外は無視する
	void setSprite(Sprite.SpriteVectorVector svv_,int spriteNo) {
		if (spriteNo >= svv_.length) return;

		sv = svv_[spriteNo];

		for(int i; i<sv.length; ++i) {
			vx.push_back(0);
			vy.push_back(0);
			vAlpha.push_back(new RootCounterS(0,255,1));
			vRate.push_back(new InteriorCounter());
			vMoveX.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vMoveY.push_back(new InteriorCounter(0,0,1));	// 無効にしとく
			vRotation.push_back(new InteriorCounter());
		}
		useSprite = true;
	}

	/// 文字と文字との インターバルの設定
	/// これで総合的な描画速度が決まる
	void setInterval(int n) {
		interval = n;
	}
	
	/// 文字と文字のインバーバル間隔を取得する
	int getInterval() {
		return interval;
	}

	/// 移動量を設定します
	void setMoveXY(int x_,int y_) {
		moveX = x_;
		moveY = y_;
	}
	
	/// 移動量を取得します
	void getMoveXY(out int x_, out int y_) {
		x_ = moveX;
		y_ = moveY;
	}

	/// 余白を設定します
	void setMargin(int top,int left,int right,int bottom) {
		topMargin = top;
		leftMargin = left;
		bottomMargin = bottom;
		rightMargin = right;
	}
	
	/// 全オブジェクトの描画アルファを設定します
	void setAlphaCounterAll(int start, int end, int step) {
		
		foreach ( inout RootCounterS c; vAlpha) {
			c.set(start, end, step);
		}
	}

	/// 描画終了したか？
	bool isFinish() {
		return finish;
	}

	/// テクスチャと属性のクリア
	void clear() {
		vt.clear();
		vx.clear();
		vy.clear();
		vAlpha.clear();
		vRate.clear();
		vMoveX.clear();
		vMoveY.clear();
		vRotation.clear();
		useSprite = false;
		init = false;
		finish = false;
		drawCount = 0;
		moveX = 0;
		moveY = 0;
		topMargin = 0;
	 	leftMargin = 0;
	 	bottomMargin = 0;
	 	rightMargin = 0;
		sv = null;
		roteFinish = null;
	}

	/// 描画
	abstract void onDraw(Screen screen);

	/// 描画結果をもらう
	void onDraw(inout TextureVector tv) {
	}

	/// 連結後の仮想座標位置の取得
	void getXY(out int x_, out int y_) {
	}


protected:
	static final const float RATE_SCALER = 128.0F;	// スケーリングする値の定義

	bool init;
	bool finish;
	bool useSprite;	//!< スプライトで描画を行うか？
	bool reverse;

	// 表示スタイルの指定
	style styleType;

	int disposeStep;	//!< 処理が終了するまでのステップ
	int interval;		//!< 次の文字を表示するまでの休止フレーム
	int intervalCount;
	int drawCount;

	int moveX;	//!< 移動量X
	int moveY;	//!< 移動量X

	int topMargin;
	int leftMargin;
	int bottomMargin;
	int rightMargin;

	vector!(int) vx;	//!< 表示位置の列
	vector!(int) vy;
	vector!(RootCounterS) vAlpha;	//!< テクスチャのα値
	vector!(InteriorCounter) vRate;	//!< テクスチャの拡大レート値 RATE_SCALER でスケーリングしてある
	vector!(InteriorCounter) vMoveX;	//!< 移動X
	vector!(InteriorCounter) vMoveY;	//!< 移動Y
	vector!(InteriorCounter) vRotation;	//!< 回転角
	bool[] roteFinish;	//!< 各テクスチャの移動終了フラグ

	vector!(Texture) vt;	//!< 表示していくテクスチャ列
	Sprite.SpriteVector sv;	//!< 表示する文字列のスプライト情報

}

/**
 タイトルテロップ等での表題表示を行います
 不透明度をげながら拡大された状態から縮小された状態に移行します
 */
class ReduceHeadlinear : Headlinear {
	// 表示スタイルの定義
	enum Style : style  {LEFT,CENTER,RIGHT,TOP_LEFT,BOTTM_LEFT,TOP_RIGHT,BOTTOM_RIGHT};

	/// 表示位置を自分できめる
	void setXY(int x,int y) {
		posx = x;
		posy = y;
		useUserPos = true;
	}

	void setRandomMove(bool b) {
		randMove = b;
	}

	/// 縮小エフェクトフレーム数の設定
	void setEffectingFrame(int step) {
		if (0>=step) {
			step = 1;
		}
		rateStep = step;
	}
	
	/// フェードアウトするかどうか？
	void setFadeOut(bool b_) {
		fadeOut = true;
	}


	/// 表示開始時の拡大レート
	void setStartRate(float rate_) {
		startRate = rate_;
	}

	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		// 設定してからにしてくれ
		if (vt.size()==0 && sv==null) return;

		if (!init) {
			// スタイルから属性を設定する
			if (!useSprite) {
				onInit(screen);
			} else {
				onInitWithSprite(screen);
			}
		}

		if (!finish) {
			intervalCount++;
			if (intervalCount >= interval) {
				intervalCount = 0;
				drawCount++;
			}

			// インクリメント
			if (reverse) {
				int j;
				for(int i=vAlpha.size()-1; i>=0; --i) {
					if (j > drawCount) break;
					vAlpha[i]++;
					vRate[i]++;
					vMoveX[i]++;
					vMoveY[i]++;
					j++;
				}
			} else {
				for(int i=0; i<vAlpha.size(); ++i) {
					if (i > drawCount) break;
					vAlpha[i]++;
					vRate[i]++;
					vMoveX[i]++;
					vMoveY[i]++;
				}
			}
		}

		// 描画完了して、フェードアウトであれば描画することはない
		if ( !(finish && fadeOut) ) {
			if (!useSprite) {
				onDrawTextures(screen);
			} else {
				onDrawSprite(screen);
			}
		}

		// エフェクトおわったんか？
		if (!finish) {
			if (reverse) {
				for(int i; i<vAlpha.size(); ++i) {
					if (!vAlpha[i].isEnd() || !vRate[i].isEnd()
					   || !vMoveX[i].isEnd() || !vMoveX[i].isEnd()) {
						break;
					}
					if (fadeOut && !nowFade) {
						foreach(inout RootCounterS c; vAlpha) {
							c.set(255, 0, 4);
						}
						nowFade = true;
					} else {
						finish = true;
					}
				}
			} else {
				for(int i=vAlpha.size()-1; i>=0; --i) {
					if (!vAlpha[i].isEnd() || !vRate[i].isEnd()
					   || !vMoveX[i].isEnd() || !vMoveX[i].isEnd()) {
						break;
					}
					
					if (fadeOut && !nowFade) {
						foreach(inout RootCounterS c; vAlpha) {
							c.set(255, 0, 4);
						}
						nowFade = true;
					} else {
						finish = true;
					}
				}
			}
		}
	}

	/// 結果をTextureVectorに出力します
	void onDraw(inout TextureVector tv) {
		if (finish) {
			int i,x,y;
			foreach(inout Texture t; vt) {
				// 座標の中心に描画されとる
				x = vx[i]+cast(int)t.getWidth()/2;
				y = vy[i]+cast(int)t.getHeight()/2;
				tv.add(t,x,y);
				i++;
			}
		}
	}

	/// 表示位置の取得
	override void getXY(out int x_, out int y_) {
		if (finish) {
			int i,x,y,minX=int.max,minY=int.max;
			if (!useSprite) {
				foreach(inout Texture t; vt) {
					// 座標の中心に描画されとる
					x = vx[i]-cast(int)t.getWidth()/2;
					y = vy[i]-cast(int)t.getHeight()/2;
					if (x<minX) {
						minX = x;
					}
					if (y<minY) {
						minY = y;
					}
					i++;
				}
			} else {
				foreach(inout SimpleSprite s;sv) {
					// 座標の中心に描画されとる
					x = vx[i];//-cast(int)s.rcRect.getWidth()/2;
					y = vy[i];//-cast(int)s.rcRect.getHeight()/2;
					if (x<minX) {
						minX = x;
					}
					if (y<minY) {
						minY = y;
					}
					i++;
				}
			}
			x_ = minX;
			y_ = minY;
		}
	}

	// 初期化
	override void clear() {
		Headlinear.clear();
		useUserPos = false;
	}


	/// コンストラクタ
	this() {
		super();
		rand = new Rand;
		rand.randomize();
		startRate = 5;	// 3ぐらい？
		rateStep = 128;	// 128で拡大終了
	}

private:

	/// テクスチャの列から描画する
	void onDrawTextures(Screen screen) {
		// 描画
		int i;
		int x;
		int y;
		int alpha;
		float rate = 1.0f;

		screen.blendSrcAlpha();

		if (!finish) {
			if (reverse) {
				int j;
				for (int ii = vt.size()-1; ii>=0; --ii) {
					if (j > drawCount) break;

					int offsetx = vMoveX[i].get();
					int offsety = vMoveY[i].get();

					x = vx[i] + offsetx;
					y = vy[i] + offsety;
					alpha = vAlpha[i].get();
					rate = (vRate[i].get()/RATE_SCALER);	// スケーリング戻し

					screen.setColor(255,255,255,alpha);
					screen.bltRotate(vt[i],x,y,0,rate,4);	// 座標の中心に描画
					j++;
				}
			} else {
				foreach(inout Texture t; vt) {
					if (i > drawCount) break;

					int offsetx = vMoveX[i].get();
					int offsety = vMoveY[i].get();

					x = vx[i] + offsetx;
					y = vy[i] + offsety;
					alpha = vAlpha[i].get();
					rate = (vRate[i].get()/RATE_SCALER);	// スケーリング戻し

					screen.setColor(255,255,255,alpha);
					screen.bltRotate(t,x,y,0,rate,4);	// 座標の中心に描画
					i++;
				}
			}
		} else {
			foreach(inout Texture t; vt) {
				x = vx[i];
				y = vy[i];
				screen.setColor(255,255,255);
				screen.bltRotate(t,x,y,0,1.0f,4);	// 座標の中心に描画
				i++;
			}
		}

		screen.resetColor();

	}



	void onDrawSprite(Screen screen) {

		// 描画
		int x;
		int y;
		int alpha;
		float rate = 1.0f;

		screen.blendSrcAlpha();

		if (!finish) {
			if (reverse) {
				int i,j;
				for (i = sv.length-1; i>=0; --i) {
					if (j > drawCount) break;

					int offsetx = vMoveX[i].get();
					int offsety = vMoveY[i].get();

					x = vx[i] + offsetx;
					y = vy[i] + offsety;
					alpha = vAlpha[i].get();
					rate = (vRate[i].get()/RATE_SCALER);	// スケーリング戻し

					screen.setColor(255,255,255,alpha);
					screen.bltRotate(sv[i].texture,x,y,&sv[i].rcRect,0,rate,0,0);	// 描画
					j++;
				}
			} else {
				foreach(int i,inout SimpleSprite s; sv) {
					if (i > drawCount) break;

					int offsetx = vMoveX[i].get();
					int offsety = vMoveY[i].get();

					x = vx[i] + offsetx;
					y = vy[i] + offsety;
					alpha = vAlpha[i].get();
					rate = (vRate[i].get()/RATE_SCALER);	// スケーリング戻し

					screen.setColor(255,255,255,alpha);
					screen.bltRotate(s.texture,x,y,&s.rcRect,0,rate,0,0);	// 描画
				}
			}
		} else {
			foreach(int i,inout SimpleSprite s; sv) {
				x = vx[i];
				y = vy[i];
				screen.setColor(255,255,255);
				screen.blt(s.texture,x,y,&s.rcRect);	// 描画
			}
		}

		screen.resetColor();
	}

	/// 初期化処理
	void onInit(Screen screen) {
		init = true;
		drawCount = 0;

		// 画面サイズ
		int screenX = screen.getWidth();
		int screenY = screen.getHeight();

		int size = vt.size();	// テクスチャの数
		int sumWidth;	// 全テクスチャの連結幅
		int maxHeight;	// 一番高いテクスチャの高さ
		int maxWidth;	// 一番幅が広いテクスチャの幅

		foreach(inout Texture t;vt) {
			int height;
			int width;
			width = cast(int)t.getWidth();
			height = cast(int)t.getHeight();

			sumWidth += width;

			if (maxWidth < width) {
				maxWidth = width;
			}

			if (maxHeight < height) {
				maxHeight = height;
			}
		}

		// そのテクスチャのサイズ
		int tWidth;
		int tHeight;
		// 表示位置
		int x,y;
		int offsetX,offsetY;
		int adjustX,adjustY;

		switch (styleType) {
		case Style.LEFT:	// 左寄せ
			{
				int i;
				offsetX = leftMargin;
				offsetY = (screenY-maxHeight)/2;

				foreach (inout Texture t;vt) {
					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.CENTER:	// センタリング
			{
				int i;

				if (useUserPos) {
					offsetX = posx;
					offsetY = posy;
				} else {
					offsetX = (screenX-sumWidth)/2;//+(sumWidth/vt.size())/2;
					offsetY = (screenY-maxHeight)/2;
				}
				foreach (inout Texture t;vt) {
					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					vMoveX[i].set(moveX,0,rateStep);
					vMoveY[i].set(moveY,0,rateStep);

					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.RIGHT:	// 右寄せ
			{
				int i;
				offsetX = (screenX-rightMargin)+sumWidth;
				offsetY = (screenY-maxHeight)/2;

				foreach (inout Texture t;vt) {
					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.TOP_LEFT:	// 左上寄せ
			{
				int i;
				offsetX = (maxWidth/2)+leftMargin;
				offsetY = topMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの高さを求める
				for(i=0; i<turnPos; ++i) {
					offsetY += cast(int)vt[i].getHeight();
				}

				// 描画位置はbottomではないので、戻しておく
				offsetY -= cast(int)vt[0].getHeight();

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetX += tWidth;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetY -= tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						x = vx[i] = offsetX + adjustX;
						y = vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.BOTTM_LEFT:	// 左下寄せ
			{
				int i;
				offsetX = (maxWidth/2)+leftMargin;
				offsetY = screenY - bottomMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの高さを求める
				for(i=0; i<turnPos; ++i) {
					offsetY -= cast(int)vt[i].getHeight();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetX += tWidth;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetY += tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						x = vx[i] = offsetX + adjustX;
						y = vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.TOP_RIGHT:	// 右上寄せ
			{
				int i;
				offsetX = (screenX-rightMargin) + (maxWidth/2);
				offsetY = topMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの幅を求める
				for(i=0; i<turnPos; ++i) {
					offsetX -= cast(int)vt[i].getWidth();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetY += tHeight;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetY += tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						vx[i] = offsetX + adjustX;
						vy[i] = offsetY;


						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.BOTTOM_RIGHT:	// 右下寄せ
			{
				int i;
				offsetX = (screenX-rightMargin) + (maxWidth/2);
				offsetY = (screenY-bottomMargin) - (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの幅を求める
				for(i=0; i<turnPos; ++i) {
					offsetX -= cast(int)vt[i].getWidth();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetY -= tHeight;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetY -= tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						vx[i] = offsetX + adjustX;
						vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		default:
			Log.printError("Headlinear.onDraw:Invalid Style!\n");
			break;
		}
	}

	void onInitWithSprite(Screen screen) {
		init = true;
		drawCount = 0;

		// 画面サイズ
		int screenX = screen.getWidth();
		int screenY = screen.getHeight();

		int size = sv.length;	// テクスチャの数
		int sumWidth;	// 全テクスチャの連結幅
		int maxHeight;	// 一番高いテクスチャの高さ
		int maxWidth;	// 一番幅が広いテクスチャの幅

		foreach(inout SimpleSprite s;sv) {
			int height;
			int width;
			width = cast(int)s.rcRect.getWidth();
			height = cast(int)s.rcRect.getHeight();

			sumWidth += width;

			if (maxWidth < width) {
				maxWidth = width;
			}

			if (maxHeight < height) {
				maxHeight = height;
			}
		}

		// そのテクスチャのサイズ
		int tWidth;
		int tHeight;
		// 表示位置
		int x,y;
		int offsetX,offsetY;
		int adjustX,adjustY;

		switch (styleType) {
		case Style.LEFT:	// 左寄せ
			{
				int i;
				offsetX = leftMargin;
				offsetY = (screenY-maxHeight)/2;

				foreach (inout Texture t;vt) {
					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.CENTER:	// センタリング
			{
				int i;

				if (useUserPos) {
					offsetX = posx;
					offsetY = posy;
				} else {
					offsetX = (screenX-sumWidth)/2+(sumWidth/size)/2;
					offsetY = (screenY-maxHeight)/2;
				}
				foreach (inout SimpleSprite s;sv) {
					tWidth = cast(int)s.rcRect.getWidth();
					tHeight = cast(int)s.rcRect.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					int mx,my;

					if (randMove) {
						if (moveX) mx = rand.get(moveX);
							if (rand.get1()) mx = -mx;
						if (moveY) my = rand.get(moveY);
							if (rand.get1()) my = -mx;
					}else {
						if (i<=size/2) {
							mx = moveX * ((size/2)-i)/(size/2);
						} else {
							mx = moveX * ((size/2)-i)/(size/2);
						}
						my = moveY;
					}

					vMoveX[i].set(mx,0,rateStep);
					vMoveY[i].set(my,0,rateStep);
					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.RIGHT:	// 右寄せ
			{
				int i;
				offsetX = (screenX-rightMargin)+sumWidth;
				offsetY = (screenY-maxHeight)/2;

				foreach (inout Texture t;vt) {
					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					// 一番最初は加算しなくてええよ～
					if (i>0) {
						offsetX += tWidth;
					}

					if (tHeight != maxHeight) {
						adjustY = (maxHeight-tHeight)/2;
					} else {
						adjustY = 0;
					}

					vx[i] = offsetX;
					vy[i] = offsetY + adjustY;

					// カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.TOP_LEFT:	// 左上寄せ
			{
				int i;
				offsetX = (maxWidth/2)+leftMargin;
				offsetY = topMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの高さを求める
				for(i=0; i<turnPos; ++i) {
					offsetY += cast(int)vt[i].getHeight();
				}

				// 描画位置はbottomではないので、戻しておく
				offsetY -= cast(int)vt[0].getHeight();

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetX += tWidth;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetY -= tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						x = vx[i] = offsetX + adjustX;
						y = vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.BOTTM_LEFT:	// 左下寄せ
			{
				int i;
				offsetX = (maxWidth/2)+leftMargin;
				offsetY = screenY - bottomMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの高さを求める
				for(i=0; i<turnPos; ++i) {
					offsetY -= cast(int)vt[i].getHeight();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetX += tWidth;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetY += tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						x = vx[i] = offsetX + adjustX;
						y = vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.TOP_RIGHT:	// 右上寄せ
			{
				int i;
				offsetX = (screenX-rightMargin) + (maxWidth/2);
				offsetY = topMargin + (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの幅を求める
				for(i=0; i<turnPos; ++i) {
					offsetX -= cast(int)vt[i].getWidth();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetY += tHeight;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetY += tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						vx[i] = offsetX + adjustX;
						vy[i] = offsetY;


						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		case Style.BOTTOM_RIGHT:	// 右下寄せ
			{
				int i;
				offsetX = (screenX-rightMargin) + (maxWidth/2);
				offsetY = (screenY-bottomMargin) - (maxHeight/2);

				// 折り返し位置
				int turnPos = vt.size()/2;

				// 折り返し地点までの幅を求める
				for(i=0; i<turnPos; ++i) {
					offsetX -= cast(int)vt[i].getWidth();
				}

				i = 0;
				foreach (inout Texture t;vt) {
					if (i==turnPos) {
						offsetY -= tHeight;
					}

					tWidth = cast(int)t.getWidth();
					tHeight = cast(int)t.getHeight();

					if (i<turnPos) {
						if (i>0) {
							offsetX += tWidth;
						}

						if (tHeight != maxHeight) {
							adjustY = (maxHeight-tHeight)/2;
						} else {
							adjustY = 0;
						}

						vx[i] = offsetX;
						vy[i] = offsetY + adjustY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					} else {
						// 折り返し最初は加算しなくてええよ～
						if (i!=turnPos) {
							offsetY -= tHeight;
						}

						if (tWidth != maxWidth) {
							adjustX = (maxWidth-tWidth)/2;
						} else {
							adjustX = 0;
						}

						vx[i] = offsetX + adjustX;
						vy[i] = offsetY;

						vMoveX[i].set(moveX,0,rateStep);
						vMoveY[i].set(moveY,0,rateStep);
					}

					// 基本カウンタの設定
					setBasicCounter(i);
					++i;
				}
			}
			break;
		default:
			Log.printError("Headlinear.onDraw:Invalid Style!\n");
			break;
		}
	}

	/// 基本カウンタを設定する
	void setBasicCounter(int index) {
		// ちゃんと設定しとけー
		if (vAlpha.size() <= index) return;

		int step = 255/rateStep;
		if ((255%rateStep)!=0) {
			step++;
		}

		if (step<=0) {
			step = 1;
		}

		// アルファカウンタ
		vAlpha[index].set(0,255,step);

		// 拡大レートカウンタ（スケーリング
		int startRate = cast(int)(startRate*RATE_SCALER);
		int endRate = cast(int)(1*RATE_SCALER);
		vRate[index].set(startRate,endRate,rateStep);
	}

private:
	Rand rand;
	float startRate;
	bool fadeOut;
	bool nowFade;

	int alphaStep;
	int rateStep;

	bool randMove;	//!< 指定されているmoveX,moveYをランダムに設定するか？
	bool useUserPos;
	int posx = int.min;
	int posy = int.min;
}

/**
 タイトルテロップ等での表題表示を行います
 不透明度をさげながらなめらかにスライドインしてから定常描画します
 */
class SlideInHeadlinear : Headlinear {

	/// 表示位置の設定
	void setXY(int x_,int y_) {
		x = x_;
		y = y_;
	}

	/// 処理完了後にフェードアウトさせるか
	void setFadeOut(bool b_) {
		fadeOut = b_;
	}

	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		try {
			// 設定してからにしてくれ
			if (vt.size()==0) return;
	
			if (!init) {
				onInit(screen);
				init = true;
			}
	
//			int	i = 0;
			int posx,posy;
			if (!finish) {
				intervalCount++;
				if (intervalCount >= interval) {
					intervalCount = 0;
					drawCount++;
				}
	
				// インクリメント
				int i,j;
				if (reverse) {
					for (i = vAlpha.size-1; i >= 0; --i) {
						if (j > drawCount) break;
	
						vAlpha[i]++;
						vRotation[i]++;
	
						// フェードアウト処理設定
						if (fadeOut && vRotation[i].isEnd() && !roteFinish[i]) {
							roteFinish[i] = true;
							int alphaNow = vAlpha[i].get();
							vAlpha[i].set(alphaNow,0,2);
						}
						j++;
					}
	
					// 描画
					for (i = vAlpha.size()-1; i >= 0; --i) {
						if (j > drawCount) break;
	
						screen.setColor(255,255,255,vAlpha[i].get());
	
						posx = vx[i] + sin.sin(vRotation[i].get(),moveX);
						posy = vy[i] + sin.sin(vRotation[i].get(),moveY);
	
						screen.blt(vt[i],posx,posy);
						j++;
					}
	
				} else {	// !reverse
	
					// 各カウンタの増分
					for (i=0; i<vAlpha.size(); ++i) {
						if (i > drawCount) break;
	
						vAlpha[i]++;
						vRotation[i]++;
	
						// フェードアウト処理設定
						if (fadeOut && vRotation[i].isEnd() && !roteFinish[i]) {
							roteFinish[i] = true;
							int alphaNow = vAlpha[i].get();
							vAlpha[i].set(alphaNow,0,2);
						}
					}
	
					// 描画
					foreach (int j, inout Texture t; vt) {
						if (j > drawCount) break;
	
						screen.setColor(255,255,255,vAlpha[j].get());
	
						posx = vx[j] + sin.sin(vRotation[j].get(),moveX);
						posy = vy[j] + sin.sin(vRotation[j].get(),moveY);
			
						screen.blt(t, posx, posy);
					}
				}
			} else if (!fadeOut) {
				// 定常描画
				int i = 0;
				foreach (inout Texture t; vt) {
					posx = vx[i] + moveX;
					posy = vy[i] + moveY;
					screen.blt(t,posx,posy);
					i++;
				}
			}
	
			// エフェクトおわったんか？
			for(int i = vAlpha.size()-1; i >= 0; --i) {
				if (!vAlpha[i].isEnd()) {
					break;
				}
				finish = true;
			}
	
			screen.resetColor();
		} catch (Exception e) {
			Log.printFatal("%s#onDraw : [%s]", this.toString(), e.toString());
		}
	}

	/// 結果をTextureVectorに出力します
	void onDraw(inout TextureVector tv) {
		if (finish) {
			int i,posx,posy;
			foreach (inout Texture t; vt) {
				posx = vx[i] + moveX;
				posy = vy[i] + moveY;
				tv.add(t,posx,posy);
				i++;
			}
		}
	}

	/// 描画位置の取得
	override void getXY(out int x_, out int y_) {
		if (finish) {
			int i,posx,posy,minX=int.max,minY=int.max;
			foreach (inout Texture t; vt) {
				posx = vx[i] + moveX;
				posy = vy[i] + moveY;
				if (posx<minX) {
					minX = posx;
				}
				if (posy<minY) {
					minY = posy;
				}
				i++;
			}
			x_ = minX;
			y_ = minY;
		}
	}

	/// コンストラクタ
	this() {
		super();
		this.sin = SinTable.get();
	}
	

private:

	/// 初期化処理
	void onInit(Screen screen) {
		int size = vt.size();	// テクスチャの数
		int sunWidth;	// 全テクスチャの連結幅
		int maxHeight;	// 一番高いテクスチャの高さ
		int maxWidth;	// 一番幅が広いテクスチャの幅

		foreach(inout Texture t; vt) {
			int height = int.min;
			int width = int.min;
			width = cast(int) t.getWidth();
			height = cast(int) t.getHeight();

			sunWidth += width;

			if (maxWidth < width) {
				maxWidth = width;
			}

			if (maxHeight < height) {
				maxHeight = height;
			}
		}

		int tWidth = 0;
		int tHeight;
		int offsetX = x;
		int offsetY = y;
		int adjustY;

		foreach (int i, inout Texture t; vt) {

			// 一つ前のテクスチャの幅に合わせてずらしていく
			offsetX += tWidth;

			tWidth = cast(int) t.getWidth();
			tHeight = cast(int) t.getHeight();

			if (tHeight != maxHeight) {
				adjustY = (maxHeight-tHeight)/2;
			} else {
				adjustY = 0;
			}

			vx[i] = offsetX;
			vy[i] = offsetY + adjustY;

			vAlpha[i].set(0, 255, 3);

			vRotation[i].set(0, 128, disposeStep);
		}

	}

private:
	SinTable sin;	//!< 移動量制御用

	int x;	//!< 描画位置X
	int y;	//!< 描画位置Y
	bool fadeOut;	//!< フェードアウトさせるか
}

class SequentialDrawAlpha : Headlinear {
	
	/// 描画位置の設定
	void setXY(int x_, int y_) {
		x = x_;
		y = y_;
	}
	
	/// 描画位置の取得
	void getXY(out int x_, out int y_) {
		x_ = x;
		y_ = y;
	}
	
	/// 縮小エフェクトフレーム数の設定
	void setEffectingFrame(int step) {
		if (0>=step) {
			step = 1;
		}
		rateStep = step;
	}


	/// 表示開始時の拡大レート
	void setStartRate(float rate_) {
		startRate = rate_;
	}
		
	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		
		if ( !init ) {
			onInit( screen );
			init = true;
		}
		
		if ( ( drawCount < vt.size() ) && ( intervalCount++ >= interval ) ) {
			intervalCount = 0;
			drawCount++;
		}
		
		foreach(int i, inout Texture t; vt) {
			
			if (drawCount == i ) break;

			if ( !roteFinish[i] ) {
				vRate[i]++;
				vAlpha[i]++;
				
				float fRate = cast(float) vRate[i].get() / RATE_SCALER;
				screen.setColor( 255, 255, 255, vAlpha[i].get() );
				screen.bltRotate(t, x, y, 0, fRate, 4);	// 描画
				
				if (vRate[i].isEnd() && vAlpha[i].isEnd() ) {
					roteFinish[i] = true;
				}
			}
		}
		
		int i = 0;
		for (i = 0; i < roteFinish.length; ++i) {
			if (!roteFinish[i]) break;
		}
		
		if (i == roteFinish.length) {
			finish = true;
		}
			
	}
	
	/// コンストラクタ
	this() {
		super();
		init = false;
		startRate = 0.0f;
	}
	
	
private :

	/// 初期化処理
	void onInit(Screen screen) {
		// テクスチャの数
		int size = vt.size();
		
		for (int i = 0; i < size; ++i) {
			// α値
			vAlpha[i].set(200, 0, 5);
			vRate[i].set( cast(int) (startRate * RATE_SCALER), cast(int) (0.8f * RATE_SCALER), rateStep );
		}
	}
	
private :
	int x;
	int y;
	bool init;
	
	int rateStep;
	float startRate;
}

