module kyojintati4d.component.music;

private import std.conv;
private import std.stream;

private import y4d_aux.filesys;
private import y4d_aux.widestring;
private import y4d_draw.drawbase;
private import y4d_draw.fontloader;
private import y4d_draw.fontrepository;
private import y4d_draw.screen;
private import y4d_draw.texture;
private import y4d_draw.textureloader;
private import y4d_draw.scenariodraw;
private import y4d_draw.sprite;
private import y4d_input.mouse;
private import y4d_input.keyinputbase;
private import y4d_sound.soundloader;
private import y4d_math.rand;
private import y4d_timer.timer;

private import yamalib.draw.textdraw;
private import yamalib.gui.guibutton;
private import yamalib.gui.guilistbox;
private import yamalib.gui.guislidebar;
private import yamalib.gui.guiscrollbar;
private import yamalib.gui.keygroup;
private import yamalib.auxil.properties;
private import yamalib.log.log;

private import kyojintati4d.val.kyojinconst;

/**
	音楽室のコンポーネント基本クラス
*/
abstract class ComponentBase {
	
	abstract void onMove(Screen);
	abstract void onDraw(Screen);
	
	/// 基本となる下地テクスチャの設定
	void setMainTexture(Texture t_) {
		this.m_main = t_;
	}
	
	/// 描画位置の設定
	void setDrawPos(int x_, int y_) {
		this.m_dx = x_;
		this.m_dy = y_;
	}
	
	/// 描画位置の取得
	void getDrawPos(out int x_, out int y_) {
		x_ = this.m_dx;
		y_ = this.m_dy;
	}
	
	/// マウスの設定
	void setMouse( MouseInput m_ ) {
		this.m_mouse = m_;
	}
	
	/// このタスクの終了リクエスト
	bool isExitReq() {
		return false;
	}
	
	/// コンストラクタ
	this( MouseInput m_ ) {
		setMouse( m_ );
	}
	
protected:

	/// 共通プロパティの取得
	static Properties getGenProp() {
		if ( m_propGeneral is null ) {
			m_propGeneral = Properties.getInstance(cast(char[]) "img2/music/skin/basic/general.txt");
//			m_propGeneral.load("img2/music/skin/basic/general.txt");
		}
		return m_propGeneral;
	}

	static const char[] C_PROP_KEY_TTL_POSX = "title_posx";
	static const char[] C_PROP_KEY_TTL_POSY = "title_posy";
	static const char[] C_PROP_KEY_DSC_POSX = "disc_posx";
	static const char[] C_PROP_KEY_DSC_POSY = "disc_posy";
	static const char[] C_PROP_KEY_MAIN_POSX = "main_posx";
	static const char[] C_PROP_KEY_MAIN_POSY = "main_posy";
	static const char[] C_PROP_KEY_PL_POSX = "pl_posx";
	static const char[] C_PROP_KEY_PL_POSY = "pl_posy";
	static const char[] C_PROP_KEY_PL_SZX = "pl_sizex";
	static const char[] C_PROP_KEY_PL_SZY = "pl_sizey";
	static const char[] C_PROP_KEY_BACK_BT_X = "back_bt_x";
	static const char[] C_PROP_KEY_BACK_BT_Y = "back_bt_y";
	
	MouseInput m_mouse;
	Texture m_main;	//!< 基本画像
	

	int m_dx;	// 基準となる描画位置
	int m_dy;

private:	
	static Properties m_propGeneral;

}

/**
	音楽室のコンポーネントメイン制御パネル
*/
class MainParts : ComponentBase {
	
	/// 終了要請
	override bool isExitReq() {
		return exit;
	}
	
	/// 再生全曲リストを設定
	void setSoundLoader( SoundLoader sl_) {
		this.m_bgm = sl_;
	}
	
	/// 再生番号を取得
	int getPlayNum() {
		return m_playNo[m_selNo];
	}
	
	/// 外部からも再生要求を受け付けるのだ
	void setPlayNum(int n) {
		this.m_playReq = n;
	}
	
	/// 外部とのインターフェース
	void setVolume(int n) {
		m_volBar.setSlidePos( cast(float)n / 100.0f );
		this.m_vol = cast(int) (100 * m_volBar.getSlidePos());
	}
	
	/// ボリュームの取得
	int getVolume() {
		return cast(int) (100 * m_volBar.getSlidePos());
	}
	
	void setInputDevice(KeyInputBase key, MouseInput mouse) {
		m_keyInput = key;
		m_mouse = mouse;
	}
	
	/// 毎回呼び出すなり
	override void onMove(Screen screen) {
		if ( !init ) {
			onInit();
		}

		m_volBar.onMove(screen);
		if ( m_volBar.isModifiy() ) {
			m_vol = cast(int) (100 * m_volBar.getSlidePos());

			// 今なってる！
			if ( -1 != m_playNoReal ) {
				this.m_bgm.get( m_playNoReal ).setVolume( m_vol );
			}
		}
		// ボリュームバー
		if ( m_volBar.isIn() ) {
			m_keyGroup.setSelectNo(m_volBar);
		}

		// ボタン
		int click = -1;
		foreach (int i,inout GUIButton bt; m_Bts) {
			bt.onMove(screen);
			if ( bt.isLClick() ) {
				click = i;
				break;
			}
		}

		if ( !(m_keyInput is null) ) {
			// キー操作によるフォーカシング
			if ( m_keyInput.isPush(1) ) {
				m_keyGroup.prev();
			} else if ( m_keyInput.isPush(2) ) {
				m_keyGroup.next();
			}
		}

		
		// 外部からの再生要求！（プレイリストからとか...）
		if (-1 != m_playReq) {
			int found = -1;
			foreach (int i, int num; m_playNo) {
				if (num == m_playReq) {
					found = i;
					break;
				}
			}
			if ( -1 != found ) {
				m_selNo = found;
				click = BT_TYPE.PLAY;
			}
			
			m_playReq = -1;
		}
		
		// なってるのに止まってる次へ
		if ( -1 != m_playNoReal && !m_pause && !m_loading) {
			if ( !this.m_bgm.isPlay(m_playNoReal) 
					&& !this.m_bgm.get( m_playNoReal ).isLoading() ) {
				click = BT_TYPE.NEXT;
			}
		}
		
		// ロード完了時にタイマースタート！
		if ( -1 != m_playNoReal && m_loading && !this.m_bgm.get( m_playNoReal ).isLoading()) {
			m_timer.reset();
			m_loading = false;
		}

		// ボタン押下処理
		if (-1 != click) {
			switch ( click ) {
			case BT_TYPE.PREV:
				// 一時停止解除
				m_pause = false;

				int oldNo = m_selNo;
				--m_selNo;
				if ( m_selNo < 0 ) {
					// 全曲リピートならば最後尾へ
					if ( m_repeatState == REP_TYPE.ALL ) {
						m_selNo = m_playNo.length - 1;
					} else {
						m_selNo = 0;
					}
				}
				
				// 前にBGMが再生されていたならば
				if ( -1 != m_playNoReal ) {
					//this.m_bgm.get( m_playNo[oldNo] ).stop();
					this.m_bgm.stopBGM();
					// 新しい曲を再生する
					this.m_bgm.get( m_playNo[m_selNo] ).setLoop(m_loop);
					this.m_bgm.playBGM(m_playNo[m_selNo]);
					this.m_bgm.get(m_playNo[m_selNo] ).setVolume( m_vol );
					m_playNoReal = m_playNo[m_selNo];
					m_timer.reset();
					m_timer.pause();
					m_loading = true;
				}
				
				break;
				
			case BT_TYPE.PLAY:
			
				if ( m_pause && -1 != m_playNoReal) {
					// 一時停止から再開
					this.m_bgm.get( m_playNoReal ).resume();
					m_timer.restart();
					m_pause = false;
				} else {
					// 通常再生
					this.m_bgm.stopBGM();
					if ( -1 != m_playNoReal ) {
						this.m_bgm.stopBGM();
					}
					if ( !this.m_bgm.isPlay(m_playNo[m_selNo]) ) {
						this.m_bgm.get( m_playNo[m_selNo] ).setLoop(m_loop);
						this.m_bgm.playBGM(m_playNo[m_selNo]);
						this.m_bgm.get(m_playNo[m_selNo] ).setVolume( m_vol );
						m_playNoReal = m_playNo[m_selNo];
						m_timer.reset();
						m_timer.pause();
						m_loading = true;
					}

				}
				break;
				
			case BT_TYPE.PAUSE:
				if ( -1 != m_playNoReal ) {
					if ( !m_pause ) {
						// 一時停止になる
						this.m_bgm.get( m_playNoReal ).pause();
						m_timer.pause();
						m_pause = true;
					} else {
						// 再開する
						this.m_bgm.get( m_playNoReal ).resume();
						m_timer.restart();
						m_pause = false;
					}
				}
				break;
				
			case BT_TYPE.STOP:
				// 一時停止解除
				m_pause = false;
				if ( -1 != m_playNoReal ) {
					this.m_bgm.stopBGM();
				}
				m_playNoReal = -1;
				break;
				
			case BT_TYPE.NEXT:
				// 一時停止解除
				m_pause = false;
				int oldNo = m_selNo;
				++m_selNo;
				if ( m_selNo >= m_playNo.length ) {
					// 全曲リピートならば頭へ
					if ( m_repeatState == REP_TYPE.ALL ) {
						m_selNo = 0;
					} else {
						m_selNo = m_playNo.length - 1;
//						m_playNoReal = -1;
						break;
					}
				} 
				
				// 前にBGMが再生されていたならば
				if ( -1 != m_playNoReal ) {
					this.m_bgm.stopBGM();
					// 新しい曲を再生する
					this.m_bgm.get(m_playNo[m_selNo]).setLoop(m_loop);
					this.m_bgm.get(m_playNo[m_selNo] ).setVolume( m_vol );
					this.m_bgm.playBGM(m_playNo[m_selNo]);
					m_playNoReal = m_playNo[m_selNo];
					m_timer.reset();
					m_timer.pause();
					m_loading = true;
				}
				
				break;
				
			case BT_TYPE.REPEAT:
				++m_repeatState;
				if ( m_repeatState > REP_TYPE.max ) {
					m_repeatState = REP_TYPE.min;
				}

				if (REP_TYPE.ALL == m_repeatState) {
					GUINormalButtonListener v = cast(GUINormalButtonListener) m_btRepeat.getEvent();
					v.setReverse(true);
					m_btRepeat.setEvent(v);
				} else {
					GUINormalButtonListener v = cast(GUINormalButtonListener) m_btRepeat.getEvent();
					v.setReverse(false);
					m_btRepeat.setEvent(v);
				}
				break;
				
			case BT_TYPE.SHUFFLE:
				m_shuff = cast(bool) !m_shuff;
				
				int no = m_playNo[m_selNo];
				
				// シャッフル処理
				if ( m_shuff ) {
					randomizeList();
				} else {
					normalizeList();
				}
	
				foreach (int i, int num; m_playNo) {
					if (num == no) {
						m_selNo = i;
						break;
					}
				}

				if (m_shuff) {
					GUINormalButtonListener v = 
						cast(GUINormalButtonListener) m_btShuffle.getEvent();
					v.setReverse(true);
					m_btShuffle.setEvent(v);
				} else {
					GUINormalButtonListener v = 
						cast(GUINormalButtonListener) m_btShuffle.getEvent();
					v.setReverse(false);
					m_btShuffle.setEvent(v);
				}
				break;
				
			case BT_TYPE.RETURN:
				this.m_bgm.stopBGM();
				m_playNoReal = -1;
				exit = true;
				break;

			default:
				break;
			}
		}
		
	}
	
	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		// プレイヤー
		screen.blt(m_main, m_dx, m_dy);
		
		// タイマ秒数
		if ( -1 != m_playNoReal ) {
			// mmss の列
			int[] nums;
			int time = m_timer.get() / 1000;
			int mm = time / 60;
			int ss = time % 60;
			
			nums ~= mm / 10;
			nums ~= mm % 10;
			nums ~= 10;			// これコロンね
			nums ~= ss / 10;
			nums ~= ss % 10;

			foreach (int i, int dire; nums) {
				m_sprite.setDirection( dire );
				m_sprite.blt( screen, m_dx + m_numx + i * 20, m_dy + m_numy );
			}
		}
		
		// ボタン描画
		foreach (int i,inout GUIButton bt; m_Bts) {
			bt.onDraw(screen);
		}

		// ボリュームバー描画
		m_volBar.onDraw(screen);
	}
	
	/// コンストラクタ
	this( MouseInput m_ ) {
		super( m_ );
		m_repeatState = REP_TYPE.NO;
		m_timer = new Timer();
		m_keyGroup = new KeyGroup();
	}
	
	/// 最後に再生した（している）曲番を返す
	int getLastPlayNo() {
		if ( -1 != m_playNoReal ) {
			return m_playNoReal;
		} else {
			return m_playNo[m_selNo];	
		}
	}
	
	/// 静的コンストラクタ
	static this() {
		string[] tmp;
		tmp ~= C_PROP_KEY_PRE_X;
		tmp ~= C_PROP_KEY_PRE_Y;
		tmp ~= C_PROP_KEY_PLAY_X;
		tmp ~= C_PROP_KEY_PLAY_Y;
		tmp ~= C_PROP_KEY_PAUSE_X;
		tmp ~= C_PROP_KEY_PAUSE_Y;
		tmp ~= C_PROP_KEY_STOP_X;
		tmp ~= C_PROP_KEY_STOP_Y;
		tmp ~= C_PROP_KEY_NEXT_X;
		tmp ~= C_PROP_KEY_NEXT_Y;
		tmp ~= C_PROP_KEY_REPEART_X;
		tmp ~= C_PROP_KEY_REPEART_Y;
		tmp ~= C_PROP_KEY_SHUFF_X;
		tmp ~= C_PROP_KEY_SHUFF_Y;
		tmp ~= C_PROP_KEY_VOL_X;
		tmp ~= C_PROP_KEY_VOL_Y;
		
		PROP_KEYS = cast(char[][]) tmp;
		
		rand = new Rand();
		rand.randomize();
	}
	
private:

	/** プロパティキー */
	static const char[] C_PROP_KEY_PRE_X = "prev_offsetx";
	static const char[] C_PROP_KEY_PRE_Y = "prev_offsety";
	static const char[] C_PROP_KEY_PLAY_X = "play_offsetx";
	static const char[] C_PROP_KEY_PLAY_Y = "play_offsety";
	static const char[] C_PROP_KEY_PAUSE_X = "pause_offsetx";
	static const char[] C_PROP_KEY_PAUSE_Y = "pause_offsety";
	static const char[] C_PROP_KEY_STOP_X = "stop_offsetx";
	static const char[] C_PROP_KEY_STOP_Y = "stop_offsety";
	static const char[] C_PROP_KEY_NEXT_X = "next_offsetx";
	static const char[] C_PROP_KEY_NEXT_Y = "next_offsety";
	static const char[] C_PROP_KEY_REPEART_X = "repeat_offsetx";
	static const char[] C_PROP_KEY_REPEART_Y = "repeat_offsety";
	static const char[] C_PROP_KEY_SHUFF_X = "shuff_offsetx";
	static const char[] C_PROP_KEY_SHUFF_Y = "shuff_offsety";
	static const char[] C_PROP_KEY_NUM_X = "num_offsetx";
	static const char[] C_PROP_KEY_NUM_Y = "num_offsety";
	static const char[] C_PROP_KEY_VOL_X = "vol_offsetx";
	static const char[] C_PROP_KEY_VOL_Y = "vol_offsety";
	static const char[] C_PROP_KEY_VOL_BAR_X = "vol_bar_offsetx";
	static const char[] C_PROP_KEY_VOL_BAR_Y = "vol_bar_offsety";
	static const char[] C_PROP_KEY_VOL_WIDTH = "vol_slide_width";

	/** キーのコレクション */
	static char[][] PROP_KEYS;

	/** ボタンの名前付きインデックス */
	enum BT_TYPE : int {
		PREV = 0,
		PLAY,
		PAUSE,
		STOP,
		NEXT,
		REPEAT,
		SHUFFLE,
		RETURN,
		VOL,
	};

	/** リピート状態 */
	enum REP_TYPE : int {
		NO = 0,	// なし
//		TRACK,	// トラック
		ALL		// 前曲
	};

	/// スワップ関数
	static void swap(inout int n1, inout int n2) {
		int tmp = n1;
		n1 = n2;
		n2 = tmp;
	}

	/// 初期化処理
	void onInit() {
		
		Log.print("MainParts init!");
		
		Properties propGen = getGenProp();
		m_dx = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_MAIN_POSX, 0);
		m_dy = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_MAIN_POSY, 0);
		
		m_prop = Properties.getInstance(cast(char[]) "img2/music/skin/basic/main.txt");
		m_main = new Texture();
		m_main.load(cast(char[]) "img2/music/skin/basic/main.png");

		// 再生リスト構築
		int maxNo = this.m_bgm.getInfoList().size();
		m_playNo = null;
		for (int i = 0; i < maxNo; ++i) {
			m_playNo ~= i;
		}
		
		// ボタン構築
		createButton();

		// タイトル画面へ戻るボタン表示位置を手動設定
		int rx = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_BACK_BT_X, 0);
		int ry = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_BACK_BT_Y, 0);
		m_btBack.setXY(m_dx + rx, m_dy + ry);
		
		// ボリューム
		createVolBar();
		
		m_keyGroup.add(m_volBar);
		m_keyGroup.add(m_btPrev);
		m_keyGroup.add(m_btPlay);
		m_keyGroup.add(m_btPause);
		m_keyGroup.add(m_btStop);
		m_keyGroup.add(m_btNext);
		m_keyGroup.add(m_btRepeat);
		m_keyGroup.add(m_btShuffle);
		m_keyGroup.add(m_btBack);
		
		
		// 秒数用スプライト
		m_sl = new SpriteLoader();
		m_sprite = new SpriteEx();
		
		m_sl.load(cast(char[]) "img2/music/skin/basic/num.sdf");
		m_sprite.setSprite( m_sl.getSprite() );
		
		m_numx = cast(int) m_prop.getPropertyNum(cast(char[]) C_PROP_KEY_NUM_X, 0);
		m_numy = cast(int) m_prop.getPropertyNum(cast(char[]) C_PROP_KEY_NUM_Y, 0);
		
		Log.print("mainCtl init finish");
		
		init = true;
	}
	
	/// 再生リストをシャッフルする
	void randomizeList() {
		int size = m_playNo.length;
		for ( int i = 0; i < size*10; ++i) {
			swap( m_playNo[ rand.get(size) ], m_playNo[ rand.get(size) ] );
		}
	}
	
	/// 再生リストを正規化する
	void normalizeList() {
		int size = m_playNo.length;
		m_playNo = null;
		for (int i = 0; i < size; ++i) {
			m_playNo ~= i;
		}
	}

	/// ボタンパーツの初期化
	void createButton() {
		
		m_tlBt = new TextureLoader();
		m_tlBt.loadDefRW( FileSys.read(cast(char[]) "img2/music/skin/basic/player.lst") );
		
		m_btPrev = new GUIButton();
		m_btPlay = new GUIButton();
		m_btPause = new GUIButton();
		m_btStop = new GUIButton();
		m_btNext = new GUIButton();
		m_btRepeat = new GUIButton();
		m_btShuffle = new GUIButton();
		m_btBack = new GUIButton();
		

		// 配列に格納して繰り返し処理
		GUIButton[] vBt;
		vBt ~= m_btPrev;
		vBt ~= m_btPlay;
		vBt ~= m_btPause;
		vBt ~= m_btStop;
		vBt ~= m_btNext;
		
		GUINormalButtonListener v;
		foreach(int i, inout GUIButton bt; vBt) {
			v  = new GUINormalButtonListener();
			v.setTextureLader(this.m_tlBt, i*3);
			v.setType(128);

			bt.setMouse(m_mouse);
			bt.setEvent(v);
		}
		
		// リピート　３段階
		v  = new GUINormalButtonListener();
		v.setTextureLader(this.m_tlBt, 15);
		v.setType(128 | 2);
		m_btRepeat.setMouse(m_mouse);
		m_btRepeat.setEvent(v);
		
		
		// シャッフル２段階
		v  = new GUINormalButtonListener();
		v.setTextureLader(this.m_tlBt, 21);
		v.setType(128 | 2);
		m_btShuffle.setMouse(m_mouse);
		m_btShuffle.setEvent(v);
		
		// タイトル画面に戻る
		v  = new GUINormalButtonListener();
		v.setTextureLader(this.m_tlBt, 27);
		v.setType(128 | 2);
		m_btBack.setMouse(m_mouse);
		m_btBack.setEvent(v);
		

		// コレクションにしておく		
		m_Bts = vBt;
		m_Bts ~= m_btRepeat;
		m_Bts ~= m_btShuffle;
		m_Bts ~= m_btBack;
		
		int[] points = getBtPos();
		
		// 描画位置設定
		foreach (int i,inout GUIButton bt; m_Bts) {
			bt.setXY( m_dx+points[i*2], m_dy+points[i*2+1] );
		}
		
	}
	
	/// 音量バー構築
	void createVolBar() {
		// プロパティより取得
		int vlength = cast(int) m_prop.getPropertyNum( cast(char[]) C_PROP_KEY_VOL_WIDTH, 100 );
		int box = cast(int) m_prop.getPropertyNum( cast(char[]) C_PROP_KEY_VOL_BAR_X, 0 );
		int boy = cast(int) m_prop.getPropertyNum( cast(char[]) C_PROP_KEY_VOL_BAR_Y, 0 );
		int ox = cast(int) m_prop.getPropertyNum( cast(char[]) C_PROP_KEY_VOL_X, 0 );
		int oy = cast(int) m_prop.getPropertyNum( cast(char[]) C_PROP_KEY_VOL_Y, 0 );

		// 音量バー
		m_volBar = new Slidebar();
		m_volBar.setMouse( m_mouse );
		m_volBar.setKey( m_keyInput );
		m_volBar.setXY( m_dx + ox, m_dy + oy );
		
		Texture tmpBase = new Texture();
		Texture tmpBar = new Texture();
		tmpBase.load(cast(char[]) "img2/music/skin/basic/base.png");
		tmpBar.load(cast(char[]) "img2/music/skin/basic/bar.png");
		
		m_volBar.setBaseTexture(tmpBase);
		m_volBar.setBarTexture(tmpBar);
		m_volBar.setLength(vlength);
		m_volBar.setSliderOffset( box, boy);
	}
	
	/// プロパティからボタンのオフセットを配列として取得する
	int[] getBtPos() {
		int[] values;
		foreach (inout char[] key; PROP_KEYS) {
			values ~= m_prop.getPropertyNum( key, -1 );
		}
		return values;
	}
	
	static Rand rand;			//!< シャッフル用

	Properties m_prop;			//!< プロパティ
	TextureLoader m_tlBt;		//!< ボタン用コレクション
	SoundLoader m_bgm;			//!< 再生する曲
	bool init;
	bool exit;					//!< 終了要請
	bool m_pause;				//!< 一時停止中か
	Timer m_timer;				//!< 疑似再生タイムを行う
	bool m_loading;				//!< ローダースレッドでロード中
	
	SpriteLoader m_sl;
	SpriteEx m_sprite;			//!< 数字
	int m_numx;					//!< 数字描画オフセット
	int m_numy;
	
	int	m_playReq = -1;			//!< 外部からの再生リクエスト
	int m_playNoReal = -1;
	int m_loop = 0;				//!< ループならば-1
	int m_vol;

	int[] m_playNo;				//!< 曲の再生順が格納されている配列
	int m_selNo;				//!< 現在選択している曲インデックス
	
	bool m_shuff;				//!< シャッフルステート
	int m_repeatState;			//!< リピートの状態（オフ、トラック、全曲）
	
	MouseInput m_mouse;
	KeyInputBase m_keyInput;
	KeyGroup m_keyGroup;
	Slidebar m_volBar;			//!< ボリュームバー
	GUIButton m_btPrev;			//!< 前曲
	GUIButton m_btPlay;			//!< 再生
	GUIButton m_btPause;		//!< 一時停止
	GUIButton m_btStop;			//!< 停止
	GUIButton m_btNext;			//!< 次曲
	GUIButton m_btRepeat;		//!< リピート
	GUIButton m_btShuffle;		//!< シャッフル
	GUIButton m_btBack;			//!< タイトル画面へ戻る
	
	GUIButton[] m_Bts;			//!< ボタンのコンテナとして使用
}

/**
	音楽室のコンポーネントプレイリスト
*/
class PlayList : ComponentBase {

	/// 新規選択されたかどうか
	bool isNewSelected() {
		return m_lstBox.isNewSelect();
	}
	
	/// 新規データのファイルパス
	int getSelectedNum() {
		return m_lstBox.getSelectedNum();
	}
	
	/// 選択状態にする
	void setSelect( int num ) {
		m_lstBox.setSelect(num);
	}

	/// 毎回呼び出すなり
	override void onMove(Screen screen) {
		if ( !init ) {
			onInit();
		}
		m_lstBox.onMove(screen);
	}
	
	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		
		// 枠描画
		screen.blt( m_upper, m_dx, m_dy );
		
		// 左右描画
		int unitHeight = cast(int) m_left.getHeight();
		for (int y = this.m_uy; y < m_lstHeight; y += unitHeight) {
			screen.blt( m_left, m_dx, y );
			screen.blt( m_right, m_rx, y );
		}
		
		// 底描画
		screen.blt( m_bottom, m_dx, m_by);
		
		m_lstBox.onDraw(screen);
	}
	
	/// コンストラクタ
	this( MouseInput m_ ) {
		super( m_ );
	}	
	
private:

	/** フォント指定 */
	static const char[] C_PROP_KEY_FONT = "Font";
	static const char[] C_PROP_KEY_FONT_SIZE = "FontSize";
	static const char[] C_PROP_KEY_FONT_COLOR = "FontColor";
	static const char[] C_PROP_KEY_ITEM_HEIGHT = "LineHeight";
	static const char[] C_PROP_KEY_SEL_BG_COLOR = "SelectedBG";


	/// タイトルリストの読み込み
	static char[][] getTitleList() {
		ubyte[] mem = cast(ubyte[]) FileSys.read(cast(char[]) "img2/music/title.lst");
		if (!mem) return null; // 読み込みエラー
		
		char[][] result;
		
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		while (!m.eof) {
			char[] linebuf = cast(char[]) m.readLine();
			if ( linebuf.length == 0 ) {
				continue;
			}
			
			result ~= linebuf;
		}
		
		return result;
	}
	
	/// 属性文字列からColor4ubを構築して返却する
	static Color4ub getColor4ub(char[] strClr) {
		Color4ub color4ub;
		
		if ( !(strClr is null) && strClr.length >= 6 ) {
			// ごにょごにょする
			ubyte r,g,b;
			
			// 0..Fの文字から数値を逆引きして、それを８ビットにまとめて求める
			// そんな処理でいいかいな...
			with (std.string) {
				b = cast(ubyte) find( hexdigits, strClr[length-1] );
				b |= (cast(ubyte) find( hexdigits, strClr[length-2] )) << 4;
				
				g = cast(ubyte) find( hexdigits, strClr[length-3] );
				g |= (cast(ubyte) find( hexdigits, strClr[length-4] )) << 4;
				
				r = cast(ubyte) find( hexdigits, strClr[length-5] );
				r |= (cast(ubyte) find( hexdigits, strClr[length-6] )) << 4;
			}

			color4ub.setColor(r,g,b);
		}
		
		return color4ub;
	}

	/// 初期化処理
	void onInit() {
		m_prop = Properties.getInstance(cast(char[]) "img2/music/skin/basic/pl.txt");
//		m_prop.load("img2/music/skin/basic/pl.txt");
		
		m_upper = new Texture();
		m_upper.load(cast(char[]) "img2/music/skin/basic/pl_upper.png");
		m_left = new Texture();
		m_left.load(cast(char[]) "img2/music/skin/basic/pl_left.png");
		m_right = new Texture();
		m_right.load(cast(char[]) "img2/music/skin/basic/pl_right.png");
		m_bottom = new Texture();
		m_bottom.load(cast(char[]) "img2/music/skin/basic/pl_bottom.png");

		Properties propGen = getGenProp();
		m_dx = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_PL_POSX, 0);
		m_dy = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_PL_POSY, 0);
		
		this.m_uy = m_dy + cast(int) m_upper.getHeight();

		createListBox();

		// 右描画位置の取得
	
		init = true;
	}
	
	/// リストボックスの生成
	void createListBox() {
		Log.print("PlayList#createListBox start!");
		
		m_lstBox = new GUIListBox();
		auto scrollBarTex = new Texture();
		scrollBarTex.load(cast(char[]) "img2/music/skin/basic/bar2.png");
		auto scrollBar = new TexturalScrollbar();
		scrollBar.setBarTexture(scrollBarTex);
		m_lstBox.setScrollbar(scrollBar);
		m_lstBox.setMouse( cast(MouseInput) m_mouse );
		
		Properties propGen = getGenProp();
		int sizex = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_PL_SZX, 0);
		int sizey = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_PL_SZY, 0);
		
		int offx = cast(int) m_left.getWidth();
		
		RectInt rc;
		rc.setRect(m_dx + offx, m_dy + this.m_uy, m_dx + sizex, m_dy + sizey);
		m_lstBox.setArea(rc);
		m_lstBox.setXY( m_dx + offx, m_dy + this.m_uy );
		

		// 下地テクスチャの設定
		Texture t = new Texture();
		t.load(cast(char[]) "img2/music/skin/basic/pl_base.png");
		m_lstBox.setBaseTexture( t );

		// リストのフォントサイズ
		float fontsz = m_prop.getPropertyDecimal( cast(char[]) C_PROP_KEY_FONT_SIZE, 1.0 );

		// フォントカラー
		char[] fontColor = m_prop.getProperty( cast(char[]) C_PROP_KEY_FONT_COLOR, cast(char[]) "FFFFFF" );
		Color4ub color = getColor4ub( fontColor );
		char[] strBgColor = m_prop.getProperty( cast(char[]) C_PROP_KEY_SEL_BG_COLOR, cast(char[]) "000000" );
		Color4ub bgColor = getColor4ub( strBgColor );
		
		// 位置項目のサイズ
		Size itemSz;
		itemSz.cx = cast(int) t.getWidth();
		itemSz.cy = cast(int) m_prop.getPropertyNum(cast(char[]) C_PROP_KEY_ITEM_HEIGHT, 15 );
		
		// アイテムを作る
		char[][] titleNames = getTitleList();
		foreach ( int i, inout char[] title; titleNames ) {
			SimpleListBoxItem item = new SimpleListBoxItem( cast(MouseInput) m_mouse);
			item.setText( title );
			item.setRate( fontsz );
			item.setFontColor( color );
			item.setSelBackColor( bgColor );
			
			if ( 0 == i ) {
				item.setSelect(true);
			}
			
			// リストのサイズ指定
			item.setSize(itemSz);
			m_lstBox.addItem( cast(ListBoxItem) item );
		} 
		
		// スクロールバーの位置調整
		m_lstBox.getScrollbar().setXY(622,30);
		m_lstBox.getScrollbar().setSize(10,sizey);
		

		// ここで右の描画位置を割り出しておく！！
		this.m_rx = m_dx + offx + cast(int) t.getWidth();
		this.m_lstHeight = cast(int) t.getHeight();
		this.m_by = this.m_uy + this.m_lstHeight;
		
		Log.print("PlayList#createListBox finish!");
	}
	
	Properties m_prop;
	Texture m_upper;
	Texture m_bottom;
	Texture m_left;
	Texture m_right;
	GUIListBox	m_lstBox;	//!< リストボックス
	
	int m_uy;	// リスト描画y
	int m_by;	// リスト底描画位置
	int m_rx;	// 右の描画位置
	int m_lstHeight;
	
	bool init;
}

/**
	音楽室のコンポーネントタイトル
*/
class Title : ComponentBase {
	
	/// テクスチャの設定
	void setTexture(Texture t_) {
		this.m_old = this.m_title;
		m_title = t_;
	}
	
	/// タイトル画像を設定
	void setTitle(Texture t_) {
		this.m_title = t_;
	}
	
	/// 毎回呼び出すなり
	override void onMove(Screen screen) {
		if ( !init ) {
			onInit();
		}
	}
	
	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		screen.blt( m_main, m_dx, m_dy );

		// センタリグ描画
		int ttlx;
		int ttly;
		if ( !(m_main is null) && !(m_title is null) ) {
			ttlx = m_dx + cast(int) ((m_main.getWidth() - m_title.getWidth()) / 2);
			ttly = m_dy + cast(int) ((m_main.getHeight() - m_title.getHeight()) / 2);
		} else {
			ttlx = m_dx;
			ttly = m_dy;
		}
		
		screen.blt( m_title, ttlx, ttly );
	}
	
	/// コンストラクタ
	this( MouseInput m_ ) {
		super( m_ );
	}	
	
private:
	/// 初期化処理
	void onInit() {
		Properties propGen = getGenProp();
		m_dx = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_TTL_POSX, 0);
		m_dy = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_TTL_POSY, 0);

		m_main = new Texture();
		m_main.load(cast(char[]) "img2/music/skin/basic/title_base.png");

		init = true;
	}

	bool init;
	
	Texture m_title;	//!< タイトル画像
	Texture m_old;
}

/**
	音楽室のコンポーネント説明
*/
class Description : ComponentBase {
	
	/// テクスチャの設定
	void setTexture(Texture t_) {
		this.m_old = this.m_bg;
		this.m_bg = t_;
	}
	
	/// 説明番号の設定 
	void setDescNo( int no ) {
		if ( no < 0 || no >= m_filenames.length ) return;
		
		char[] strText = cast(char[])FileSys.read( m_filenames[no] );
		m_tdc.setText( toWCS(strText) );

		m_sd.setTextDrawContext(m_tdc);
		m_sd.updateText();
		drawchar = m_sd.getDrawChar();
	}
	
	/// 毎回呼び出すなり
	override void onMove(Screen screen) {
		if ( !init ) {
			onInit();
		}
	}
	
	///
	void setFontLoader( FontLoader fl ) {
		this.m_fl = fl;
	}
	
	/// 影を描画するか
	void drawShadow(bool drawFlg) {
		m_drawShadow = drawFlg;	
	}
	
	/// 毎回呼び出すなり
	override void onDraw(Screen screen) {
		screen.blt( m_main, m_dx, m_dy );

		// センタリグ描画
		int bgx;
		int bgy;
		if ( !(m_main is null) && !(m_bg is null) ) {
			bgx = m_dx + cast(int) ((m_main.getWidth() - m_bg.getWidth()) / 2);
			bgy = m_dy + cast(int) ((m_main.getHeight() - m_bg.getHeight()) / 2);
			screen.blt( m_bg, bgx, bgy);
		}
		
		// 文
		// テキストの描画
		int tx,ty;
		for(int i = 0; i < drawchar.length; ++i) {
			float size = drawchar[i].size; 
			// 影の描画
			if (m_drawShadow) {
				screen.setColor(0,0,0);
				tx = m_dx + m_ox + cast(int) drawchar[i].pos.x + 2;
				ty = m_dy + m_oy + cast(int) drawchar[i].pos.y + 1;
				
				// 拡大ありで描画
				if (1.0f != size) {
					screen.bltRotate(
						drawchar[i].texture,	// テクスチャ 
						tx, ty, 	// x,y 位置
						0, 			// 回転角
						size, 		// 拡大率
						0			// 描画ベース位置（左上）
						);
				} else {
					screen.blt(drawchar[i].texture, tx, ty);
				}
			}

			// 本文字の描画
			screen.setColor( drawchar[i].color );
			tx = m_dx + m_ox + cast(int) drawchar[i].pos.x;
			ty = m_dy + m_oy + cast(int) drawchar[i].pos.y;
			
			// 拡大ありで描画
			if (1.0f != size) {
				screen.bltRotate(
					drawchar[i].texture,	// テクスチャ 
					tx, ty, 	// x,y 位置
					0, 			// 回転角
					size-0.1f, 		// 拡大率
					0			// 描画ベース位置（左上）
					);
			} else {
				screen.blt(drawchar[i].texture, tx, ty);
			}

			// ルビ影の描画
			screen.setColor( 0, 0, 0 );
			ScenarioTextDraw.DrawChar[] rubi = drawchar[i].rubi;
			foreach (inout ScenarioTextDraw.DrawChar c; rubi) {
				tx = m_dx + m_ox + cast(int) c.pos.x;
				ty = m_dy + m_oy + cast(int) c.pos.y;
				screen.blt(c.texture, tx, ty);
			}

			// ルビの描画
			screen.setColor( 255, 255, 255 );
			foreach (inout ScenarioTextDraw.DrawChar c; rubi) {
				tx = m_dx + m_ox + cast(int) c.pos.x;
				ty = m_dy + m_oy + cast(int) c.pos.y;
				screen.blt(c.texture, tx, ty);
			}
		}		
		
	}
	
	/// コンストラクタ
	this( MouseInput m_ ) {
		super( m_ );
		m_drawShadow = true;
	}	
	
private:

	static const char[] C_PROP_KEY_OFFSETX = "text_offsetx";
	static const char[] C_PROP_KEY_OFFSETY = "text_offsety";

	/// 初期化処理
	void onInit() {
		Properties propGen = getGenProp();
		m_dx = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_DSC_POSX, 0);
		m_dy = cast(int) propGen.getPropertyNum(cast(char[]) C_PROP_KEY_DSC_POSY, 0);
		
		m_prop = Properties.getInstance(cast(char[]) "img2/music/skin/basic/desc.txt");
//		m_prop.load("img2/music/skin/basic/desc.txt");
		m_ox = cast(int) m_prop.getPropertyNum(cast(char[]) C_PROP_KEY_OFFSETX, 5);
		m_oy = cast(int) m_prop.getPropertyNum(cast(char[]) C_PROP_KEY_OFFSETY, 5);
		
		m_main = new Texture();
		m_main.load(cast(char[]) "img2/music/skin/basic/desc_base.png");
		
		m_areax = cast(int) m_main.getWidth();
		m_areay = cast(int) m_main.getHeight();
		
		readLst();
		initTextDraw();
		
		init = true;
	}

	/// TextDrawの初期化	
	void initTextDraw() {
		m_sd = new ScenarioTextDraw();
		m_tdc = new TextDrawContext();
		
		m_tdc.width = (cast(int) m_main.getWidth()) - (m_ox + 30);

		FontRepository fr = m_sd.getFontRepository();
		fr.setLoader(m_fl, KyojinConst.C_FONT_NO_MAIN_TEXT);
		fr.setMax(300);

		// ルビ用フォントローダの設定
		fr = m_sd.getRubiFontRepository();
		fr.setLoader(m_fl, KyojinConst.C_FONT_NO_RUBI);
		fr.setMax(300);

		char[] strText = cast(char[])FileSys.read( m_filenames[0] );
		m_tdc.setText( toWCS(strText) );

		m_sd.setTextDrawContext(m_tdc);
		m_sd.updateText();
		drawchar = m_sd.getDrawChar();
	}
	
	/// ファイルリスト読み込み
	void readLst() {
		ubyte[] mem = cast(ubyte[]) FileSys.read(cast(char[]) "img2/music/desc/lst.txt");
		if (!mem) return; // 読み込みエラー
		
		char[][] result;
		
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		while (!m.eof) {
			char[] linebuf = cast(char[]) m.readLine();
			if ( linebuf.length == 0 ) {
				continue;
			}
			m_filenames ~= linebuf;
		}
//printf("lst num %d\n", m_filenames.length);
	}


	bool init;
	
	TextDrawContext m_tdc;
	ScenarioTextDraw m_sd;
	ScenarioTextDraw.DrawChar[] drawchar;	//!< 描画する矩形情報
	
	Properties m_prop;
	
	FontLoader m_fl;

	bool m_drawShadow;
	Texture m_old;
	Texture m_bg;	// 背景画像
	char[][] m_filenames;
	
	int m_ox;
	int m_oy;
	
	int m_areax;
	int m_areay;
	
}