module yamalib.character;

private import std.string;	// atoi

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

private import y4d_draw.screen;
private import y4d_draw.texture;
private import y4d_draw.textureloader;
private import y4d_draw.sprite;
private import y4d_draw.transbltter;
private import y4d_draw.drawbase;

private import y4d_math.sintable;
private import y4d_math.rand;

private import y4d_timer.fixtimer;

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

/// キャラの状態管理クラス
class Chara {
public:
	RootCounterS charaMove;		//<! キャラ移動変数
	int pos;				//!< 立ち位置
	int preset_pos;			//!< プリセットされて立ち位置の番号
	int slide = 0;			//!< スライドイン、アウト、フラグ
	bool wait = false;		//!< キャラエフェクト待機フラグ
	int move_x;
	bool autoClear=true;	//!< 消去エフェクト後、キャラクタを自動でクリアするか　キャラクタが非表示になったあとも、キャラクタを使いたい場合はOffにする
	
	static const int C_CHARA_BLT_Y = 0;
	
	/// キャラクターテクスチャローダの設定
	void setTextureLoader(TextureLoader tl) 
	in {
		assert( !(tl is null) );
	}
	body
	{
		textureLoader = tl;
	}
	
	/// 非表示エフェクト中か？
	bool isEffectOut() {
		if (charaEffect is null) {
			return false;
		}
		return charaEffect.getStart() > charaEffect.getEnd();
	}
	
	/// 表示オフセット位置の設定
	void setOffset(int ox, int oy) {
		m_offsetx = ox;
		m_offsety = oy;
	}
	void getOfffset(out int ox, out int oy) {
		ox = m_offsetx;
		oy = m_offsety;
	}
	
	/// 画面の幅を設定します
	void setViewWidth(int viewWidth)
	in
	{
		assert( viewWidth > 0);
	}
	body {
		m_viewWidth = viewWidth;
	}
	int getViewWidth() {
		return this.m_viewWidth;
	}
	
	/**
	 * いまエフェクトを行っているのであれば、それを終了させる
	 * また、それがキャラクターアウトならば、自分を、未使用状態にする
	 */
	void effectFinish() {
		int end = charaMove.getEnd();
		charaMove.opAssign(end);
		end = getCharaEffectCounter().getEnd();
		getCharaEffectCounter().opAssign(end);
		effectNo = -1;

		// char_out 処理なら消す
		if (0==end) {
			reset();
		}
	}
	
	/// 表示エフェクト中か？
	bool isEffectIn() {
		if (charaEffect is null) {
			return false;
		}
		return charaEffect.getStart() < charaEffect.getEnd();
	}
	
	bool isReplace() {
		return this.replace;
	}
	
	/**
	 * キャラクター表示時のエフェクトを設定
	 * キャラクター入れ替えのときのエフェクト指定はsetEffectReplaceで
	 */
	void setEffect(int effectType, int effectSpeed) {
		setReplace(false);
		this.effectNo = effectType;
		this.charaEffect.set(0,255,effectSpeed);
	}
	
	void setEffectReplace(int fontEffectType, int frontEfectSpeed, int backEffectType, int backEffectSpeed) {
		setEffect(fontEffectType, frontEfectSpeed);
		this.charaEffectPre.set(0,255, backEffectSpeed);
		this.effectNoBack = backEffectType;
		setReplace(true);
	}

	/// フェードイン開始
	void fadeIn(int speed, int speedPre=1) {
		charaEffect.set(0,255,speed);
		charaEffectPre.set(0, 255, calcAlphaStep(speed) * speedPre );
	}
	
	/// キャラクターの描画
	void blt(Screen screen, bool draw=true) 
	in
	{
		assert( !(screen is null) );
	}
	body
	{
		int x,y,alpha,tx,ty,phase,i;
		Texture t1,t2;
		y = C_CHARA_BLT_Y;
		alpha = 255;
		phase = 255;
		// color バックアップ
		Color4ub colorOrg = screen.getColor4ub(); 

		if ( isUse() ) {
			t1 = textureLoader.get(getTextureNo());

			// ぬるぽ？
			if(!t1) {
				Log.printError("texture load fault!!:%s\n", getCharId());
				return;
			}

			tx = cast(int) t1.getWidth();
			ty = cast(int) t1.getHeight();

			if (effectNo != -1) {
				charaEffect++;
				if (charaEffect.isEnd()) {
					wait = false;
					replace = false;
				}

				phase = charaEffect.get();
			}

			// スライド
			if (slide != 0) {
				if(preset_pos != -1) {
					x = getCharaPresetPos(screen, preset_pos, tx);
				} else {
					// 位置直接指定
					x = pos-tx/2;
				}
				charaMove++;
				x += st.sin(charaMove.get(),move_x);
			} else {
				// プリセットポジション
				if(preset_pos != -1) {
					x = getCharaPresetPos(screen, preset_pos, tx);
				} else {
					// 位置直接指定
					x = pos-tx/2;
				}
			}

			if (phase == 255) {

				screen.setColor( this.m_color );
				
				screen.blt(t1,x,y);
			} else if (phase == 0) {
//				if (!toSioriFlag) {
//					reset();
//				}
			} else {
				if (replace) {
					t2 = textureLoader.get(getTextureNoOld());
					if (t2) {
						screen.setColor(m_color.r, m_color.g, m_color.b, 255-phase);
						screen.blt(t2,x + m_offsetx,y + m_offsety);
					}
				}
				if (draw) {
					screen.setColor(m_color.r, m_color.g, m_color.b);
					TransBltter.blt(effectNo,screen,
						t1,x + m_offsetx,y + m_offsety,phase);
				}
			}
		}
		
		// color 戻し
		screen.setColor( colorOrg );
		
	}
	
	/// エフェクト中か
	bool isEffect() { 
		return cast(bool) (!charaMove.isEnd()||!charaEffect.isEnd()); 
	}

	/// 使用しているか
	bool isUse() {
		return ((charNo==-1||strCharId is null) ? false : true);
	}

	
	RootCounterS getCharaEffectCounter() {
		return this.charaEffect;
	}

	/// フェードアウト開始
	void fadeOut(int effectType, int speed) {
		this.effectNo = effectType;
		charaEffect.set(255,0,speed);
		setReplace(false);
	}
	
	/// 描画色の設定
	void setColor(Color4ub color) {
		this.m_color = color;
	}
	
	/// 描画色の取得
	Color4ub getColor() {
		return this.m_color;
	}

	/// 未使用状態に戻す
	void reset() {
		charaEffect.set(0,255,1);
		effectNo = -1;
		charNo = -1;
		preset_pos = 1;
		strCharId = null;
		strCharIdOld = null;
		wait = false;
		replace = false;
		slide = 0;
		pos = 0;
		this.m_color.r = 255;
		this.m_color.g = 255;
		this.m_color.b = 255;
		this.m_color.a = 255;
		charaMove.set(0,0,1);
		move_x = 0;
	}

	/// キャラクタＩＤ文字列を設定する
	void setCharId(char[] str) {
		if (!str) return;
		strCharIdOld = strCharId;
		charNoOld = charNo;
		strCharId = str;
		
		if ( (str in charMap) is null ) {
			Log.printError("%s not find in CharaMap!", str);
			
			auto dump = charMap.keys;
			foreach (s; dump) {
				Log.printWarn("chara id :  %s", s);
			}
			
			return;
		}
		
		charNo = charMap[str];
	}
	/// このキャラクターのＩＤを返却する
	char[] getCharId() { return strCharId; }

	/// キャラクタＩＤ文字列から割り出したテクスチャ番号を返す
	int getTextureNo() {
		return charNo;
	}

	/// キャラクタＩＤ文字列から割り出した前のテクスチャ番号を返す
	int getTextureNoOld() {
		return charNoOld;
	}

	/// 名前に対応したテクスチャ番号を返す
	int getNo() { return charNo; }

	/// 文字列と番号のマップ
	static void setCharMap(char[] filename) {
		ubyte[] memory = cast(ubyte[])FileSys.read(filename);
		std.stream.MemoryStream m = new std.stream.MemoryStream(memory);

		int count = 0;
		while(!m.eof) {
			lineParser.setLine(cast(char[]) m.readLine());
			// filename はすてる
			lineParser.getStr();
			// charID
			char[] str = lineParser.getStr();
			if ( str is null ) continue;
			
			charMap[str] = count++;
			
			Log.print("charMap added %s - %s", str, count-1);
		}
		
		m.close();
	}

	/// コンストラクタ
	this() {
		st = SinTable.get();
		charaMove = new RootCounterS;
		charaMove.set(0,0,1);	// 無効
		charaEffect = new RootCounterS(0,255,2);
		charaEffectPre = new InteriorCounter();
		reset();
		m_viewWidth = 640; // デフォルト
	}

	// 静的コンストラクタ
	static this() {
		lineParser = new LineParser();
	}
	
	
	/** キャラクター入れ替え時のフェード値はかなりデリケートなので専用のクラスを作る */
	static class BackCharaAlaphaCounter {
	public:
		int inc() {
			int now = m_frontCharaAlphaCounter.get();
			m_backCharaAlphaCounter.inc();
			return get();
			
		}
		
		/// 現在の値の取得
		int get() {
			if (m_frontCharaAlphaCounter.isEnd()) {
				return 0;
			}
			
			int now = m_frontCharaAlphaCounter.get();
			if (!m_converge && FIT_POSITION >= m_frontEnd - now) {
				m_converge = true;
				int ownNow = m_backCharaAlphaCounter.get();
				m_backCharaAlphaCounter.set(ownNow, 0, 
					((m_frontEnd - now) / m_frontCharaAlphaCounter.getStep()) - 1 );
			}
			
			if (m_converge) {
//printf("$%d ", (m_backCharaAlphaCounter.get()));
				return m_backCharaAlphaCounter.get();
			} 
//printf("#%d ", (255 - m_backCharaAlphaCounter.get()));
			return 255 - m_backCharaAlphaCounter.get();
		}
		
		
		this(RootCounterS frontCharaAlphaCounter, uint backRate) 
		in
		{
			assert(!(frontCharaAlphaCounter is null));
		}
		body
		{
			m_frontCharaAlphaCounter = frontCharaAlphaCounter;
			m_backCharaAlphaCounter = new InteriorCounter();
			
			m_frontStart = frontCharaAlphaCounter.getStart();	// = 0
			m_frontEnd = frontCharaAlphaCounter.getEnd(); // = 255
			int step = frontCharaAlphaCounter.getStep();
			
			m_backCharaAlphaCounter.set(0, 255, cast(int) (Chara.calcAlphaStep(step) * 1.5) );
		}
		
	private:
		static const int FIT_POSITION = 32;
		int m_viewWidth;
		RootCounterS m_frontCharaAlphaCounter;
		InteriorCounter m_backCharaAlphaCounter;
		int m_frontEnd;
		int m_frontStart;
		bool m_converge;
	}
	

protected:

	/// 指定したステップで、RootCounterを動作させたときに、何回で終端に達するかを返却
	static int calcAlphaStep(int step) {
		if (0 == step) { 
			step = 1;
		}
		int result;
		if (0 > step) {
			result = (255 * (step + 1)) + step;
			return result;
		} else {
			result = 255 / step;
			return  (255 % step) == 0 ? result : result + 1;
		}
	}

	/// キャラクターのプリセット位置を取得する	
	int getCharaPresetPos(Screen screen, int presetNumber, int textureWidth) {
		int halfScreenWidth = screen.getWidth() / 2;
		switch (presetNumber) {
			case 0: 
				return (halfScreenWidth - (m_viewWidth / 4)) - (textureWidth / 2);
			case 1: 
				return halfScreenWidth - (textureWidth / 2);
			case 2:
				return (halfScreenWidth + (m_viewWidth / 4)) - (textureWidth / 2);
			default:
				Log.printError("%s#getCharaPresetPos : undefined pos :%s", this.toString(), presetNumber);
				assert(false);
		}
	}

	/// 置換フラグの設定
	void setReplace(bool b) {
		this.replace = b;
	}


	static int[char[]] charMap;		//!< キャラクター番号とキャラクターＩＤ文字列との連想記憶
	static LineParser lineParser;	//!< ラインパーサ
	static SinTable st;

	TextureLoader textureLoader;
	int charNo;		//!< 実キャラクター番号
	int charNoOld;	//!< 前に表示していたキャラクター番号
	char[] strCharId;	//!< キャラクターのＩＤ文字列
	char[] strCharIdOld;	//!< 前に表示していたキャラクタのＩＤ文字列
	int preX, preY;		//!< 前のキャラクターが表示されていた位置
	Color4ub m_color = {r:255, g:255, b:255, a:255};
	int m_offsetx;	// 表示オフセット位置
	int m_offsety;
	int m_viewWidth; //!< 画面の幅
	
	int effectNo;			//!< トランジットエフェクト番号
	int effectNoBack;		//!< トランジット番号置換前用
	RootCounterS charaEffect;		//<! キャラエフェクト変数
	InteriorCounter charaEffectPre;	//<! キャラエフェクト変数
	BackCharaAlaphaCounter backCharaAlphaCounter;
	bool replace;			//!< キャラ置き換えフラグ
	
	unittest {
		ICounter.setStepRate(1);
		RootCounterS testCounter = new RootCounterS();
		testCounter.set(0,255,2);
		
		int count = calcAlphaStep(2);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
				assert( !testCounter.isEnd() );
			} else {
				assert( testCounter.isEnd() );
			}
		}

		testCounter.set(0,255,-2);
		count = calcAlphaStep(-2);
printf("%d \n", count);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
printf("%d %d \n", i, testCounter.get());
				assert( !testCounter.isEnd() );
			} else {
printf("%d \n", count);
				assert( testCounter.isEnd() );
			}
		}

		testCounter.set(0,255,7);
		count = calcAlphaStep(7);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
				assert( !testCounter.isEnd() );
			} else {
				assert( testCounter.isEnd() );
			}
		}

		testCounter.set(0,255,3);
		count = calcAlphaStep(3);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
				assert( !testCounter.isEnd() );
			} else {
				assert( testCounter.isEnd() );
			}
		}

		testCounter.set(0,255,5);
		count = calcAlphaStep(5);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
				assert( !testCounter.isEnd() );
			} else {
				assert( testCounter.isEnd() );
			}
		}

		testCounter.set(0,255,1);
		count = calcAlphaStep(1);
		for (int i = 0; i < count; ++i) {
			testCounter.inc();
			if (i != count-1) {
				assert( !testCounter.isEnd() );
			} else {
				assert( testCounter.isEnd() );
			}
		}
	}
}


/**
	モーション付きキャラクター描画クラス
*/
class CharaSprite : Chara {
public:

	override void setEffectReplace(int fontEffectType, int frontEfectSpeed, int backEffectType, int backEffectSpeed) {
		setEffect(fontEffectType, frontEfectSpeed);
		
//		this.charaEffectPre.set(0,255, backEffectSpeed);
		// KYOJIN 固有のエフェクトスピード
		
		this.charaEffectPre.set(0,255, calcAlphaStep(frontEfectSpeed) - 1);
		backCharaAlphaCounter = new BackCharaAlaphaCounter(charaEffect, uint.max);
	
		this.effectNoBack = backEffectType;
		setReplace(true);
	}


	/// アニメーション付き描画
	override void blt(Screen screen, bool draw=true) {
		int x, y, alpha;
		int tx, ty, phase;
		int i;
		Texture t1, t2;
		y = C_CHARA_BLT_Y;
		alpha = 255;
		phase = 255;
		// オリジナルcolor
		Color4ub colorOrg = screen.getColor4ub();
		
		try {
			if ( isUse() ) {
				Sprite.SpriteVector tv = spriteEx.getDirectionalSprite();
				t1 = tv[spriteEx.getMotion()].texture;	// 基本、アニメーションスプライトの高さは同じものとする
	
				tx = cast(int)tv[spriteEx.getMotion()].rcRect.getWidth();
				ty = cast(int)tv[spriteEx.getMotion()].rcRect.getHeight();
	
				if (-1 != effectNo) {
					charaEffect++;
					if ( !(charaEffectPre is null) ) {
						charaEffectPre.inc();
					}
					if ( !(backCharaAlphaCounter is null) ) {
						backCharaAlphaCounter.inc();
					}
					
					if ( charaEffect.isEnd() ) {
						wait = false;
						replace = false;
						
						if (charaEffect.get() == 0 && autoClear) {
							reset();
						}
						
						/// 消去エフェクト完了ならここで終了
						if (charaEffect.get() == 0) {
							debug {
								if (!autoClear) {
									Log.printLook("Chara Out. but not clear %s", strCharId);
								}
							}
							return;
						}
					}
	
					phase = charaEffect.get();
				}
	
				// スライド
				if (0 != slide) {
					if(-1 != preset_pos) {
						x = getCharaPresetPos(screen, preset_pos, tx);
					} else {
						// 位置直接指定
						x = pos - tx / 2;
					}
					
					if (!replace) {
						// 最終描画位置予想
						preX = x + move_x;
						preY = y;
					}
	
					charaMove++;
					assert( !(st is null) );
					x += st.sin(charaMove.get(), move_x);
				} else {
					// プリセットポジション
					if (preset_pos != -1) {
						x = getCharaPresetPos(screen, preset_pos, tx);
					} else {
						// 位置直接指定
						x = pos - tx / 2;
					}
	
					// 最終描画位置予想
					if (!replace) {
						preX = x;
						preY = y;
					}
				}
				
	
				if (phase == 255) {
					screen.setColor( m_color );
					screen.blt(t1, x + m_offsetx, y + m_offsety, &tv[spriteEx.getMotion()].rcRect);
					preX = x;
					preY = y;
					/* モーション中ならば次の画像へ */
					incMotionS();
				} else if (phase == 0) {
					reset();
	
				} else {
					// 線形でフェードしても上手く画像が繋がらない
					int rad = cast(int) (128.0f * (cast(float) (255-phase)/255.0f));
					// 古いテクスチャの表示
					if (replace) {
						if ( !(charaTexOld is null) ) {
	
	//						screen.setColor(m_color.r, m_color.g, m_color.b, 255-phase);
	//						screen.setColor(m_color.r, m_color.g, m_color.b, 255-charaEffectPre.get());
	//						screen.setColor(m_color.r, m_color.g, m_color.b, backCharaAlphaCounter.get());
	//printf("#%d ", backCharaAlphaCounter.get() );
	
							//screen.blt(charaTexOld, preX, preY);
							
							
							//test
							{
								screen.setColor(m_color.r, m_color.g, m_color.b, 255);
	//							TransBltter.blt(backEffectType, screen, charaTexOld, preX, preY, (287-phase));
								TransBltter.blt(effectNoBack, screen, 
									charaTexOld, preX + m_offsetx, preY + m_offsety, 255-charaEffectPre.getSined());
	//							TransBltter.blt(effectNoBack, screen, charaTexOld, preX, preY, backCharaAlphaCounter.get());
							}
						} else {
							Log.printWarn("%s#blt : replece request. Bat OldTexutre is null", this.toString());
						}
					}
					// フェードインアウトは手動
					if (draw) {
						screen.setColor(m_color.r, m_color.g, m_color.b, 255);
						TransBltter.blt(effectNo, screen, t1, x + m_offsetx, y + m_offsety, phase);
						/* モーション中ならば次の画像へ */
						incMotionS();
					}
				}
			}
			
			screen.setColor( colorOrg );
		} catch (Object o) {
			Log.printFatal("CharaSprite#blt : [%s]", o.toString());
		}

	}
	
	/** アニメーション中であるかどうかを設定する */
	void setMotion(bool b) {
		motion = b;
	}
	
	/** アニメーション中であるかどうかを返却する */
	bool isMotion() {
		return motion;
	}
	
	/** スプライト定義ファイル名からキャラクターを読み込む */
	void setSpriteDefFile(char[] str) {
		if (!str) return;
		strCharIdOld = strCharId.dup;
		// スプライト設定してあんのか？
		if( !( spriteEx.getAllSprite() is null ) ) {
			Sprite.SpriteVector sv = spriteEx.getDirectionalSprite();
			// このSpriteVectorはリリースされるので、今のテクスチャのディープコピーをとっておかなくてはならない
			charaTexOld = sv[spriteEx.getMotion()].texture.dup();
			
		}
		
		if ( !(str in charMap) ) {
			Log.printError("CharaSprite#setSpriteDefFile : %s key not found in charMap!!!", str );
			return;
		}
		
		charNoOld = charNo;
		charNo = charMap[str];
		
		strCharId = str;
		spriteDefFileName = charSpriteDefMap[str];

		// キャラスプライトの読み込み
		if (spriteDefFileName) {
			spriteLoader.load( FileSys.makeFullName(spriteDefFileName) );
			spriteEx.setSprite(spriteLoader.getSprite());
		} else {
			Log.printError("SDF file read Error! : %s\n", str);
		}
	}
		
	/// キャラＩＤ文字列とスプライト定義ファイル名とのマップ
	static void setCharMap(char[] filename) {
		ubyte[] memory = cast(ubyte[])FileSys.read(filename);
		std.stream.MemoryStream m = new std.stream.MemoryStream(memory);

		try {
			while(!m.eof) {
				lineParser.setLine(cast(char[]) m.readLine());
				// スプライト定義ファイル名
				char[] fileName = lineParser.getStr();
				// charID
				char[] str = lineParser.getStr();
				if ( str is null) continue;
				
				charSpriteDefMap[str] = FileSys.makeFullName(fileName);
			}
		} catch (Exception e) {
			Log.printFatal("CharaSprite#setCharMap : [%s]", e.toString());
		} finally {
			if ( !(m is null) ) {
				m.close();
			}
		}
		
	}
	
	/// コンストラクタ
	this() {
		super();
		charaMove = new RootCounterS;
		charaMove.set(0,0,1);	// 無効
		charaEffect = new RootCounterS(0,255,2);
		spriteLoader = new SpriteLoader;
		spriteEx = new SpriteEx;
		motion = true;
	}
	
	/// デストラクタ
	~this() {
	}
	
protected:
	/** モーションちゅうであればさせる
		 一週したらモーションを停止させる
	 */
	void incMotionS() {
		if (motion) {
			spriteEx.incMotion();
			/** 一周したならアニメーションを停止させる */
			if (spriteEx.getMotion() == 0) {
				motion = false;
			}
		}
	}
	
private:

	
	/** 現在のスプライトの現在のテクスチャを保存する */
	void setStoreCurrentTexture() {
	}
		
protected:

	static char[][char[]] charSpriteDefMap;		//!< キャラクター番号とスプライト定義ファイル名との連想記憶

	Sprite.SpriteVectorVector sprite;	//! 立ちキャラのスプライト
	SpriteLoader spriteLoader;	//! スプライト定義ファイル読み込みクラス
	SpriteEx spriteEx;	//! スプライトクラス
	
	Texture charaTexOld;	//! 以前表示していたいテクスチャ
	bool motion;	//! モーションさせるか
	
	char[] spriteDefFileName;	//! スプライト定義ファイル名
}

/**
	まばたきキャラクタークラス
	人間にありそうな瞬きを自動的に行います
*/
public class WinkCharacter : CharaSprite {
	
	/// まばたき間隔のゆらぎ幅を設定
	void setWinkRand(uint winkRand) {
		// Zeroって…
		if (winkRand == 0) {
			return;
		}
		this.winkRand = winkRand;
	}
	
	/// コンストラクタ
	this() {
		super();
		// モーションフラグ開始
		motion = true;
		winkRand = WINK_RAND;
		timer = new FixTimer();
	}
	
	/// デストラクタ
	~this() {
	}
	
	/// 静的コンストラクタ
	static this() {
		rand = new Rand();
		rand.randomize();
	}

protected:
	/** デフォルトのまばたき間隔 */
	static const uint WINK_DEFAULT_INTERVAL_MSEC = 3000;
	/** まばたき間隔のゆらぎ +-２2 */
	static const uint WINK_RAND = 2000;

	/** ウィンク動作を再定義 */
	override void incMotionS() {
		int time;
		if (motion) {
			if (winkStop) {
				timer.update();
				time = timer.get();
				
				if (winkWait <= time) {
					winkStop = false;
				}
			} else {
				spriteEx.incMotion();
				/** 一周したならアニメーションを停止させる */
				if (spriteEx.getMotion() == 0) {
					winkStop = true;
					/** まばたき停止時間監視開始 */
					timer.reset();
					winkWait = WINK_DEFAULT_INTERVAL_MSEC - (rand.get( (winkRand * 2) ) - winkRand);
				}
			}
		}
	}
	
protected:
	/** またばき制御する変数を生成する */
	static Rand rand;
	/** またばき速度を計測するタイマ */
	FixTimer timer;
	/** まばたき中か？ */
	bool winkStop;
	/** まばたき停止時間 */
	uint winkWait;
	/** まばたき間隔のゆらぎ幅 */
	uint winkRand;
}
