//
//	ToolLib_Button.c
//
//	エディター共通処理 - ツールの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
//	- 当ライブラリが生成するオブジェクトの識別子を、整数ハンドル⇒ポインタに変更しました。
//	  オリジナル版では生成したオブジェクトを固定長配列に格納して管理していたのですが、メモリ節約のためにリストで管理するように変更し、そのためには整数ハンドルよりもポインタの方が都合が良いからです。
//	  尚、フォントハンドルと文字列入力ハンドルは、当ライブラリが生成するオブジェクトではなくバックエンドのオブジェクトなので、今回の変更には関係無く整数ハンドルのままです。
//	* Tue May 02 22:54:46 JST 2017 Naoyuki Sawa
//	- 各モジュール内のサイズ定義の倍率を一律で調整出来るように、ToolLib.hにTOOL_SCALEの定義を追加しました。
//	  詳細は、ToolLib.hの同日のコメントを参照して下さい。
//	* Sun May 14 23:23:22 JST 2017 Naoyuki Sawa
//	- ToolButton_IsAnyHovered(),ToolButton_IsHovered()関数を追加しました。
//	  詳細は、ToolLib.hの同日のコメントを参照して下さい。
//
#include "clip.h"
//*****************************************************************************
//	定数、マクロ、構造体
//*****************************************************************************
#define TOOL_FIRST_REPEAT_WAIT	(0.2)		//ボタンリピートの最初の待ち時間。
#define TOOL_REPEAT_WAIT	(0.017)		//2回目以降のボタンリピートの間隔。
//-----------------------------------------------------------------------------
//ボタン構造体
/*typedef*/ struct _SToolButton {
	SToolVisible*	TVisible;		//関連付けられている表示状態情報。
	SToolWindow*	TWindow;		//関連付けられているウインドウ情報。
	int		On_Off_SwitchType;	//ON状態とOFF状態が押すたびに切り替わるタイプのボタンかどうか。
	POINT		Position;		//ボタンの位置。
	SIZE		Size;			//ボタンのサイズ。
	char*		Name;			//ボタンの名前の文字列が格納されているメモリアドレスを保存しているポインタ。
	int		Visible;		//表示状態。
	int		Bottom;			//ボタンが押下状態かどうか。
	double		BottomTime;		//ボタンが押下されている時間。
	int		FirstRepeat;		//最初のリピート処理が行われたかどうか。
	int		RepeatBottom;		//リピート入力も考慮したボタン押下状態。
	int		Click;			//ボタンがクリックされたかどうか。
	int		OnOffFlag;		//ボタンのON/OFF状態。
} /*SToolButton*/;
//*****************************************************************************
//	ローカル変数
//*****************************************************************************
static GSList*	g_TButton;
//*****************************************************************************
//	ローカル関数宣言
//*****************************************************************************
static void ToolButton_Step(SToolButton* TButton, double StepTime);
static void ToolButton_Draw(SToolButton* TButton);
//*****************************************************************************
//	アプリケーション用関数
//*****************************************************************************
//ボタン情報を作成する。
//[in]
//	On_Off_SwitchType	ON状態とOFF状態が押すたびに切り替わるタイプのボタンかどうか。
//	Name			名前となる文字列のアドレス。
//	x,y			ボタンを表示する左上座標。
//	w,h			ボタンの幅と高さ。
//[out]
//	戻り値			ボタン情報。
SToolButton* ToolButton_Create(int On_Off_SwitchType, const char* Name, int x, int y, int w, int h) {
	SToolButton* TButton;
	//新しいボタン情報を格納するためのメモリの確保。
	TButton = calloc(1, sizeof(SToolButton));
	if(!TButton) { DIE(); }
	//確保したメモリ領域を保存。
	g_TButton = g_slist_append(g_TButton, TButton);
	//情報を初期化。
//不要	TButton->TVisible          = NULL;
//不要	TButton->TWindow           = NULL;
	TButton->On_Off_SwitchType = On_Off_SwitchType;
	TButton->Position.x        = x;
	TButton->Position.y        = y;
	TButton->Size.cx           = w;
	TButton->Size.cy           = h;
	TButton->Visible           = 1;
//不要	TButton->Bottom            = 0;
//不要	TButton->BottomTime        = 0.0;
//不要	TButton->FirstRepeat       = 0;
//不要	TButton->Click             = 0;
//不要	TButton->OnOffFlag         = 0;
	ToolButton_SetName(TButton, Name);
	//ボタン情報を返す。
	return TButton;
}
//-----------------------------------------------------------------------------
//ボタン情報を削除する。
//[in]
//	TButton			ボタン情報。
void ToolButton_Delete(SToolButton* TButton) {
	//名前保存用にメモリを確保していた場合は解放。
	free(TButton->Name);
	//確保していたメモリを解放する。
	g_TButton = g_slist_remove(g_TButton, TButton);
	free(TButton);
}
//-----------------------------------------------------------------------------
//全てのボタン情報を削除する。
void ToolButton_Delete_All() {
	//有効な全てのボタンを削除する。
	while(g_TButton) { ToolButton_Delete(g_TButton->data); }
}
//-----------------------------------------------------------------------------
//ボタン情報に関連付ける表示状態情報をセットする。
//[in]
//	TButton			ボタン情報。
//	TVisible		表示状態情報。
void ToolButton_SetVisibleHandle(SToolButton* TButton, SToolVisible* TVisible) {
	TButton->TVisible = TVisible;
}
//-----------------------------------------------------------------------------
//ボタン情報の名前を設定する。
//[in]
//	TButton			ボタン情報。
//	Name			名前となる文字列のアドレス。
void ToolButton_SetName(SToolButton* TButton, const char* Name) {
	//既に名前用にメモリを確保していた場合は解放する。
	free(TButton->Name);
	TButton->Name = NULL;
	//名前のアドレスが有効な場合はメモリを確保して保存する。
	if(Name) {
		TButton->Name = strdup(Name);
		if(!TButton->Name) { DIE(); }
	}
}
//-----------------------------------------------------------------------------
//ボタン情報と関連付けるウインドウ情報を設定する。
//[in]
//	TButton			ボタン情報。
//	TWindow			ウインドウ情報。
void ToolButton_SetWindow(SToolButton* TButton, SToolWindow* TWindow) {
	TButton->TWindow = TWindow;
}
//-----------------------------------------------------------------------------
//ボタン情報の位置を変更する。
//[in]
//	TButton			ボタン情報。
//	x,y			変更後のボタンの座標。
void ToolButton_SetPosition(SToolButton* TButton, int x, int y) {
	TButton->Position.x = x;
	TButton->Position.y = y;
}
//-----------------------------------------------------------------------------
//ボタン情報のON/OFF状態を変更する。
//[in]
//	TButton			ボタン情報。
//	Flag			変更後のON/OFF状態。(0以外:ON,0:OFF)
void ToolButton_SetOnOffFlag(SToolButton* TButton, int Flag) {
	TButton->OnOffFlag = Flag;
}
//-----------------------------------------------------------------------------
//ボタン情報のON/OFF状態を取得する。
//[in]
//	TButton			ボタン情報。
//[out]
//	戻り値			ON/OFF状態。(0以外:ON,0:OFF)
int ToolButton_GetOnOffFlag(SToolButton* TButton) {
	return TButton->OnOffFlag;
}
//-----------------------------------------------------------------------------
//ボタン情報のボタンがクリックされたかどうかを取得する。
//[in]
//	TButton			ボタン情報。
//	Reset			クリックされたかどうかの状態をリセットするかどうか。(0以外:リセットする,0:リセットしない)
//[out]
//	戻り値			ボタンがクリックされたかどうか。(0以外:クリックされた,0:クリックされていない)
int ToolButton_GetClick(SToolButton* TButton, int Reset) {
	int Ret = TButton->Click;
	if(Reset) { TButton->Click = 0; }
	return Ret;
}
//-----------------------------------------------------------------------------
//ボタン情報のボタンが押下されているかどうかを取得する。
//[in]
//	TButton			ボタン情報。
//[out]
//	戻り値			ボタンが押下されているかどうか。(0以外:押下されている,0:押下されていない)
int ToolButton_GetBottom(SToolButton* TButton) {
	return TButton->Bottom;
}
//-----------------------------------------------------------------------------
//ボタン情報のボタンが押下されているかどうかを取得する。(リピート機能付き版)
//[in]
//	TButton			ボタン情報。
//[out]
//	戻り値			ボタンが押下されているかどうか。(0以外:押下されている,0:押下されていない)
int ToolButton_GetRepeatBottom(SToolButton* TButton) {
	return TButton->RepeatBottom;
}
//-----------------------------------------------------------------------------
//ボタン情報の表示状態を変更する。
//[in]
//	TButton			ボタン情報。
//	Visible			新しい表示状態。
void ToolButton_SetVisible(SToolButton* TButton, int Visible) {
	//新しい表示状態を保存する。
	TButton->Visible = Visible;
}
//-----------------------------------------------------------------------------
//ボタン情報の表示状態を取得する。
//[in]
//	TButton			ボタン情報。
//[out]
//	戻り値			表示状態。(0以外:表示,0:非表示)
int ToolButton_GetVisible(SToolButton* TButton) {
	//表示状態が0の場合は0を返す。
	if(!TButton->Visible) { return 0; }
	//関連付けられているウインドウ情報の表示状態が0の場合は0を返す。
	if(!ToolWindow_GetVisible(TButton->TWindow)) { return 0; }
	//表示状態情報が登録されていて、且つ表示状態情報の表示状態が0の場合は0を返す。
	if(!ToolVisible_GetVisible(TButton->TVisible)) { return 0; }
	//ここにきていたら非表示ではないということなので0以外を返す。
	return 1;
}
//-----------------------------------------------------------------------------
//全てのボタン情報の状態推移処理を行う。
//[in]
//	StepTime		進める時間。(単位:秒)
void ToolButton_Step_All(double StepTime) {
	GSList* list = g_TButton;
	while(list) {
		ToolButton_Step(list->data, StepTime);
		list = list->next;
	}
}
//-----------------------------------------------------------------------------
//全てのボタン情報の描画移処理を行う。
void ToolButton_Draw_All() {
	GSList* list = g_TButton;
	while(list) {
		ToolButton_Draw(list->data);
		list = list->next;
	}
}
//-----------------------------------------------------------------------------
//マウスカーソルがいずれかのボタンの内にあるかどうかをチェックする。
//[out]
//	戻り値			いずれかのボタンの内にマウスカーソルがあるかどうか。(0以外:内にある,0:外にある)
int ToolButton_IsAnyHovered() {
	GSList* list = g_TButton;
	while(list) {
		if(ToolButton_IsHovered(list->data)) { return 1; }
		list = list->next;
	}
	return 0;
}
//-----------------------------------------------------------------------------
//マウスカーソルがボタンの内にあるかどうかをチェックする。
//[in]
//	TButton			ボタン情報。
//[out]
//	戻り値			ボタンの内にマウスカーソルがあるかどうか。(0以外:内にある,0:外にある)
int ToolButton_IsHovered(SToolButton* TButton) {
	int MouseX, MouseY;
	int TempX, TempY;
	int In;
	//表示状態が0の場合は何もせず終了。
	if(!ToolButton_GetVisible(TButton)) { return 0; }	//ここまで
	//マウスカーソルの位置を取得。
	ToolStatic_GetMousePosition(&MouseX, &MouseY);
	//ボタンの表示位置とボタンの表示領域にマウスカーソルがあるかどうかを取得。
	if(TButton->TWindow) {
		ToolWindow_GetDrawLeftUpPos(TButton->TWindow, &TempX, &TempY);
		TempX += TButton->Position.x;
		TempY += TButton->Position.y;
		In = ToolStatic_CheckMouseIn(TempX,
		                             TempY,
		                             TempX + TButton->Size.cx,
		                             TempY + TButton->Size.cy) &&
		     ToolWindow_CheckInfoAreaIn(TButton->TWindow,
		                                MouseX,
		                                MouseY);
	} else {
		TempX = TButton->Position.x;
		TempY = TButton->Position.y;
		In = ToolStatic_CheckMouseIn(TempX,
		                             TempY,
		                             TempX + TButton->Size.cx,
		                             TempY + TButton->Size.cy);
	}
	return In;
}
//*****************************************************************************
//	ローカル関数
//*****************************************************************************
//ボタン情報の状態推移処理を行う。
//[in]
//	TButton			ボタン情報。
//	StepTime		進める時間。(単位:秒)
static void ToolButton_Step(SToolButton* TButton, double StepTime) {
	int MouseX, MouseY;
	int MouseRepeatInput, MouseInput, MouseEdgeInput;
	int TempX, TempY;
	int In;
	//表示状態が0の場合は何もせず終了。
	if(!ToolButton_GetVisible(TButton)) { return; }	//ここまで
	//マウスカーソルの位置を取得。
	ToolStatic_GetMousePosition(&MouseX, &MouseY);
	//マウスの入力状態を取得。
	MouseRepeatInput = 0;
	MouseInput       = (ToolStatic_GetMouseButton()     & (1<<0)/*MOUSE_INPUT_LEFT*/) != 0;
	MouseEdgeInput   = (ToolStatic_GetMouseEdgeButton() & (1<<0)/*MOUSE_INPUT_LEFT*/) != 0;
	//ボタンの表示領域にマウスカーソルがあるかどうかを調べる。
	if(TButton->TWindow) {
		ToolWindow_GetDrawLeftUpPos(TButton->TWindow, &TempX, &TempY);
		TempX += TButton->Position.x;
		TempY += TButton->Position.y;
		In = ToolStatic_CheckMouseIn(TempX,
		                             TempY,
		                             TempX + TButton->Size.cx,
		                             TempY + TButton->Size.cy) &&
		                             ToolWindow_CheckInfoAreaIn(TButton->TWindow,
		                                                        MouseX,
		                                                        MouseY);
	} else {
		In = ToolStatic_CheckMouseIn(TButton->Position.x,
		                             TButton->Position.y,
		                             TButton->Position.x + TButton->Size.cx,
		                             TButton->Position.y + TButton->Size.cy);
	}
	//左ボタンが押されているかどうかで処理を分岐。
	if(MouseInput) {
		//左ボタンが押されている場合はリピート処理を行う。
		TButton->BottomTime += StepTime;
		if(TButton->FirstRepeat) {
			if(TButton->BottomTime >= TOOL_REPEAT_WAIT) {
				MouseRepeatInput      = 1;
				TButton->BottomTime  -= TOOL_REPEAT_WAIT;
			}
		} else {
			if(TButton->BottomTime >= TOOL_FIRST_REPEAT_WAIT) {
				MouseRepeatInput      = 1;
				TButton->FirstRepeat  = 1;
				TButton->BottomTime  -= TOOL_FIRST_REPEAT_WAIT;
			}
		}
		//左ボタンが押された瞬間は必ずリピート入力考慮のボタン押下状態も0以外にする。
		if(MouseEdgeInput) { MouseRepeatInput = 1; }
		//ボタン表示領域にカーソルがあり、左ボタンが押された瞬間の場合はボタン押下状態にする。
		if(MouseEdgeInput && In) { TButton->Bottom = 1; }
	} else {
		//左ボタンが押されていない場合はリピート処理をリセットする。
		TButton->FirstRepeat = 0;
		TButton->BottomTime  = 0.0;
		//マウスカーソルがボタンの表示領域にあり、且つ今までボタンが押下状態だった場合はクリックフラグを立て、ON/OFF状態を反転する。
		if(In && TButton->Bottom) {
			TButton->Click     = 1;
			TButton->OnOffFlag = !TButton->OnOffFlag;
		}
		//ボタン押下状態をリセットする。
		TButton->Bottom = 0;
	}
	//リピート入力がされていて、且つマウスカーソルがボタンの表示領域にあったらリピート入力考慮のボタン押下状態を0以外にする。
	if(MouseRepeatInput && In) {
		TButton->RepeatBottom = 1;
	} else {
		TButton->RepeatBottom = 0;
	}
}
//-----------------------------------------------------------------------------
//ボタン情報を画面に描画する。
//[in]
//	TButton			ボタン情報。
static void ToolButton_Draw(SToolButton* TButton) {
	int MouseX, MouseY;
	int ButtonFontHandle;
	int DrawWidth;
	int TempX, TempY;
	int In;
	int addx, addy;
	int CenterColor;
	int TopLeftC1, TopLeftC2;
	int RightBottomC1, RightBottomC2;
	//表示状態が0の場合は何もせず終了。
	if(!ToolButton_GetVisible(TButton)) { return; }	//ここまで
	//マウスカーソルの位置を取得。
	ToolStatic_GetMousePosition(&MouseX, &MouseY);
	//ボタン名描画用のフォントハンドルを取得。
	ButtonFontHandle = ToolStatic_GetButtonFontHandle();
	//ボタン名の描画幅を取得。
	DrawWidth = ToolFunc_GetDrawStringWidthToHandle(TButton->Name, -1, ButtonFontHandle);
	//ボタンの表示位置とボタンの表示領域にマウスカーソルがあるかどうかを取得。
	if(TButton->TWindow) {
		ToolWindow_GetDrawLeftUpPos(TButton->TWindow, &TempX, &TempY);
		TempX += TButton->Position.x;
		TempY += TButton->Position.y;
		In = ToolStatic_CheckMouseIn(TempX,
		                             TempY,
		                             TempX + TButton->Size.cx,
		                             TempY + TButton->Size.cy) &&
		     ToolWindow_CheckInfoAreaIn(TButton->TWindow,
		                                MouseX,
		                                MouseY);
	} else {
		TempX = TButton->Position.x;
		TempY = TButton->Position.y;
		In = ToolStatic_CheckMouseIn(TempX,
		                             TempY,
		                             TempX + TButton->Size.cx,
		                             TempY + TButton->Size.cy);
	}
	//ボタンが押されているか、若しくはON/OFF切り替えモードのON状態の場合は押されているボタンの配色にする。
	if((In && TButton->Bottom) ||
	   (TButton->On_Off_SwitchType && TButton->OnOffFlag)) {
		addx          = 1;
		addy          = 1;
		CenterColor   = ToolFunc_GetColor(192,192,192);
		TopLeftC1     = ToolFunc_GetColor(  0,  0,  0);
		TopLeftC2     = ToolFunc_GetColor( 64, 64, 64);
		RightBottomC1 = ToolFunc_GetColor(255,255,255);
		RightBottomC2 = ToolFunc_GetColor(191,191,191);
	} else {
		addx          = 0;
		addy          = 0;
		CenterColor   = ToolFunc_GetColor(255,255,255);
		RightBottomC1 = ToolFunc_GetColor(  0,  0,  0);
		RightBottomC2 = ToolFunc_GetColor( 64, 64, 64);
		TopLeftC1     = ToolFunc_GetColor(255,255,255);
		TopLeftC2     = ToolFunc_GetColor(191,191,191);
	}
	//ボタン情報にウインドウ情報が関連付けられている場合は描画領域の制限設定を行う。
	if(TButton->TWindow) { ToolWindow_SetupDrawArea(TButton->TWindow); }
	//ボタンの描画。
	ToolStatic_DrawButton(TempX,
	                      TempY,
	                      TButton->Size.cx,
	                      TButton->Size.cy,
	                      TopLeftC1,
	                      TopLeftC2,
	                      RightBottomC1,
	                      RightBottomC2,
	                      CenterColor);
	//ボタン名の描画。
	ToolFunc_SetDrawBlendMode(1/*DX_BLENDMODE_ALPHA*/, 180);
	ToolFunc_DrawStringToHandle(TempX + (TButton->Size.cx - DrawWidth       ) / 2 + addx,
	                            TempY + (TButton->Size.cy - TOOL_FONT_HEIGHT) / 2 + addy,
	                            TButton->Name,
	                            ToolFunc_GetColor(0,0,0),
	                            ButtonFontHandle);
	//ブレンドモードと描画可能領域を元に戻す。
	ToolFunc_SetDrawBlendMode(0/*DX_BLENDMODE_NOBLEND*/, 0);
	ToolFunc_SetDrawAreaFull();
}
