#include "stdafx.h"

#define OWN__COMBOBOXQUEUE__

#include "ComboboxQueue.h"

// デストラクタ(ANSI版)
ComboboxQueueA::~ComboboxQueueA()
{
	if (szStrings != NULL) {
		if (num > 0) {
			for(--num ; num>=0 ; --num) {
				if (szStrings[num]) {
					free(szStrings[num]);
					szStrings[num] = NULL;
				}
			}
			num = 0;
		}
		free(szStrings);
		szStrings = NULL;
	}
	if (strSubkey != NULL) {
		free(strSubkey);
		strSubkey = NULL;
	}
	if (strValue != NULL) {
		free(strValue);
		strValue = NULL;
	}
}

// デストラクタ(Unicode版)
ComboboxQueueW::~ComboboxQueueW()
{
	if (szStrings != NULL) {
		if (num > 0) {
			for(--num ; num>=0 ; --num) {
				if (szStrings[num]) {
					free(szStrings[num]);
					szStrings[num] = NULL;
				}
			}
			num = 0;
		}
		free(szStrings);
		szStrings = NULL;
	}
	if (strSubkey != NULL) {
		free(strSubkey);
		strSubkey = NULL;
	}
	if (strValue != NULL) {
		free(strValue);
		strValue = NULL;
	}
}

// 定義と初期化(ANSI版)
bool ComboboxQueueA::Init(HWND hCtrl, int num, HKEY hRoot, const char *szSubkey, const char *szValue)
{
	if (hCtrl == NULL || num <= 0 || szSubkey == NULL || szValue == NULL) {
		// 引数エラー
		return false;
	}

	this->hRoot = hRoot;
	strSubkey = _strdup(szSubkey);
	strValue = _strdup(szValue);
	this->hCtrl = hCtrl;
	this->num = num;
	szStrings = NULL;

	DWORD  dwSize;				// 読みだそうとする値のサイズ
	DWORD  dwType;				// 読みだそうとする値の型
	LPSTR  mszData;				// 読みだそうとする値を入れる場所
	bool   bRet = false;		// 戻り値
	HKEY   hKey;

	// numの分だけ文字列（ポインタ）格納領域を確保
	szStrings = (char **)calloc(num, sizeof(char *));
	if (szStrings == NULL) {
		// メモリ不足
		return false;
	}

	if (ERROR_SUCCESS == RegOpenKeyExA(hRoot, szSubkey, 0, KEY_READ, &hKey)) {
		if (ERROR_SUCCESS != RegQueryValueExA(hKey, szValue, NULL, &dwType, NULL, &dwSize)) {
			// 値がない…初回呼び出し
			bRet = true;
		}
		else if (dwType != REG_MULTI_SZ || dwSize <= 0) {
			// 値の型が異なる、あるいは、値のサイズが0 … 新規作成で、次回以降上書き
			bRet = true;
		}
		else {
			++dwSize;
			mszData = (LPSTR)calloc(dwSize+2, sizeof(char));
			if (mszData) {
				LONG lRes = RegQueryValueExA(hKey, szValue, NULL, NULL, (LPBYTE)mszData, &dwSize);
				if (lRes == ERROR_SUCCESS) {
					DWORD i = 0;
					int   n = 0;
					bRet = true;

					for(; n < num ; ++n) {
						szStrings[n] = _strdup(&mszData[i]);
						if (szStrings[n] == NULL) {
							// メモリ不足
							bRet = false;
							break;
						}
						SendMessageA(hCtrl, CB_ADDSTRING, 0, (LPARAM)&mszData[i]);

						// 次の文字列までスキップ
						// ただし \0 の連続を見つけたら終了
						while(mszData[i] != 0) {
							++i;
						}
						if (mszData[++i] == 0) {
							break; // \0の連続を発見
						}
					}
				}
				free(mszData);
			}
		}

		RegCloseKey(hKey);
	}
	else {
		// キーがない … 初回呼び出し
		bRet = true;
	}

	return bRet;
}

// 定義と初期化(Unicode版)
bool ComboboxQueueW::Init(HWND hCtrl, int num, HKEY hRoot, const wchar_t *szSubkey, const wchar_t *szValue)
{
	if (hCtrl == NULL || num <= 0 || szSubkey == NULL || szValue == NULL) {
		// 引数エラー
		return false;
	}

	this->hRoot = hRoot;
	strSubkey = _wcsdup(szSubkey);
	strValue = _wcsdup(szValue);
	this->hCtrl = hCtrl;
	this->num = num;
	szStrings = NULL;

	DWORD  dwSize;				// 読みだそうとする値のサイズ
	DWORD  dwType;				// 読みだそうとする値の型
	LPWSTR mszData;			// 読みだそうとする値を入れる場所
	bool   bRet = false;		// 戻り値
	HKEY   hKey;

	// numの分だけ文字列（ポインタ）格納領域を確保
	szStrings = (wchar_t **)calloc(num, sizeof(wchar_t *));
	if (szStrings == NULL) {
		// メモリ不足
		return false;
	}

	if (ERROR_SUCCESS == RegOpenKeyExW(hRoot, szSubkey, 0, KEY_READ, &hKey)) {
		if (ERROR_SUCCESS != RegQueryValueExW(hKey, szValue, NULL, &dwType, NULL, &dwSize)) {
			// 値がない…初回呼び出し
			bRet = true;
		}
		else if (dwType != REG_MULTI_SZ || dwSize <= 0) {
			// 値の型が異なる、あるいは、値のサイズが0 … 新規作成で、次回以降上書き
			bRet = true;
		}
		else {
			++dwSize;
			mszData = (LPWSTR)calloc(dwSize+2, sizeof(wchar_t));
			if (mszData) {
				LONG lRes = RegQueryValueExW(hKey, szValue, NULL, NULL, (LPBYTE)mszData, &dwSize);
				if (lRes == ERROR_SUCCESS) {
					DWORD i = 0;
					int   n = 0;
					bRet = true;

					for(; n < num ; ++n) {
						szStrings[n] = _wcsdup(&mszData[i]);
						if (szStrings[n] == NULL) {
							// メモリ不足
							bRet = false;
							break;
						}
						SendMessageW(hCtrl, CB_ADDSTRING, 0, (LPARAM)&mszData[i]);

						// 次の文字列までスキップ
						// ただし \0 の連続を見つけたら終了
						while(mszData[i] != 0) {
							++i;
						}
						if (mszData[++i] == 0) {
							break; // \0の連続を発見
						}
					}
				}
				free(mszData);
			}
		}

		RegCloseKey(hKey);
	}
	else {
		// キーがない … 初回呼び出し
		bRet = true;
	}

	return bRet;
}


// 定義と初期化(ANSI版)
bool ComboboxQueueA::Init(HWND hWnd, int iCtrl, int num, HKEY hRoot, const char *szSubkey, const char *szValue)
{
	return Init(GetDlgItem(hWnd, iCtrl), num, hRoot, szSubkey, szValue);
}

// 定義と初期化(Unicode版)
bool ComboboxQueueW::Init(HWND hWnd, int iCtrl, int num, HKEY hRoot, const wchar_t *szSubkey, const wchar_t *szValue)
{
	return Init(GetDlgItem(hWnd, iCtrl), num, hRoot, szSubkey, szValue);
}

// szTextを追加する。必要に応じて、古い文字列は削除する。(ANSI版)
bool ComboboxQueueA::Add(const char *szText)
{
	int iListCount;	// 実際に何個のデータがあるかを調べる（numはあくまでバッファの量を表す）

	// すでに登録済みの文字列か?
	for(iListCount=0 ; szStrings[iListCount] != NULL && iListCount < num ; ++iListCount) {
		if (lstrcmpA(szStrings[iListCount], szText) == 0) {
			// 登録済み文字列だった…順番の並べ替え（ただし、すでに0番目の場合は何もしない）
			if (iListCount == 0) {
				return true;
			}
			char *tmp = szStrings[iListCount];

			for(; iListCount > 0 ; --iListCount) {
				szStrings[iListCount] = szStrings[iListCount-1];
			}
			szStrings[0] = tmp;

			// レジストリへの書き込み
			return regist_strings();
		}
	}

	// 新しく登録する文字列
	char *strNew = _strdup(szText);
	if (strNew == NULL) {
		return false; // メモリ不足
	}

	// 登録数をオーバーする場合は、末尾のデータを削除
	if (iListCount >= num) {
		free(szStrings[iListCount-1]);
		--iListCount;
	}

	// 順番の並べ替え
	for(; iListCount > 0 ; --iListCount) {
		szStrings[iListCount] = szStrings[iListCount-1];
	}
	
	// 登録
	szStrings[0] = strNew;

	// レジストリへの書き込み
	return regist_strings();
}

// szTextを追加する。必要に応じて、古い文字列は削除する。(Unicode版)
bool ComboboxQueueW::Add(const wchar_t *szText)
{
	int iListCount;	// 実際に何個のデータがあるかを調べる（numはあくまでバッファの量を表す）

	// すでに登録済みの文字列か?
	for(iListCount=0 ; szStrings[iListCount] != NULL && iListCount < num ; ++iListCount) {
		if (lstrcmpW(szStrings[iListCount], szText) == 0) {
			// 登録済み文字列だった…順番の並べ替え（ただし、すでに0番目の場合は何もしない）
			if (iListCount == 0) {
				return true;
			}
			wchar_t *tmp = szStrings[iListCount];

			for(; iListCount > 0 ; --iListCount) {
				szStrings[iListCount] = szStrings[iListCount-1];
			}
			szStrings[0] = tmp;

			// レジストリへの書き込み
			return regist_strings();
		}
	}

	// 新しく登録する文字列
	wchar_t *strNew = _wcsdup(szText);
	if (strNew == NULL) {
		return false; // メモリ不足
	}

	// 登録数をオーバーする場合は、末尾のデータを削除
	if (iListCount >= num) {
		free(szStrings[iListCount-1]);
		--iListCount;
	}

	// 順番の並べ替え
	for(; iListCount > 0 ; --iListCount) {
		szStrings[iListCount] = szStrings[iListCount-1];
	}
	
	// 登録
	szStrings[0] = strNew;

	// レジストリへの書き込み
	return regist_strings();
}

// レジストリへの書き込みと、コンボボックスへの登録(ANSI版)
bool ComboboxQueueA::regist_strings()
{
	HKEY hKey;
	bool bRet = true;
	int  iListCount = 0;	// 実際に何個のデータがあるかを調べる（numはあくまでバッファの量を表す）
	int  iLoop;

	// レジストリへの書き込み
	if (ERROR_SUCCESS == RegCreateKeyEx(hRoot, strSubkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) {
		DWORD dwLenAll = 1;		// 最後に\0\0と来るので、最後の1文字分余計に準備する
		// 文字列の全部の長さを計算
		for(iListCount=0 ; szStrings[iListCount] != NULL && iListCount < num ; ++iListCount) {
			dwLenAll += (DWORD)lstrlen(szStrings[iListCount]);
			++dwLenAll; // 文字列終端の \0 の分
		}

		// 計算された文字列の長さの分だけメモリを確保し、multisz文字列を作る
		LPSTR mszData = (LPSTR)calloc(dwLenAll, sizeof(char));
		if (mszData) {
			DWORD dwPointer = 0;

			for(iLoop=0 ; iLoop<iListCount ; ++iLoop) {
				lstrcpy(&mszData[dwPointer], szStrings[iLoop]);
				dwPointer += (DWORD)lstrlen(szStrings[iLoop]);
				++dwPointer; // 文字列終端の \0 の分
			}

			RegSetValueEx(hKey, strValue, 0, REG_MULTI_SZ, (LPBYTE)mszData, dwLenAll*sizeof(char));
			free(mszData);
		}
		else {
			bRet = false;	// メモリ不足
		}
		RegCloseKey(hKey);
	}
	else {
		bRet = false;	// レジストリを開けない
	}

	// コンボボックス内の全データ削除
	for(iLoop = (int)SendMessageA(hCtrl, CB_GETCOUNT, 0, 0) ; iLoop>0 ; --iLoop) {
		SendMessageA(hCtrl, CB_DELETESTRING, 0, 0);
	}

	// コンボボックスへの登録
	for(iLoop=0 ; iLoop<iListCount ; ++iLoop) {
		SendMessageA(hCtrl, CB_ADDSTRING, 0, (LPARAM)szStrings[iLoop]);
	}

	return bRet;
}

// レジストリへの書き込みと、コンボボックスへの登録(Unicode版)
bool ComboboxQueueW::regist_strings()
{
	HKEY hKey;
	bool bRet = true;
	int  iListCount = 0;	// 実際に何個のデータがあるかを調べる（numはあくまでバッファの量を表す）
	int  iLoop;

	// レジストリへの書き込み
	if (ERROR_SUCCESS == RegCreateKeyExW(hRoot, strSubkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) {
		DWORD dwLenAll = 1;		// 最後に\0\0と来るので、最後の1文字分余計に準備する
		// 文字列の全部の長さを計算
		for(iListCount=0 ; szStrings[iListCount] != NULL && iListCount < num ; ++iListCount) {
			dwLenAll += (DWORD)lstrlenW(szStrings[iListCount]);
			++dwLenAll; // 文字列終端の \0 の分
		}

		// 計算された文字列の長さの分だけメモリを確保し、multisz文字列を作る
		LPWSTR mszData = (LPWSTR)calloc(dwLenAll, sizeof(wchar_t));
		if (mszData) {
			DWORD dwPointer = 0;

			for(iLoop=0 ; iLoop<iListCount ; ++iLoop) {
				lstrcpyW(&mszData[dwPointer], szStrings[iLoop]);
				dwPointer += (DWORD)lstrlenW(szStrings[iLoop]);
				++dwPointer; // 文字列終端の \0 の分
			}

			RegSetValueExW(hKey, strValue, 0, REG_MULTI_SZ, (LPBYTE)mszData, dwLenAll*sizeof(wchar_t));
			free(mszData);
		}
		else {
			bRet = false;	// メモリ不足
		}
		RegCloseKey(hKey);
	}
	else {
		bRet = false;	// レジストリを開けない
	}

	// コンボボックス内の全データ削除
	for(iLoop = (int)SendMessageW(hCtrl, CB_GETCOUNT, 0, 0) ; iLoop>0 ; --iLoop) {
		SendMessageW(hCtrl, CB_DELETESTRING, 0, 0);
	}

	// コンボボックスへの登録
	for(iLoop=0 ; iLoop<iListCount ; ++iLoop) {
		SendMessageW(hCtrl, CB_ADDSTRING, 0, (LPARAM)szStrings[iLoop]);
	}

	return bRet;
}
