module yamalib.gui.guilistbox;

private import y4d_aux.widestring;
private import y4d_draw.screen;
private import y4d_draw.drawbase;
private import y4d_draw.texture;
private import y4d_draw.fontrepository;
private import y4d_input.mouse;
private import y4d_input.mouse;

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

private import yamalib.log.log;

/**
	WindowsのListboxをエミュレートする
*/
class GUIListBox : IGUIParts {
	
	/// 表示エリアの設定
	void setArea(RectInt rc) {
		m_area = rc;
		m_bar.setSize(10, rc.getHeight());
	}
	
	/// リストボックスの下地となるテクスチャ
	void setBaseTexture(Texture t_) {
		this.m_base = t_;
	}
	
	/// スクロールバーの設定
	void setScrollbar(Scrollbar bar) 
	in
	{
		assert( !(bar is null) );
	}
	body
	{
		m_bar = bar;
	}
	
	/// スクロールバーの取得
	Scrollbar getScrollbar() {
		return m_bar;
	}
	
	/// 複数選択可／不可
	void setMultiSelectable(bool b) {
		m_multiSel = b;
	}
	
	/// 位置の設定
	override void setXY(int x_, int y_) {
		super.setXY(x_,y_);
		m_bar.setXY(x_,y_);
	}
	
	
	/// マウス設定
	override void setMouse(MouseInput mouse_) {
		super.setMouse( mouse_ );
		m_bar.setMouse( mouse_ );
	}
	
	/// リストボックス内にマウスカーソルがあるか
	bool isInBox() {
		int mx,my;
		mouse.getPos(mx, my);
		return ptInRect(mx,my, 
			m_area.left, m_area.top, m_area.right, m_area.bottom);
	}
	
	/// 選択状態にする
	void setSelect(int n) {
		if (n < 0) return;
		if (n >= this.items.length) return;
		this.items[n].setSelect(true);
		foreach (int i,inout ListBoxItem item; items) {
			if (i == n ) continue;
			item.setSelect(false);
		}
	}
	
	/// 一個だけ選択されていれば０以上を返却する。でなければ-1;
	int getSelectedNum() {
		foreach (int i, inout ListBoxItem item; this.items ) {
			if ( item.isSelect() ) {
				return i;
			}
		}
		return -1;
	}
	
	/// 新規選択されたか
	bool isNewSelect() {
		return this.m_newSelect;
	}
	
	/// 仮想高さの取得
	int getVHieght() {
		int size;
		foreach ( inout ListBoxItem item; items) {
			size += item.getSize().cy;
		}
		return size;
	}

	/// 項目を最後に追加する
	void addItem(ListBoxItem item) {
		this.items ~= item;
		m_bar.setScrollSize(getVHieght());
	}
	
	/// 項目を追加する
	void addItemAll(ListBoxItem[] items_) {
		this.items ~= items_;
	}
	
	/// リストボックス項目の取得
	ListBoxItem[] getItemAll() {
		return this.items;
	}
	
	/// 選択されているアイテム配列を取得する
	ListBoxItem getSelectedItem() {
		int selectedIndex = getSelectedNum();
		if (-1 == selectedIndex) {
			return null;
		}
		return getItem(selectedIndex);
	}
	
	/// リストボックス項目の取得
	ListBoxItem getItem(int i) {
		if ( i < 0 || i >= items.length) {
			return null;
		}
		return items[i];
	}
	
	/// 毎回呼び出すなり
	override void onMove(Screen screen) {
		m_newSelect = false;
		int newSel = -1;

		foreach (int i,inout ListBoxItem item; items) {
			// 動かす前の状態として保持
			bool select = item.isSelect();
			item.onMove(screen);
			// 動作後、動かされたかどうかの判定
			if ( item.isLClick() ) {
				if (!select) {
					newSel = i;
					item.setSelect(true);
				}
			}
		}
		
		if (-1 != newSel && !m_multiSel) {
			m_newSelect = true;
			// 複数選択が不可で新規選択されれば、全項目フラグを再設定
			foreach (int i,inout ListBoxItem item; items) {
				if (i == newSel ) continue;
				item.setSelect(false);
			}
		}
		
		int scrollx,scrolly;
		m_bar.getXY(scrollx,scrolly);
		m_bar.setXY( scrollx, this.y );
		m_bar.onMove(screen);
		
		float pos = m_bar.getScrollPos();
		m_topIndex = cast(int) (pos * items.length);
	}

	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		
		if ( m_base ) {
			screen.blt( m_base, m_area.left, m_area.top);
		}
		
		int ox,oy;
		// アイテム描画
		for (int i = m_topIndex; i < items.length; ++i) {
			Size sz = items[i].getSize();
			
			if ( sz.cy + oy > m_area.bottom) {
				break;
			}
			
			items[i].setPos(x,y + oy);
			items[i].onDraw(screen);
			oy += sz.cy;
		}
		
		m_bar.onDraw(screen);
	}
	
	/// コンストラクタ
	this() {
		m_bar = new Scrollbar();
	}
		
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);
	}
	
	int m_topIndex;
	
	Texture m_base;

	ListBoxItem[] items;
	Scrollbar	m_bar;
	RectInt m_area;
	bool m_multiSel;
	bool m_newSelect;
}


/**
	表示項目
*/
interface ListBoxItem  {
	void onMove(Screen);
	void onDraw(Screen);
	
	void setPos(int,int);
	void getPos(out int,out int);
	
	void setSize(Size);
	Size getSize();
	
	bool isSelect();
	void setSelect(bool);
	
	bool isLClick();
	bool isRClick();
}

/**
	リストボックス表示項目のシンプルな実装
*/
class SimpleListBoxItem : ListBoxItem {

	/// フォント
	static FontRepository getFontRepository() { return fontrep; };
	
	/// 表示文字列
	void setText(char[] str) {
		m_tex = null;
		m_text = toWCS(str);
		foreach (inout wchar c; m_text) {
			Texture t = getFontRepository().getTexture(c);
			if (!t) continue;
			m_tex ~= t;
		}
	}
	
	/// 表示テキストを取得
	wchar[] getText() {
		return m_text;
	}
	
	/// フォントのレートを設定
	void setRate( float f_ ) {
		this.m_rate = f_;
	}
	
	/// フォントの色を設定
	void setFontColor( Color4ub color_ ) {
		this.m_fontColor = color_;
	}
	
	/// 選択中の背景カラー	
	void setSelBackColor( Color4ub color_ ) {
		this.m_selectedBackColor = color_;
	}
	
	/// 選択状態有無
	bool isSelect() {
		return this.m_select;
	}
	
	/// 選択状態設定
	void setSelect(bool b) {
		this.m_select = b;
	}
	
	/// 項目領域の設定
	void setSize(Size sz) {
		this.m_areaSz = sz;
	}
	/// 項目領域の取得
	Size getSize() {
		return this.m_areaSz;
	}
	
	/// 位置の設定
	void setPos(int x_, int y_) {
		this.x = x_;
		this.y = y_;
	}
	
	/// 位置の取得
	void getPos(out int x_, out int y_) {
		x_ = this.x;
		y_ = this.y;
	}
	
	/// 毎回呼び出すなり
	void onMove(Screen screen) {
		m_lclick = false;
		m_rclick = false;
				
		int mx, my;
		m_mouse.getPos(mx, my);
		
//printf("mx=%d, my=%d, left=%d, top=%d, right=%d, bottom=%d\n",mx, my, x, y, x + m_areaSz.cx, y + m_areaSz.cy);	
		if ( ptInRect(mx, my, x, y, x + m_areaSz.cx, y + m_areaSz.cy) ) {
			if ( m_mouse.isLButtonUp() ) {
				m_lclick = true;
			}
			
			if ( m_mouse.isRButtonUp() ) {
				m_rclick = true;
			}
		}
	}
	
	/// 毎回呼び出すなり
	void onDraw(Screen screen) {
		// 下地描画
		if (m_select) {
			drawSelected(screen, x, y);
		}
		
		Color4ub colOrg = screen.getColor4ub();
		
		screen.setColor( this.m_fontColor );
		
		// 高さは指定にならうといて、幅だけ保証
		float sumW = 0.0f;
		foreach ( inout Texture t; m_tex) {
			float w = t.getWidth() * m_rate;
			if ( m_areaSz.cx < sumW + w) break;
			screen.bltRotate( t, cast(int) (x+sumW), y, 0, m_rate, 0);
			sumW += w;
		}
		
		screen.setColor(colOrg);
	}
	
	/// 左クリックされた
	bool isLClick() {
		return m_lclick;
	}
	
	/// 右クリックされた
	bool isRClick() {
		return m_rclick;
	}
	
	/// コンストラクタ
	this(MouseInput mouse_) {
		m_mouse = mouse_;
		m_rate = 1.0f;
		m_fontColor.setColor( 255, 255, 255 );
		m_selectedBackColor.setColor( 0, 0, 0 );
	}
	
	/// 静的コンストラクタ
	static this() {
		fontrep = new FontRepository();
	}
	
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);
	}
	
	/// 選択背景の描画
	void drawSelected(Screen screen, int x, int y) {
		Color4ub colOrg = screen.getColor4ub();
		screen.setColor(m_selectedBackColor);
		
		screen.drawPolygon (
			x, y,
			x + m_areaSz.cx, y,
			x + m_areaSz.cx, y + m_areaSz.cy,
			x, y + m_areaSz.cy
		);
		
		screen.setColor(colOrg);
	}

	static FontRepository fontrep;
	
	MouseInput m_mouse;
	int x;
	int y;
	Color4ub m_fontColor;			// フォントのカラー	
	Color4ub m_selectedBackColor;	// 選択された背景カラー	
	float m_rate;		// フォントの文字サイズ
	bool m_select;
	wchar[] m_text;
	Texture[] m_tex;
	bool m_lclick;
	bool m_rclick;
	Size m_areaSz;
}