//////////////////////////////////////////
// User.h
// Copyright (c) 09/29/2021 by Ysama
//////////////////////////////////////////
/* オブジェクト ファイル内にライブラリ検索レコードを配置します。
   このコメントの種類には、リンカーで検索するライブラリの名前 
   (および場合によってはパス) を持つコメント文字列パラメーターを
   指定する必要があります。
*/
#pragma comment (lib, "crypt32.lib")
//定数の定義
#define	IDC_LISTVIEW 1000
#define UP 0
#define DOWN 1
#define NO_OF_SUBITEM 4

////////////////////////
//リストビューの作成
////////////////////////
CLISTVIEW g_ListView;

//////////////////////////
//データファイル用CSTR変数
//////////////////////////
CSTR g_Data;

////////////////////////////
//データファイル名用CSTR変数
////////////////////////////
CSTR g_File;

//////////////////////////
//ヘルプファイル用CSTR変数
//////////////////////////
CSTR g_HelpFile;

////////////////////////////////
//ポップアップメニュ−のハンドル
////////////////////////////////
HMENU g_hPopup;

//////////////////////////
//サブアイテムのソート関連
//////////////////////////
//列とソート方法の配列
int sortsubno[NO_OF_SUBITEM];
//ソートコールバック関数
int CALLBACK CompProc(LPARAM lp1, LPARAM lp2, LPARAM lpSort) {

	static int nItem1, nItem2;
	static char buf1[MAX_PATH], buf2[MAX_PATH];
	//アイテムの検索
	nItem1 = g_ListView.FindItem(-1, NULL, lp1);
	nItem2 = g_ListView.FindItem(-1, NULL, lp2);
	//アイテム情報の取得
	g_ListView.GetItemText(nItem1, (int)lpSort, buf1, MAX_PATH);
	g_ListView.GetItemText(nItem2, (int)lpSort, buf2, MAX_PATH);
	if(sortsubno[(int)lpSort] == UP)
		return(strcmp(buf1, buf2));
	else
		return(strcmp(buf1, buf2) * -1);
}

////////////////////////////
//カンマ区切り文字切り出し
//★str 文字列は切り出した文
//字列だけ短縮されるので注意
////////////////////////////
char* GetDelimtStr(char* str) {

	static char buff[MAX_PATH * 4];	//取り敢えず1K bytes確保
	char *cp = str;
	int len = 0;
	switch(*cp) {
	case 0:
			return 0;			//NULL終端を指していれば単に帰る（FALSE）
	case '\"':					//「""」処理
		cp++;					//'\"'の次に進める
		while(*cp != '\"') {
			buff[len] = *cp; 	//buffへのコピー
			cp++;
			len++;
		}
		cp++;					//cpは'\"'の次の','へ進める
		buff[len] = '\0';		//buffのNULL終端処理
		break;
	default:					//「""」以外
		//','でも'\n'でもNullでもなければ
		while(*cp != ',' && *cp != '\n' && *cp) {
			buff[len] = *cp;	//strデータをbuffにコピーしてゆく
			cp++;
			len++;
		}
		buff[len] = '\0';		//buffのNULL終端処理
		break;
	}
	if(*cp){					//cpがNULL終端を指していなければ
		cp++;					//','や'\n'を指しているので一つ進め
	}
	lstrcpy(str, cp);			//残余の文字列を文字列先頭に移動
	return buff;				//切り取った文字列を返す（TRUE）
}

///////////////
//ファイル処理
///////////////
bool DataLoad(HWND hWnd, char* fn, bool cyph = TRUE) {

	//データファイルの読み込み
	if(cyph) { 	//データが暗号化されている場合
		//ファイルを読み込みでオープン
		bool bSuccess = FALSE;						//処理結果フラグ
		HANDLE hFile = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ, NULL,
						OPEN_EXISTING, NULL, NULL);	//ファイルを読込みでオープン
		if(hFile != INVALID_HANDLE_VALUE) {			//オープンできた場合
			DWORD dwFileSize = GetFileSize(hFile, NULL);
			if(dwFileSize != 0xFFFFFFFF) {			//-1は(0xFFFFFFFF)エラー
				char* dat = new char[dwFileSize];	//データバッファを確保する
				DWORD dwRead;
				if(ReadFile(hFile, (LPVOID)dat, dwFileSize, &dwRead, NULL)) {
					DATA_BLOB blobIn;
					blobIn.cbData = dwRead;			//暗号化するデータのサイズ
					blobIn.pbData = (BYTE*)dat;		//暗号化するデータのポインター
					DATA_BLOB blobOut;
					CRYPTPROTECT_PROMPTSTRUCT promptStruct;
					promptStruct.cbSize = sizeof(CRYPTPROTECT_PROMPTSTRUCT);
					promptStruct.dwPromptFlags = CRYPTPROTECT_PROMPT_ON_PROTECT;
					promptStruct.hwndApp = hWnd;
					promptStruct.szPrompt = L"データの復号化";
					if(!CryptUnprotectData(&blobIn, NULL, NULL,
											NULL, &promptStruct, 0, &blobOut))
						MessageBox(hWnd, TEXT("データの復号化に失敗しました。"),
									"エラー", MB_OK | MB_ICONERROR);
					else {
					//blobOut.pbDataが復号化データ、blobOut.cbDataはそのサイズ
						g_Data = (char*)blobOut.pbData;
						LocalFree(blobOut.pbData);	//blobOutのメモリーを解放
						bSuccess = TRUE;
					}
				}
				delete [] dat;						//バッファを廃棄
			}
			CloseHandle(hFile);						//ファイルのクローズ
		}
		if(!bSuccess)								//g_Dataに読み込めなかった場合
			return bSuccess;
	}
	else { 	//データが暗号化されていない場合
		if(!g_Data.FromFile(fn))					//読み込めなかった場合
			return FALSE;
	}
	//データ読み出し−行読み出しを繰り返す
	char* cp;
	int i = 0;
	while(cp = GetDelimtStr(g_Data.ToChar())) {
		//一行から語句を読み出し、コラムにセットすることを4回繰り返す
		g_ListView.InsertItem(i, cp, -1, i);	//LVIF_PARAMをソートの為に追加
		cp = GetDelimtStr(g_Data.ToChar());
		g_ListView.SetItem(i, 1, cp);
		cp = GetDelimtStr(g_Data.ToChar());
		g_ListView.SetItem(i, 2, cp);
		cp = GetDelimtStr(g_Data.ToChar());
		g_ListView.SetItem(i, 3, cp);
		i++;
	}
 	return TRUE;
}

bool DataSave(HWND hWnd, char* fn, bool cyph = TRUE) {

	//データの作成
	g_Data = "";	//初期化
 	//行数だけ文字列化を繰り返す
	int n = g_ListView.GetItemCount();
	int i, j;
	for(i = 0; i < n; i++) {
		char buff[MAX_PATH];
		CSTR row = "";
		//g_ListViewの4列のデータを読み出し、コンマ区切の文字列一行とする
		for(j = 0; j < 3; j++) {
			g_ListView.GetItemText(i, j, buff, MAX_PATH);
			row = row + buff;
			row = row + ",";
		}
		g_ListView.GetItemText(i, j, buff, MAX_PATH);
		row = row + "\"";	//最終項目は
		row = row + buff;	//文字列中に'\n'があるので
		row = row + "\"";	//「""」で囲う
 		//一行データをg_Dataへ足しこんでゆく
		g_Data = g_Data + row;
		g_Data = g_Data + "\n";
	}
	//データ(g_Data)ファイルの書き込み
	if(cyph) { 	//データを暗号化する場合
		DATA_BLOB blobIn;
		blobIn.cbData = strlen(g_Data.ToChar()) + 1;	//暗号化するデータのサイズ
		blobIn.pbData = (BYTE*)g_Data.ToChar();			//暗号化するデータのバイトポインター
		DATA_BLOB blobOut;
		CRYPTPROTECT_PROMPTSTRUCT promptStruct;
		promptStruct.cbSize = sizeof(CRYPTPROTECT_PROMPTSTRUCT);
		promptStruct.dwPromptFlags = CRYPTPROTECT_PROMPT_ON_PROTECT;
		promptStruct.hwndApp = hWnd;
		promptStruct.szPrompt = L"データの暗号化";
		if(!CryptProtectData(&blobIn, NULL, NULL, NULL, &promptStruct, 0, &blobOut)) {
			MessageBox(hWnd, TEXT("データの暗号化に失敗しました。"), "エラー", MB_OK | MB_ICONERROR);
			return FALSE;
		}
		else { 	//{blobOut.pbDataが暗号化されたデータ、blobOut.cbDataはそのサイズ
			//属性がFILE_ATTRIBUTE_NORMALでなければFILE_ATTRIBUTE_NORMALにする
			DWORD attrib = GetFileAttributes(fn);
			if(attrib != FILE_ATTRIBUTE_NORMAL)
				SetFileAttributes(fn, FILE_ATTRIBUTE_NORMAL);
			//ファイルを書込みでオープン
			bool bSuccess = FALSE;				//処理結果フラグ
			HANDLE hFile = CreateFile(fn, GENERIC_WRITE, NULL, NULL,
								CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if(hFile == INVALID_HANDLE_VALUE)	//オープン失敗
				MessageBox(hWnd, "ファイルを開けられませんでした。", "エラー", MB_OK | MB_ICONERROR);
			else {
				DWORD dwWritten;
				bSuccess = WriteFile(hFile, blobOut.pbData, blobOut.cbData, &dwWritten, NULL);
				CloseHandle(hFile);				//ファイルのクローズ
			}
			LocalFree(blobOut.pbData);			//blobOutのメモリーを解放
			SetFileAttributes(fn, attrib);		//属性を戻す
			return bSuccess;					//ファイルの書き込み結果を返す
		}
	}
	else
		return g_Data.ToFile(fn);	//ファイルの書き込み結果を返す
}
