module yamalib.draw.spreadstring;

private import y4d_draw.drawbase;
private import y4d_draw.screen;
private import y4d_draw.fontloader;
private import y4d_draw.fontrepository;
private import y4d_draw.texturevector;

private import y4d_math.sintable;

private import yamalib.counterfsp;

/** 横に字間を広げながら描画する演出描画クラス */
public class SpreadString {
public:
	enum BASE_POS {LEFT, CENTER, RIGHT};

	/// FontLoader を動的につくるヘルパ (外部にもってくことも検討してちょ)
	static FontLoader createFontLoader (char[] fontFile, uint fontSize, uint fontIndex) {
		auto fontloader = new FontLoader();
		fontloader.loadDefRW( createFontLoaderDefStr(fontFile, 0, fontSize, fontIndex) );
		return fontloader;
	}

	/// FontRepository をつくるヘルパ (外部にもってくことも検討してちょ)
	static FontRepository setupFontRepository(FontLoader fontloader, int fontNo, int max) {
		auto fr = new FontRepository;
		fr.setLoader(fontloader,fontNo);
		fr.setMax(max);
		return fr;
	}
	
	/// コンストラクタ
	this(FontRepository fontRepository_) {
		assert(fontRepository_ !is null);
		fontRepository = fontRepository_;
	}
	
	/// 描画する文字列を設定する
	void setStringUnicode(wchar[] str) {
		assert(str !is null);
		drawString = str;
		textureVec = fontRepository.getTexture(str);
	}
	wchar[] getStringUnicode() {
		return drawString;
	}
	
	/// 拡大率を設定
	void setRate(float rate_) {
		rate = rate_ ;
	}
	float getRate() {
		return rate;
	}
	
	void setRad(int rad_) {
		rad = rad_ % 512;
	}
	int gtRad() {
		return rad;
	}
	
	/// アルファを設定
	void setAlpha(int alpha_) {
		alpha = alpha_;
	}
	int getAlpha() {
		return alpha;
	}
	
	/// 字間カウンターの設定
	void setSpaceCounter(IDecimalCounter spaceCounter_) {
		assert(spaceCounter_ !is null);
		spaceCounter = spaceCounter_;
	}
	IDecimalCounter getSpaceCounter() {
		return spaceCounter;
	}
	
	void onMove(Screen screen) {
		if (spaceCounter is null || textureVec is null) {
			return;
		}
		spaceCounter.inc();
	}
	
	/// 現在の設定で描画する
	void onDraw(Screen screen, int x, int y, BASE_POS base) {
		if (spaceCounter is null || textureVec is null) {
			return;
		}
		// オリジナルカラー保存
		Color4ub colorOrg = screen.getColor4ub();

		// アルファ設定
		screen.setColor(colorOrg.r, colorOrg.g, colorOrg.b, alpha);
		float addWidth = 0;
		float spaceRate = spaceCounter.getDecimal();
		
		switch (base) {
		case BASE_POS.LEFT:
			foreach (i, textureInfo; textureVec.getTextureInfo()) {
				screen.bltRotate(textureInfo.texture,
					cast(int) ((x + textureInfo.posX + addWidth)), y, 0, rate, 0);
				addWidth += (spaceRate * rate);
			}
			break;
		case BASE_POS.CENTER:
			if (0 == rad) {
				drawBaseCenter(screen, x, y);
			} else {
				drawBaseCenterRad(screen, x, y);
			}
			break;
		case BASE_POS.RIGHT:
			break;
		}
	
		// カラーもどし
		screen.setColor(colorOrg);
	}

	/// 状態を初期化	
	void reset() {
		spaceCounter.reset();
		drawString = null;
	}
	
private:
	static char[] createFontLoaderDefStr(char[] fontFile, uint fontNo, uint fontSize, 
		uint fontIndex) {
		return fontFile ~ "," ~ std.string.toString(fontNo) ~ "," ~ std.string.toString(fontSize)
			~ "," ~ std.string.toString(fontIndex);
	}
	
	void drawBaseCenterRad(Screen screen, int x, int y) {
		if (sin is null) {
			sin = SinTable.get();
		}
		float addWidth = 0;
		float spaceRate = spaceCounter.getDecimal();
		float offsetPos = getConterOffsetPos();
		double r;
		foreach (i, textureInfo; textureVec.getTextureInfo()) {
			r = (addWidth - offsetPos);
			screen.bltRotate(textureInfo.texture,
				cast(int) (x + (sin.cos_d(rad) * r)), 
					cast(int) (y + sin.sin_d(rad) * r), rad, rate, 4);
			addWidth += spaceRate + textureInfo.texture.getWidth() * rate;
		}
	}
	
	void drawBaseCenter(Screen screen, int x, int y) {
		float addWidth = 0;
		float spaceRate = spaceCounter.getDecimal();
		float offsetPos = getConterOffsetPos();
		foreach (i, textureInfo; textureVec.getTextureInfo()) {
			screen.bltRotate(textureInfo.texture,
				cast(int) (x + addWidth - offsetPos), 
				y, 0, rate, 4);
			addWidth += spaceRate + textureInfo.texture.getWidth() * rate;
		}
	}
	
	float getConterOffsetPos() {
		auto textureInfos = textureVec.getTextureInfo();
		if (textureInfos is null) {
			return 0.0f;
		}
		float spaceRate = spaceCounter.getDecimal();
		return ( (textureVec.getWidth() * rate) + (spaceRate * (textureInfos.length - 1))) / 2.0f;
	}
	
	static SinTable sin = null;

	// 描画文字列のリポジトリ
	FontRepository fontRepository;
	// 描画文字列の大きさ
	float rate = 1.0f;
	// 角度
	int rad = 0;
	// 描画中の文字列テクスチャ
	TextureVector textureVec;
	// 描画している文字列
	wchar[] drawString;
	/// 描画アルファ
	int alpha = 255;
	///　字間カウンター
	IDecimalCounter spaceCounter;
}

/// SpreadString を二つ使用して、疑似ブラーを行うクラス
public class SpreadStringBlur {
public:
	/// このクラスで必要なエッセンスデータ	
	static class EssencePram {
	public:
		FontRepository frontFont;
		FontRepository backFont;
		int spreadStartWidth;
		int spreadEndWidth;
		int spreadSpeed;
		int rad;
		float frontRate = 1.0f;
		float backRate = 1.0f;
		ICounter frontAlpha;
		ICounter backAlpha;
		
		bool isValid() {
			return (frontFont !is null) && (backFont !is null);
		}
	}
	
	/// コンストラクタ
	this(EssencePram essenceParam_) {	
		assert(essenceParam_ !is null);
		essenceParam = essenceParam_;
		
		if (!essenceParam.isValid()) {
			throw new Exception("SpreadStringBlur : Invalid essenceParam");
		}
		
		frontString = new SpreadString(essenceParam.frontFont);
		frontString.setRad(essenceParam.rad);
		frontString.setRate(essenceParam.frontRate);
		{
			IDecimalCounter widthCounter = new PowIncCounter();
			widthCounter.set(essenceParam.spreadStartWidth, essenceParam.spreadEndWidth, 
				essenceParam.spreadSpeed);
			frontString.setSpaceCounter(widthCounter);
		}
		
		backString = new SpreadString(essenceParam.backFont);
		backString.setRad(essenceParam.rad);
		backString.setRate(essenceParam.backRate);
		{
			IDecimalCounter widthCounter = new PowIncCounter();
			widthCounter.set(essenceParam.spreadStartWidth, essenceParam.spreadEndWidth, 
				essenceParam.spreadSpeed);
			backString.setSpaceCounter(widthCounter);
		}
	}
	
	void setCounterBase(float base) {
		auto frontCounter = cast(PowIncCounter) frontString.getSpaceCounter();
		if (frontCounter !is null) {
			frontCounter.setBase(base);
		}
		auto backCounter = cast(PowIncCounter) backString.getSpaceCounter();
		if (backCounter !is null) {
			backCounter.setBase(base);
		}
	}
	
	EssencePram getEssence() {
		return essenceParam;
	}
	
	bool isEndAlpha() {
		if (essenceParam.frontAlpha !is null) {
			if (!essenceParam.frontAlpha.isEnd()) {
				return false;
			}
		}
		if (essenceParam.backAlpha !is null) {
			if (!essenceParam.backAlpha.isEnd()) {
				return false;
			}
		}
		return true;
	}
	
	void setX(int x) {
		posx = x;
	}
	int getX() {
		return posx;
	}
	
	void setY(int y) {
		posy = y;
	}
	int getY() {
		return posy;
	}
	
	void setPosXY(int x, int y) {
		posx = x;
		posy = y;
	}
	
	void getPosXY(out int x, out int y) {
		x = posx;
		y = posy;
	}
	
	void setStartWait(int startWait_) {
		startWait = startWait_;
	}
	int getStatWait() {
		return startWait;
	}
	
	/// 文字列の設定
	void setStringUnicode(wchar[] str) {
		backString.setStringUnicode(str);
		frontString.setStringUnicode(str);
	}
	
	/// 状態を初期化する
	void reset() {
		if (essenceParam.frontAlpha !is null) {
			essenceParam.frontAlpha.reset();
		}
		if (essenceParam.backAlpha !is null) {
			essenceParam.backAlpha.reset();
		}
		frontString.reset();
		backString.reset();
	}
	
	/// 表の文字列画像を取得
	SpreadString getFront() {
		return frontString;
	}
	
	/// 裏の文字列画像を取得
	SpreadString getBack() {
		return backString;
	}
	
	void onMove(Screen screen) {
		if (startWait > 0) {
			--startWait;
			return;
		}
		
		if (essenceParam.frontAlpha !is null) {
			essenceParam.frontAlpha.inc();
		}
		if (essenceParam.backAlpha !is null) {
			essenceParam.backAlpha.inc();
		}
		frontString.onMove(screen);
		backString.onMove(screen);
	}
	
	void onDraw(Screen screen) {
		if (startWait > 0) {
			return;
		}

		if (essenceParam.frontAlpha !is null) {
			frontString.setAlpha(essenceParam.frontAlpha.get());
		}
		if (essenceParam.backAlpha !is null) {
			backString.setAlpha(essenceParam.backAlpha.get());
		}
		frontString.onDraw(screen, posx, posy, SpreadString.BASE_POS.CENTER);
		backString.onDraw(screen, posx, posy, SpreadString.BASE_POS.CENTER);
	}
	
	
private:
	EssencePram essenceParam;
	// 描画用
	SpreadString frontString;
	// ブラー用
	SpreadString backString;
	// スタート待機
	int startWait = 0;
	
	int posx;
	int posy;
}

