//
//	ToolLib_List.cpp
//
//	エディター共通処理 - ツールのGUIのリスト
//
//	* Tue Apr 18 21:46:46 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	* Wed Apr 19 22:11:14 JST 2017 Naoyuki Sawa
//	- 「①DxLibに依存しないようにする」と「②C言語でコンパイル出来るようにする」を対応しました。
//	  詳細は、/clip/ToolLib/README.TXT の、同日のコメントを参照して下さい。
//	* Thu Apr 20 21:31:57 JST 2017 Naoyuki Sawa
//	- 当ライブラリが生成するオブジェクトの識別子を、整数ハンドル⇒ポインタに変更しました。
//	  オリジナル版では生成したオブジェクトを固定長配列に格納して管理していたのですが、メモリ節約のためにリストで管理するように変更し、そのためには整数ハンドルよりもポインタの方が都合が良いからです。
//	  尚、フォントハンドルと文字列入力ハンドルは、当ライブラリが生成するオブジェクトではなくバックエンドのオブジェクトなので、今回の変更には関係無く整数ハンドルのままです。
//	- ToolLib_List.cppの、文字列のメモリ確保の処理を単純化しました。
//	  オリジナル版では高速化のためにある程度まとめてメモリを確保する処理になっていましたが、当実装では単純にstrv形式の関数を使用して都度メモリを確保するように変更しました。
//	  現在の所、高速性が必要な場面で当ライブラリを使用する予定は無いので、速度よりも実装の単純さの方を優先したからです。
//
#include "clip.h"
//*****************************************************************************
//	定数、マクロ、構造体
//*****************************************************************************
#define TOOL_ONE_LINE_HEIGHT		(16)		//リスト1行辺りの縦幅。
//-----------------------------------------------------------------------------
//リスト構造体
/*typedef*/ struct _SToolList {
	SToolVisible*	TVisible;		//関連付けられている表示状態情報。
	SToolWindow*	TWindow;		//関連付けられているウインドウ情報。
	char**		ObjList;		//追加されている行の文字列のアドレスが格納されているポインタ配列。(strv形式)
	int		Width;			//追加されている文字列の中で最大の横幅。
	int		SelectIndex;		//選択されている行。
	int		Change;			//リストの状態に変化があったかどうか。
	int		Visible;		//表示状態。
} /*SToolList*/;
//*****************************************************************************
//	ローカル変数
//*****************************************************************************
static GSList*	g_TList;
//*****************************************************************************
//	ローカル関数宣言
//*****************************************************************************
static void ToolList_Step(SToolList* TList, double StepTime);
static void ToolList_Draw(SToolList* TList);
//*****************************************************************************
//	アプリケーション用関数
//*****************************************************************************
//リスト情報を作成する。
//[out]
//	戻り値			リスト情報。
SToolList* ToolList_Create() {
	SToolList* TList;
	//新しいリスト情報を格納するためのメモリの確保。
	TList = calloc(1, sizeof(SToolList));
	if(!TList) { DIE(); }
	//確保したメモリ領域を保存。
	g_TList = g_slist_append(g_TList, TList);
	//情報を初期化。
//不要	TList->TVisible    = NULL;
//不要	TList->TWindow     = NULL;
	TList->ObjList     = strv_new(NULL);
//不要	TList->Width       = 0;
	TList->SelectIndex = -1;
//不要	TList->Change      = 0;
	TList->Visible     = 1;
	//リスト情報を返す。
	return TList;
}
//-----------------------------------------------------------------------------
//リスト情報を削除する。
//[in]
//	TList			リスト情報。
void ToolList_Delete(SToolList* TList) {
	//行情報用に確保していたメモリを解放。
	strv_free(TList->ObjList);
	//確保していたメモリを解放する。
	g_TList = g_slist_remove(g_TList, TList);
	free(TList);
}
//-----------------------------------------------------------------------------
//全てのリスト情報を削除する。
void ToolList_Delete_All() {
	//有効な全てのリストを削除する。
	while(g_TList) { ToolList_Delete(g_TList->data); }
}
//-----------------------------------------------------------------------------
//リスト情報に関連付ける表示状態情報をセットする。
//[in]
//	TList			リスト情報。
//	TVisible		表示状態情報。
void ToolList_SetVisibleHandle(SToolList* TList, SToolVisible* TVisible) {
	TList->TVisible = TVisible;
}
//-----------------------------------------------------------------------------
//リスト情報を初期化する。
//[in]
//	TList			リスト情報。
//	TWindow			関連付けるウインドウ情報。
void ToolList_Initialize(SToolList* TList, SToolWindow* TWindow) {
	//行情報用に確保していたメモリを解放。
	strv_free(TList->ObjList);
	//情報を初期化する。
	TList->TWindow     = TWindow;
	TList->ObjList     = strv_new(NULL);
	TList->Width       = 0;
	TList->SelectIndex = -1;
	TList->Change      = 0;
	//ウインドウのクライアント領域を初期化。
	ToolWindow_SetClientSize(TList->TWindow, 16, 16);
}
//-----------------------------------------------------------------------------
//リスト情報の選択されている行の変更などが発生したかどうかを取得する。
//[in]
//	TList			リスト情報。
//	Reset			変更が発生したかどうかの情報をリセットするかどうか。
//[out]
//	戻り値			変更が発生したかどうか。(0以外:変更が発生した,0:変更は発生していない)
int ToolList_GetChange(SToolList* TList, int Reset) {
	int Result = TList->Change;
	if(Reset) { TList->Change = 0; }
	return Result;
}
//-----------------------------------------------------------------------------
//リスト情報に行情報を追加する。
//[in]
//	TList			リスト情報。
//	String			追加する行に表示する文字列。
//	...			可変個数引数。
void ToolList_AddObj(SToolList* TList, const char* String, ...) {
	va_list VaList;
	char* FixString;
	int DrawWidth;
	//可変個引数から追加する文字列を構築する。
	va_start(VaList, String);
	FixString = strdup_vprintf(String, VaList);
	va_end(VaList);
	//追加する行の文字列の横幅を取得する。
	DrawWidth = ToolFunc_GetDrawStringWidthToHandle(FixString, -1, ToolStatic_GetNormalFontHandle());
	//今までの最大横幅より大きい場合は最大横幅を更新。
	if(DrawWidth > TList->Width) { TList->Width = DrawWidth; }
	//新しい行の情報を保存。
	strv_push(&TList->ObjList, FixString);
	//ウインドウのクライアント領域を更新。
	ToolWindow_SetClientSize(TList->TWindow, TList->Width, ToolList_GetHeight(TList));
	//一つ目の行だった場合は、追加した行を選択状態にする。
	if(ToolList_GetObjNum(TList) == 1) { ToolList_SetSelectIndex(TList, 0); }
}
//-----------------------------------------------------------------------------
//リスト情報の表示状態を変更する。
//[in]
//	TList			リスト情報。
//	Visible			新しい表示状態。
void ToolList_SetVisible(SToolList* TList, int Visible) {
	//新しい表示状態を保存する。
	TList->Visible = Visible;
}
//-----------------------------------------------------------------------------
//リスト情報の表示状態を取得する。
//[in]
//	TList			リスト情報。
//[out]
//	戻り値			表示状態。(0以外:表示,0:非表示)
int ToolList_GetVisible(SToolList* TList) {
	//表示状態が0の場合は0を返す。
	if(!TList->Visible) { return 0; }
	//関連付けられているウインドウ情報の表示状態が0の場合は0を返す。
	if(!ToolWindow_GetVisible(TList->TWindow)) { return 0; }
	//表示状態情報が登録されていて、且つ表示状態情報の表示状態が0の場合は0を返す。
	if(!ToolVisible_GetVisible(TList->TVisible)) { return 0; }
	//ここにきていたら非表示ではないということなので0以外を返す。
	return 1;
}
//-----------------------------------------------------------------------------
//リスト情報の選択行を変更する。
//[in]
//	TList			リスト情報。
//	Index			行番号。
void ToolList_SetSelectIndex(SToolList* TList, int Index) {
	TList->SelectIndex = Index;
}
//-----------------------------------------------------------------------------
//リスト情報の選択している行を取得する。
//[in]
//	TList			リスト情報。
//[out]
//	戻り値			選択している行の番号。(どの行も選択されていない場合は-1)
int ToolList_GetSelectIndex(SToolList* TList) {
	return TList->SelectIndex;
}
//-----------------------------------------------------------------------------
//リスト情報に追加した行の数を取得する。
//[in]
//	TList			リスト情報。
int ToolList_GetObjNum(SToolList* TList) {
	return strv_length(TList->ObjList);
}
//-----------------------------------------------------------------------------
//リスト情報に追加した全ての行をあわせた縦幅を取得する。
//[in]
//	TList			リスト情報。
//[out]
//	戻り値			リストの全行の縦幅。(ピクセル単位)
int ToolList_GetHeight(SToolList* TList) {
	return TOOL_ONE_LINE_HEIGHT * ToolList_GetObjNum(TList);
}
//-----------------------------------------------------------------------------
//リスト情報に追加した全ての行をあわせた横幅を取得する。
//[in]
//	TList			リスト情報。
//[out]
//	戻り値			リストの全行の横幅。(ピクセル単位)
int ToolList_GetWidth(SToolList* TList) {
	return TList->Width;
}
//-----------------------------------------------------------------------------
//全てのリスト情報の状態推移処理を行う。
//[in]
//	StepTime		進める時間。(単位:秒)
void ToolList_Step_All(double StepTime) {
	GSList* list = g_TList;
	while(list) {
		ToolList_Step(list->data, StepTime);
		list = list->next;
	}
}
//-----------------------------------------------------------------------------
//全てのリスト情報の描画移処理を行う。
void ToolList_Draw_All() {
	GSList* list = g_TList;
	while(list) {
		ToolList_Draw(list->data);
		list = list->next;
	}
}
//*****************************************************************************
//	ローカル関数
//*****************************************************************************
//リスト情報の状態推移処理を行う。
//[in]
//	TList			リスト情報。
//	StepTime		進める時間。(単位:秒)
static void ToolList_Step(SToolList* TList, double StepTime) {
	int MouseX, MouseY;
	RECT InfoArea;
	int DrawX, DrawY;
	int MouseOffsetX, MouseOffsetY;
	int Index;
	//表示状態が0の場合は何もせず終了。
	if(!ToolList_GetVisible(TList)) { return; }	//ここまで
	//マウスの左ボタンが押された瞬間ではない場合は特に何もしない。
	if(!(ToolStatic_GetMouseEdgeButton() & (1<<0)/*MOUSE_INPUT_LEFT*/)) { return; }	//ここまで
	//マウスカーソルの位置を取得。
	ToolStatic_GetMousePosition(&MouseX, &MouseY);
	//関連付けされているウインドウ情報の情報表示領域を取得。
	InfoArea = ToolWindow_GetInfoArea(TList->TWindow);
	//関連付けされているウインドウ情報のスクロールバーの状態も考慮したクライアント領域の描画原点となるスクリーン座標を取得する。
	ToolWindow_GetDrawLeftUpPos(TList->TWindow, &DrawX, &DrawY);
	//リスト情報上でのマウスの位置を算出。
	MouseOffsetX = MouseX - DrawX;
	MouseOffsetY = MouseY - DrawY;
	//マウスのボタンを押した瞬間がリストの領域から外れていたら何もせず終了。
	if((MouseX < InfoArea.left) || (MouseX >= InfoArea.right ) ||
	   (MouseY < InfoArea.top ) || (MouseY >= InfoArea.bottom) ||
	   ( MouseOffsetY                         <  0) ||
	   ((MouseOffsetY / TOOL_ONE_LINE_HEIGHT) >= ToolList_GetObjNum(TList))) { return; }	//ここまで
	//マウスカーソルの位置に該当する行を算出。
	Index = MouseOffsetY / TOOL_ONE_LINE_HEIGHT;
	//マウスカーソルの位置にある行の文字列部分にカーソルがなかったら何もせずに終了。
	if((MouseOffsetX <  0) ||
	    MouseOffsetX >= ToolFunc_GetDrawStringWidthToHandle(TList->ObjList[Index], -1, ToolStatic_GetNormalFontHandle())) { return; }	//ここまで
	//選択している行の変更。
	ToolList_SetSelectIndex(TList, Index);
	//状態に変化があったかどうかのフラグを立てる。
	TList->Change = 1;
}
//-----------------------------------------------------------------------------
//リスト情報を画面に描画する。
//[in]
//	TList			リスト情報。
static void ToolList_Draw(SToolList* TList) {
	int WinDrawX, WinDrawY;
	int ClientY;
	RECT InfoArea;
	int NormalColor, SelectColor;
	int NormalFontHandle;
	int i, n;
	//表示状態が0の場合は何もせず終了。
	if(!ToolList_GetVisible(TList)) { return; }	//ここまで
	//ウインドウ情報のスクロールバーの状態も考慮したクライアント領域の描画原点となるスクリーン座標を取得する。
	ToolWindow_GetDrawLeftUpPos(TList->TWindow, &WinDrawX, &WinDrawY);
	//ウインドウ情報のスクロールバーの状態も考慮したクライアント領域の描画原点となる座標を取得する。
	ToolWindow_GetClientArea(TList->TWindow, NULL, &ClientY);
	//ウインドウ情報を元に SetDrawArea で描画領域を制限する設定を行う。
	ToolWindow_SetupDrawArea(TList->TWindow);
	//ウインドウ情報の情報表示領域を取得する。
	InfoArea = ToolWindow_GetInfoArea(TList->TWindow);
	//通常の文字列の描画色と選択されている文字列の描画色を取得する。
	NormalColor = ToolFunc_GetColor(255,255,255);
	SelectColor = ToolFunc_GetColor(255,  0,  0);
	//文字列描画用のフォントハンドルを取得。
	NormalFontHandle = ToolStatic_GetNormalFontHandle();
	//行の描画ループ。
	n = ToolList_GetObjNum(TList);
	for(i = ClientY / TOOL_ONE_LINE_HEIGHT; i < n; i++) {
		//描画位置が情報表示領域から外れていたらループを抜ける。
		if((i * TOOL_ONE_LINE_HEIGHT + WinDrawY) > InfoArea.bottom) { break; }
		//行の文字列を描画。
		ToolFunc_DrawStringToHandle(WinDrawX,
		                            WinDrawY + i * TOOL_ONE_LINE_HEIGHT,
		                            TList->ObjList[i],
		                           (TList->SelectIndex == i) ? SelectColor : NormalColor,
		                            NormalFontHandle);
	}
	//描画可能範囲を元に戻す。
	ToolFunc_SetDrawAreaFull();
}
