module yamalib.draw.menu;

private import y4d_draw.drawbase;
private import y4d_draw.texture;
private import y4d_draw.screen;
private import y4d_input.mouse;
private import y4d_input.mouse;

private import ytl.vector;

private import yamalib.counterfsp;
private import yamalib.gui.guiparts;
private import yamalib.gui.guibutton;

/** メニューバーに設定するアイテム */
class MenuItem {
public:	
	/// メニューボタンを設定する
	void setButton(GUIButton bt_) {
		this.m_bt = bt_;
	}
	
	/// メニューボタンの取得
	GUIButton getButton() {
		return this.m_bt;
	}
	
	void onMove(Screen screen) {
		m_bt.onMove(screen);
	}

	void onDraw(Screen screen) {
		m_bt.onDraw(screen);
	}
	
	/// コンストラクタ
	this() {
	}

private:
	GUIButton m_bt;
}

/** メニューバーに設定するアイテム */
class PartialMenuItem : MenuItem {
public:	

	/// 移動処理
	override void onMove(Screen screen) {
		if ( !m_offset.isEnd() ) {
			m_offset++;
			m_bt.setXYOffset( m_offset.get(), 0);
			if ( m_offset.isEnd() ) {
				m_show = false;
				m_hide = false;	
			}
		}
		
		super.onMove(screen);
	}
	
	/// 移動中か？
	bool isMoving() {
		return cast(bool) !this.m_offset.isEnd();
	}
	/// 表示中か
	bool isShow() {
		return this.m_show;
	}
	/// 非表示中か？
	bool isHide() {
		return this.m_hide;
	}
	
	/// 今すぐランチャーを最小化する
	void hideNow() {
		hide();
		m_bt.setXYOffset( m_offset.getEnd(), 0);
		m_show = m_hide =false;
	}
	
	/// ステップ数
	void setHideStep(int step_) {
		m_hideStep = step_;
		m_offset.setStep( m_hideStep );
	}
	
	/// 表示
	void show() {
		if ( m_show && isMoving() ) {
			return;
		}
		
		m_show = true;
		m_hide = false;
		
		int ox,oy;
		m_bt.getXYOffset(ox,oy);
		m_offset.set( ox, 0, m_hideStep );
	}
	
	/// 非表示
	void hide() {
		if ( m_hide && isMoving() ) {
			return;
		}

		m_hide = true;
		m_show = false;
	
		int x, y, ox, oy;
		m_bt.getXY(x,y);
		m_bt.getXYOffset(ox,oy);
		m_offset.set( ox, 640-x, m_hideStep );
	}
	
	/// コンストラクタ
	this() {
		super();
		m_offset = new InteriorCounter();
		m_offset.set(0, 0, DEFAULT_SPEED);
		m_hideStep = DEFAULT_SPEED;
	}
	
private:
	static const int DEFAULT_SPEED = 60;

	bool m_show;
	bool m_hide;
	int m_hideStep;
	InteriorCounter m_offset;
}

/// いろいろなメニューバーを作りたいので、インターフェース導入！
interface MenuBar {

	/// 基本位置の設定
	void setPos(int x_, int y_);
	/// メニューボタンの追加
	void addItem(MenuItem);
	/// メニューアイテムの取得
	vector!(MenuItem) getItems();
	///	追加したメニューアイテムのクリア
	void clearItems();
	/// オート隠すの設定
	void setAutoHide(bool, int, int, int);
	/// メニューバーにマウスが入っているか
	bool isInBar();
	/// メニューを表示します
	void show();
	/// メニューを隠します
	void hide();	
	/// 毎回呼び出すなり
	void onMove(Screen);
	/// 毎回呼び出すなり
	void onDraw(Screen);	
	/// 完全に表示状態か
	bool isShow();
	bool isHide();
}


/// Windowsと同様の動きが実装できるメニューバー
class NormalMenuBar : MenuBar {
	enum eHideType : int { TOP=0, LEFT, BOTTOM, RIGHT, FADE };


	/// 下地画像の設定
	void setBaseTexture(Texture t_) {
		m_baseTex = t_;
	}
	
	/// 基本位置の設定
	void setPos(int x_, int y_) {
		x = x_;
		y = y_;
	}
	
	/// メニューボタンの追加
	void addItem(MenuItem bt_) {
		m_items.push_back(bt_);
	}
	
	/// メニューアイテムの取得
	vector!(MenuItem) getItems() {
		return this.m_items;
	}
	
	/// メニューボタンを破棄します
	void clearItems() {
		m_items.clear();
	}
	
	/// オート隠すの設定
	void setAutoHide(bool b_, int type_, int step, int waitTime) {
		m_autoHide = b_;
		eType = cast(eHideType) type_;
		m_hideStep = step;
	}
	
	/// メニューバーにマウスが入っているか
	bool isInBar() {
		if ( m_mouse is null || m_baseTex is null ) return false;
		
		int mx,my,sx,sy;
		m_mouse.getPos(mx,my);
		sx = cast(int) m_baseTex.getWidth();
		sy = cast(int) m_baseTex.getHeight();
		
		return ptInRect(mx,my, x, y, x + sx, y + sy);
	}
	
	/// メニューを表示します
	void show() {
		if (null is m_baseTex) return;
		
		int nowPos = m_offset.get();
		int size;
		
		switch (eType) {
		case eHideType.TOP:
		case eHideType.BOTTOM:
			size = cast(int) m_baseTex.getHeight();
			m_offset.set(nowPos, 0, m_hideStep); 
			break;

		case eHideType.LEFT:
		case eHideType.RIGHT:
			size = cast(int) m_baseTex.getWidth();
			m_offset.set(nowPos, 0, m_hideStep); 
			break;

		case eHideType.FADE:
			m_alpha.set(m_alpha.get(), 255, m_hideStep); 
			break;

		default:
			break;
		}
	}
	
	/// メニューを隠します
	void hide() {

		if (null is m_baseTex) return;

		int nowPos = m_offset.get();
		int size;
		
		switch (eType) {
		case eHideType.TOP:
			size = cast(int) m_baseTex.getHeight();
			m_offset.set(nowPos, -size, m_hideStep); 
			break;

		case eHideType.BOTTOM:
			size = cast(int) m_baseTex.getHeight();
			m_offset.set(nowPos, size, m_hideStep); 
			break;

		case eHideType.LEFT:
			size = cast(int) m_baseTex.getWidth();
			m_offset.set(nowPos, -size, m_hideStep); 
			break;

		case eHideType.RIGHT:
			size = cast(int) m_baseTex.getWidth();
			m_offset.set(nowPos, -size, m_hideStep); 
			break;

		case eHideType.FADE:
			m_alpha.set(m_alpha.get(), 0, m_hideStep); 
			break;

		default:
			break;
		}
	}
	
	bool isShow() {
		return false;
	}
	
	bool isHide() {
		return false;
	}
	
	/// 毎回呼び出すなり
	void onMove(Screen screen) {
		
		m_offset++;
		m_alpha++;	
	
		foreach ( MenuItem item; m_items ) {
			item.onMove(screen);
		}	
	}
	
	/// 毎回呼び出すなり
	void onDraw(Screen screen) {

		screen.setColor(255, 255, 255, m_alpha.get() );
		
		// ベース表示
		switch (eType) {
		case eHideType.TOP:
		case eHideType.BOTTOM:
			screen.blt( m_baseTex, x, y + m_offset.get() );
			break;

		case eHideType.LEFT:
		case eHideType.RIGHT:
			screen.blt( m_baseTex, x + m_offset.get(), y );
			break;

		case eHideType.FADE:
			screen.blt( m_baseTex, x, y );
			break;

		default:
			break;
		}
		
		foreach ( MenuItem item; m_items ) {
			item.onDraw(screen);
		}	
	}
	
	/// コンストラクタ
	this(MouseInput mouse_) {
		this.m_mouse = mouse_;

		x = y = 0;
		m_alpha = new RootCounterS();
		m_offset = new RootCounterS();
		m_alpha.set(255,255,0);
		m_offset.set(0,0,0);
		m_hideStep = 1;
		
		eType = eHideType.TOP;
		m_items = new vector!(MenuItem);
	}
	
	
private :
	/// 点が矩形の内側にあるかどうか
	static bool ptInRect(int px, int py, int left, int top, int right, int bottom) {
		return cast(bool) (px >= left && px < right
			&& py >= top && py < bottom);
	}


	Texture m_baseTex;
	MouseInput m_mouse;
	
	eHideType eType;
	bool m_autoHide;
	int x;
	int y;
	int m_hideStep;
	RootCounterS m_alpha;
	RootCounterS m_offset;
	
	vector!(MenuItem) m_items;
}


/// メニューパーツがメニューバーを構成するようなもの
class PartialMenuBar : MenuBar {
	enum eHideType : int { TOP=0, LEFT, BOTTOM, RIGHT, FADE };

	/// 基本位置の設定
	void setPos(int x_, int y_) {
		x = x_;
		y = y_;
	}
	
	/// ランチャーを起動するインジケータボタンを設定する
	void setIndicator(GUIButton bt_, int ox_, int oy_) {
		m_indicator = bt_;
		
		int x,y;
		m_offset.set(0, ox_, m_hideStep);
		m_indOx = ox_;
		m_indOy = oy_;
	}
	
	/// メニューボタンの追加
	void addItem(MenuItem bt_) {
		(cast(PartialMenuItem) (bt_)).setHideStep( m_hideStep );
		m_items.push_back(bt_);
		
	}
	
	/// メニューアイテムの取得
	vector!(MenuItem) getItems() {
		return this.m_items;
	}
	
	/// メニューボタンを破棄します
	void clearItems() {
		m_items.clear();
	}
	
	/// オート隠すの設定
	void setAutoHide(bool b_, int type_, int step, int waitTime) {
		m_autoHide = b_;
		m_hideWait = waitTime;
		eType = cast(eHideType) type_;
		setHideStep(step);
	}
	
	/// 消える速度の設定
	void setHideStep(int step_) {
		m_hideStep = step_;
		foreach (MenuItem item; m_items ) {
			(cast(PartialMenuItem) (item)).setHideStep( m_hideStep );
		}
	}
	
	/// メニューバーにマウスが入っているか
	bool isInBar() {
		foreach ( MenuItem item; m_items ) {
			if ( item.getButton().isIn() ) {
				return true;
			}
		}
		
		if ( !(m_indicator is null) ) {
			return m_indicator.isIn();
		}
		
		return false;
	}
	
	/// メニューを表示します
	void show() {
		PartialMenuItem it = cast(PartialMenuItem) m_items[0];
		bool b = it.isShow();

		foreach ( MenuItem item; m_items ) {
			(cast(PartialMenuItem) item).show();
		}
		
		if ( b != it.isShow() ) {
			int ox,oy;
			m_indicator.getXYOffset(ox,oy);
			m_offset.set( ox, 0, m_hideStep);
		}
	}
	
	/// メニューを隠します
	void hide() {
		PartialMenuItem it = cast(PartialMenuItem) m_items[0];
		bool b = it.isHide();

		foreach ( MenuItem item; m_items ) {
			(cast(PartialMenuItem) item).hide();
		}

		if ( b != it.isHide() ) {
			int ox,oy;
			m_indicator.getXYOffset(ox,oy);
			m_offset.set( ox, m_indOx, m_hideStep);
		}
	}
	
	bool isShow() {
		foreach (MenuItem item; m_items) {
			auto concreateItem = cast(PartialMenuItem) item;
			if ( concreateItem.isMoving() || !concreateItem.isShow()) {
				return false;
			}
		}
		return true;
	}
	
	bool isHide() {
		foreach (MenuItem item; m_items) {
			auto concreateItem = cast(PartialMenuItem) item;
			if ( concreateItem.isMoving() || !concreateItem.isHide()) {
				return false;
			}
		}
		return true;
	}
	
	
	/// 直ちにメニューを隠します
	void hideNow() {
		PartialMenuItem it = cast(PartialMenuItem) m_items[0];
		bool b = it.isHide();

		foreach ( MenuItem item; m_items ) {
			(cast(PartialMenuItem) item).hideNow();
		}

		if ( b != it.isHide() ) {
			int ox,oy;
			m_indicator.getXYOffset(ox,oy);
			m_offset.set( ox, m_indOx, m_hideStep);
		}
	}
	
	/// 毎回呼び出すなり
	void onMove(Screen screen) {
		m_alpha++;	
		
		foreach ( MenuItem item; m_items ) {
			item.onMove(screen);
		}
		
		m_offset.inc();
		// 去人対応 インジケータは動かさない
//		m_indicator.setXYOffset( m_offset.get(), 0 );

		PartialMenuItem item = cast(PartialMenuItem) m_items[0];
		bool aShow = item.isShow();
		bool aHide = item.isHide();

		m_indicator.onMove(screen);
		if (m_autoHide) {
			if ( !aShow && m_indicator.isIn() ) {
				show();
			}
			if ( !isInBar() ) {
				hide();
			}
		} else {
			// ボタン押下
			if ( m_indicator.isLClick() ) {
				hide();
			}
		}
	}
	
	/// 毎回呼び出すなり
	void onDraw(Screen screen) {
		m_indicator.onDraw(screen);

		foreach ( MenuItem item; m_items ) {
			item.onDraw(screen);
		}	

	}
	
	/// コンストラクタ
	this(MouseInput mouse_) {
		this.m_mouse = mouse_;
		m_autoHide = true;
		
		x = y = 0;
		m_alpha = new RootCounterS();
		m_alpha.set(255,255,0);
		m_hideStep = 1;
		
		m_offset = new FreeLineCounter(new InteriorCounter());
		eType = eHideType.RIGHT;
		m_items = new vector!(MenuItem);
		
		m_hideStep = 60;
	}
	
private:
	MouseInput m_mouse;
	eHideType eType;
	bool m_autoHide;
	int x;
	int y;
	RootCounterS m_alpha;
	
	GUIButton m_indicator;
	FreeLineCounter m_offset;
//	InteriorCounter m_offset;
	int m_hideStep;
	int m_hideWait;
	int m_indOx;
	int m_indOy;
	
	vector!(MenuItem) m_items;
	
}
