//-------------------------
//         CSTRW.h
//   Copyright April 2022
//     By K. Yoshida
//      Version 1.0A
//注：使用にはwchar.hが必要
//-------------------------
#ifndef	_CSTRW_
#define _CSTRW_

class CSTRW {

	WCHAR*	m_str;

public:
	CSTRW();									//コンストラクター（無）
	CSTRW(const WCHAR*);						//コンストラクター（文字列）
	CSTRW(const CSTRW&);						//コンストラクター（CSTRW）
	CSTRW(const long);							//コンストラクター（整数）
	~CSTRW();									//デストラクター
	CSTRW& operator = (const CSTRW&);			//operator = (B) ---> = B;
	CSTRW& operator = (const long);				//operator = (B) ---> = B;
	WCHAR* ToChar() {return m_str;}				//文字列ポインターとして返す
	void Print();								//m_strを印字する
	friend CSTRW operator + (const CSTRW&, const CSTRW&);	//A operator + (B)  --->= A + B;
	friend bool operator == (const CSTRW&, const CSTRW&);	//A operator == (B) --->= A == B;
	friend bool operator == (const CSTRW&, const WCHAR*);	//A operator == (B) --->= A == B;
	friend bool operator != (const CSTRW&, const CSTRW&);	//A operator == (B) --->= A != B;
	friend bool operator != (const CSTRW&, const WCHAR*);	//A operator == (B) --->= A != B;
	bool FromFile(WCHAR*, WCHAR*, HWND);		//ファイルからの読込
	bool FromFile(WCHAR*);						//ファイルからの読込
	bool ToFile(WCHAR*, WCHAR*, HWND);			//ファイルへの書き込み
	bool ToFile(WCHAR*);						//ファイルへの書き込み
	bool ToUTF8File(WCHAR*, bool);				//ファイルへの書き込み（boolはBOM有無のフラグ）
	bool Next(CSTRW&);							//m_strの単語を切り取り出す
	bool GetLine(CSTRW&);						//この行末まで進む
	bool NextLine();							//次の行頭まで進む
	WCHAR* Find(WCHAR*, WCHAR*);				//文字列の検索
};

//コンストラクター（無）
CSTRW::CSTRW() {

	m_str = new WCHAR[1];
	*m_str = L'\0';
}

//コンストラクター（文字列）
CSTRW::CSTRW(const WCHAR* string) {

	m_str = new WCHAR[lstrlenW(string) + 1];
	lstrcpyW(m_str, string);
}

//コンストラクター（CSTRW）
CSTRW::CSTRW(const CSTRW& cst) {

	m_str = new WCHAR[lstrlenW(cst.m_str) + 1];
	lstrcpyW(m_str, cst.m_str);
}

//コンストラクター（整数）
CSTRW::CSTRW(const long i) {

	WCHAR sbuff[MAX_PATH / 8];
	wsprintfW(sbuff, L"%d", i);
	m_str = new WCHAR[lstrlenW(sbuff) + 1];
	lstrcpyW(m_str, sbuff);
}

//デストラクター
CSTRW::~CSTRW() {

	delete [] m_str;
}

//CSTRWの代入
CSTRW& CSTRW::operator = (const CSTRW& cst) {

	if(&cst != this) {	//You can't copy yourself
		delete [] m_str;
		m_str = new WCHAR[lstrlenW(cst.m_str) + 1];
		lstrcpyW(m_str, cst.m_str);
	}
	return *this;
}

//整数の代入
CSTRW& CSTRW::operator = (const long i) {

	WCHAR sbuff[MAX_PATH / 8];
	wsprintfW(sbuff, L"%d", i);
	m_str = new WCHAR[lstrlenW(sbuff) + 1];
	lstrcpyW(m_str, sbuff);
	return *this;
}

//m_strを印字する
void CSTRW::Print() {

	MessageBoxW(NULL, m_str, L"CSTRW Message", MB_OK);
}

//追加演算子の定義
CSTRW operator + (const CSTRW& cst1, const CSTRW& cst2) {

	CSTRW strw;
	delete [] strw.m_str;
	strw.m_str = new WCHAR[lstrlenW(cst1.m_str) + lstrlenW(cst2.m_str) + 1];
	lstrcpyW(strw.m_str, cst1.m_str);
	lstrcatW(strw.m_str, cst2.m_str);
	return strw;
}

//比較演算子の定義（１）
bool operator == (const CSTRW& cst1, const CSTRW& cst2) {

	return !lstrcmpW(cst1.m_str, cst2.m_str);
}

//比較演算子の定義（２）
bool operator == (const CSTRW& cst, const WCHAR* str) {

	return !lstrcmpW(cst.m_str, str);
}

//比較演算子の定義（３）
bool operator != (const CSTRW& cst1, const CSTRW& cst2) {

	return lstrcmpW(cst1.m_str, cst2.m_str);
}

//比較演算子の定義（４）
bool operator != (const CSTRW& cst, const WCHAR* str) {

	return lstrcmpW(cst.m_str, str);
}

//ファイルからの読込
bool CSTRW::FromFile(WCHAR* Filter, WCHAR* FilePath, HWND hWnd = NULL) {

	WCHAR FileName[MAX_PATH];
	WCHAR file_path[MAX_PATH];
	lstrcpyW(file_path, FilePath);	//Default Path
	OPENFILENAMEW ofn;				//オープンファイルダイアログ構造体
	//オープンファイルダイアログでファイル名を取得する
	FileName[0] = NULL;
	ZeroMemory(&ofn, sizeof(ofn));	//ゼロ初期化
	ofn.lStructSize		= sizeof(ofn);
	ofn.hwndOwner		= hWnd;
	ofn.lpstrFilter		= Filter;
	ofn.nFilterIndex	= 1;
	ofn.lpstrFile		= FileName;
	ofn.nMaxFile		= MAX_PATH;
	if(!*FilePath)					//規定値のNULLであれば現在のディレクトリー
		GetCurrentDirectoryW(MAX_PATH, file_path);
	else
		lstrcpyW(file_path, FilePath);
	ofn.lpstrInitialDir = file_path;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	if(!GetOpenFileNameW(&ofn))	return FALSE;		//読み込み結果
	//ファイルを読み込む
	BOOL bSuccess = FALSE;
	HANDLE hFile;
	hFile = CreateFileW(FileName, 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)エラー
			LPBYTE Buff;							//読み込みバッファ用ポインター
			Buff = new BYTE[dwFileSize];
			DWORD dwRead;
			if(ReadFile(hFile, Buff, dwFileSize, &dwRead, NULL)) {
				bSuccess = TRUE; //読み込み成功
			}
			if(Buff[0] == 0xFF && Buff[1] == 0xFE) {//UTF-16、UTF-32のBOMチェック
				if(Buff[2] | Buff[3]) {				//いずれもNULLではない→UTF-16のBOM
					delete [] m_str;				//元の文字列を廃棄して
					//BOM以降の読み込んだデータを入れられるようにする
					m_str = new WCHAR[dwFileSize / sizeof(WCHAR)];
					CopyMemory(m_str, Buff + 2, dwFileSize - 2);
					//念のため、バッファの終端にNULLを置く
					m_str[(dwFileSize - 2) / sizeof(WCHAR) - 1] = L'\0';
				}
				else {	//Buff[2]とBuff[3])がいずれもNULL→UTF-32のBOM（またはUTF-16でNULLのみのファイルの場合）
					bSuccess = FALSE;	//UTF-32BOMの場合、何もせず読み込み失敗扱い
				}
			}
			else if(Buff[0] == 0xEF && Buff[1] == 0xBB && Buff[2] == 0xBF) {	//UTF-8のBOMチェック
				int iBuffSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff + 3), -1, NULL, NULL);
				if(iBuffSize) {	//UTF-8文字列の場合
					//m_str用メモリーの確保
					if(m_str)
						delete [] m_str;
					m_str = new WCHAR[iBuffSize];
					//UTF8をWCHARへ変換
					MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff + 3), -1, m_str, iBuffSize);
				}
				else {					//BOM付きUTF-8で変換不能な場合
					bSuccess = FALSE;	//何もせず読み込み失敗扱い
				}
			}
			else {						//UTF-16、32、8のBOMがない場合
				//先ずBOM無しUTF-8か否かをチェック
				int iBuffSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff), -1, NULL, NULL);
				if(iBuffSize) {			//BOM無しUTF-8の場合
					//m_str用メモリーの確保
					if(m_str)
						delete [] m_str;
					m_str = new WCHAR[iBuffSize];
					//UTF8をWCHARへ変換
					MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff), -1, m_str, iBuffSize);
				}
				else {					//BOMが無く、UTF-8変換できない場合「丸のみ」
					m_str = new WCHAR[dwFileSize / sizeof(WCHAR) + 1];
					CopyMemory(m_str, Buff, dwFileSize);
					//念のため、バッファの終端にNULLを置く
					m_str[dwFileSize / sizeof(WCHAR)] = L'\0';
				}
			}
			delete [] Buff;							//読み込みバッファ解放
		}
		CloseHandle(hFile);							//ファイルのクローズ
	}
	return bSuccess;
}

//ファイルからの読込
bool CSTRW::FromFile(WCHAR* FileName) {

	BOOL bSuccess = FALSE;
	HANDLE hFile;
	hFile = CreateFileW(FileName, 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)エラー
			LPBYTE Buff;							//読み込みバッファ用ポインター
			Buff = new BYTE[dwFileSize];
			DWORD dwRead;
			if(ReadFile(hFile, Buff, dwFileSize, &dwRead, NULL)) {
				bSuccess = TRUE; //読み込み成功
			}
			if(Buff[0] == 0xFF && Buff[1] == 0xFE) {//UTF-16、UTF-32のBOMチェック
				if(Buff[2] | Buff[3]) {				//いずれもNULLではない→UTF-16のBOM
					delete [] m_str;				//元の文字列を廃棄して
					//BOM以降の読み込んだデータを入れられるようにする
					m_str = new WCHAR[dwFileSize / sizeof(WCHAR)];
					CopyMemory(m_str, Buff + 2, dwFileSize - 2);
					//念のため、バッファの終端にNULLを置く
					m_str[(dwFileSize - 2) / sizeof(WCHAR) - 1] = L'\0';
				}
				else {	//Buff[2]とBuff[3])がいずれもNULL→UTF-32のBOM（またはUTF-16でNULLのみのファイルの場合）
					bSuccess = FALSE;	//UTF-32BOMの場合、何もせず読み込み失敗扱い
				}
			}
			else if(Buff[0] == 0xEF && Buff[1] == 0xBB && Buff[2] == 0xBF) {	//UTF-8のBOMチェック
				int iBuffSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff + 3), -1, NULL, NULL);
				if(iBuffSize) {	//UTF-8文字列の場合
					//m_str用メモリーの確保
					if(m_str)
						delete [] m_str;
					m_str = new WCHAR[iBuffSize];
					//UTF8をWCHARへ変換
					MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff + 3), -1, m_str, iBuffSize);
				}
				else {					//BOM付きUTF-8で変換不能な場合
					bSuccess = FALSE;	//何もせず読み込み失敗扱い
				}
			}
			else {						//UTF-16、32、8のBOMがない場合
				//先ずBOM無しUTF-8か否かをチェック
				int iBuffSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff), -1, NULL, NULL);
				if(iBuffSize) {			//BOM無しUTF-8の場合
					//m_str用メモリーの確保
					if(m_str)
						delete [] m_str;
					m_str = new WCHAR[iBuffSize];
					//UTF8をWCHARへ変換
					MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCCH)(Buff), -1, m_str, iBuffSize);
				}
				else {					//BOMが無く、UTF-8変換できない場合「丸のみ」
					m_str = new WCHAR[dwFileSize / sizeof(WCHAR) + 1];
					CopyMemory(m_str, Buff, dwFileSize);
					//念のため、バッファの終端にNULLを置く
					m_str[dwFileSize / sizeof(WCHAR)] = L'\0';
				}
			}
			delete [] Buff;							//読み込みバッファ解放
		}
		CloseHandle(hFile);							//ファイルのクローズ
	}
	return bSuccess;
}

//ファイルへの書き込み（規定値UTF-16 リトルエンディアン）
bool CSTRW::ToFile(WCHAR* Filter, WCHAR* DefExt, HWND hWnd = NULL) {

	WCHAR FileName[MAX_PATH];
	WCHAR file_path[MAX_PATH];
	OPENFILENAMEW ofn;				//オープンファイルダイアログ構造体
	//オープンファイルダイアログでファイル名を取得する
	ZeroMemory(&ofn, sizeof(ofn));	//ゼロ初期化
	FileName[0] = NULL;
	ofn.lStructSize		= sizeof(ofn);
	ofn.hwndOwner		= hWnd;
	ofn.lpstrFilter		= Filter;
	ofn.nFilterIndex	= 1;
	ofn.lpstrFile		= FileName;
	ofn.nMaxFile		= MAX_PATH;
	ofn.lpstrInitialDir = L".";
	ofn.lpstrDefExt		= DefExt;	//デフォルトの拡張子
	ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |
				OFN_OVERWRITEPROMPT;
	if(!GetSaveFileNameW(&ofn))	return FALSE;;
	//ファイルを書き込む
	BOOL bSuccess = FALSE;
	HANDLE hFile;
	hFile = CreateFileW(FileName, GENERIC_WRITE, NULL, NULL,	//ファイルを書込みでオープン
						CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile != INVALID_HANDLE_VALUE) {		//オープンできたか
		//バッファから文字列長取得し、NULL終端も加算
		DWORD dwTextLength = (lstrlenW(m_str) + 1) * sizeof(WCHAR);
		DWORD dwWritten;
		//BOMの書き込み - WindowsはIntel系でUTF-16 FF FE（リトルエンディアン）
		BYTE BOM[2] = {0xFF, 0xFE};
		if(WriteFile(hFile, BOM, 2, &dwWritten, NULL)) {
			WriteFile(hFile, m_str, dwTextLength, &dwWritten, NULL);
			bSuccess = TRUE;	//書込み成功
		}
		CloseHandle(hFile);			//ファイルのクローズ
	}
	return bSuccess;
}

//ファイルへの書き込み（規定値UTF-16 リトルエンディアン）
bool CSTRW::ToFile(WCHAR* FileName) {

	BOOL bSuccess = FALSE;
	HANDLE hFile;
	hFile = CreateFileW(FileName, GENERIC_WRITE, NULL, NULL,	//ファイルを書込みでオープン
						CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile != INVALID_HANDLE_VALUE) {		//オープンできたか
		//バッファから文字列長取得し、NULL終端も加算
		DWORD dwTextLength = (lstrlenW(m_str) + 1) * sizeof(WCHAR);
		DWORD dwWritten;
		//BOMの書き込み - WindowsはIntel系でUTF-16 FF FE（リトルエンディアン）
		BYTE BOM[2] = {0xFF, 0xFE};
		if(WriteFile(hFile, BOM, 2, &dwWritten, NULL)) {
			WriteFile(hFile, m_str, dwTextLength, &dwWritten, NULL);
			bSuccess = TRUE;	//書込み成功
		}
		CloseHandle(hFile);			//ファイルのクローズ
	}
	return bSuccess;
}

//UTF-8ファイルへの書き込み（boolはBOM有無のフラグ）
bool CSTRW::ToUTF8File(WCHAR* FileName, bool bom = TRUE) {

	BOOL bSuccess = FALSE;
	HANDLE hFile;
	hFile = CreateFileW(FileName, GENERIC_WRITE, NULL, NULL,	//ファイルを書込みでオープン
						CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile != INVALID_HANDLE_VALUE) {		//オープンできたか
		//WCHARをUTF8へ変換した際の文字列長（バイト）を取得
		DWORD dwBuffSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, m_str, -1, NULL, NULL, NULL, NULL);
		if(!dwBuffSize) {
			CloseHandle(hFile);				//ファイルのクローズ
			return FALSE;
		}
		//UTF-8文字列用メモリーの確保
		char* utf8 = new char[dwBuffSize];
		//WCHAR文字列をUTF8文字列へ変換
		WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, m_str, -1, utf8, dwBuffSize, NULL, NULL);
		//UTF-8文字列の書き込み
		DWORD dwWritten;
		if(bom) {	//BOM有り
			//BOMの書き込み - UTF-8 0xEF 0xBB 0xBF
			BYTE BOM[3] = {0xEF, 0xBB, 0xBF};
			if(WriteFile(hFile, BOM, 3, &dwWritten, NULL)) {
				WriteFile(hFile, utf8, dwBuffSize, &dwWritten, NULL);
				bSuccess = TRUE;	//書込み成功
			}
		}
		else {		//BOM無し
			if(WriteFile(hFile, utf8, dwBuffSize, &dwWritten, NULL))
				bSuccess = TRUE;	//書込み成功
		}
		delete [] utf8;				//UTF-8文字列メモリーの開放
		CloseHandle(hFile);			//ファイルのクローズ
	}
	return bSuccess;
}

//m_strの単語を切り取り出す
bool CSTRW::Next(CSTRW& destination) {

	CSTRW str;
	delete [] str.m_str;
	str.m_str = new WCHAR[lstrlenW(m_str) + 1];
	WCHAR* ptr = m_str;
	WCHAR* des = str.m_str;
	bool Terminate = TRUE;
	//スペース文字はスキップする
	while(iswspace(*ptr) || *ptr == L'　' || *ptr == L',') {
			ptr++;						//スキップスペース文字
	}
	//非スペース文字を調べる
	while(!iswspace(*ptr)) {			//スペース文字でない
		if(!*ptr) {						//NULL終端ならFALSEを返す
			Terminate = FALSE;
			break;
		}
		else if(*ptr == L'　') {
			break;						//全角"　"なら終了
		}
		else if(*ptr == L',') {
			ptr++;						//','をスキップして次の文字まで進む
			break;						//空白文字扱い
		}
		else if(*ptr == L'"') {
			ptr++;						//'"'をスキップして中身だけ取り出す
			while(*ptr != L'"') {		//'"'なら'"'まで進む
				if(!*ptr) {				//NULL終端ならFALSEを返す
					Terminate = FALSE;
					break;
				}
				*des = *ptr;
				ptr++;	des++;
			}
			ptr++;						//ソースポインターは次の文字へ進める
			break;						//ループを抜けてデスティネーションはNULLが入る
		}
		else if(*ptr == L'/' && *(ptr + 1) == L'*') {
			ptr++;	ptr++;				//"/*〜*/"コメント
			while(!(*ptr == L'*' && *(ptr + 1) == L'/')) {
				if(!*ptr) {				//NULL終端ならFALSEを返す
					Terminate = FALSE;
					break;
				}
				ptr++;
			}
			ptr++;	ptr++;
			//再度スペース文字をスキップする
			while(iswspace(*ptr) || *ptr == L'　' || *ptr == L',') {
				if(!*ptr) {				//NULL終端ならFALSEを返す
					Terminate = FALSE;
					break;
				}
				ptr++;				//スキップスペース文字
			}
			continue;
		}
		else if(*ptr == L'/' && *(ptr + 1) == L'/') {
			while(*ptr != L'\n') {		//"//"コメント
				if(!*ptr) {				//NULL終端ならFALSEを返す
					Terminate = FALSE;
					break;
				}
				ptr++;
			}
			while(iswspace(*ptr) || *ptr == L'　' || *ptr == L',') {
				if(!*ptr) {				//NULL終端ならFALSEを返す
					Terminate = FALSE;
					break;
				}
				ptr++;				//スキップスペース文字
			}
			continue;
		}
		else {
			*des = *ptr;
		}
		ptr++;	des++;
	}
	*des = L'\0';

	destination = str;			//destinationに切り取った文字列を代入
	str = ptr;					//strにm_strより進んだptr以降の文字列を代入
	delete [] m_str;
	m_str = new WCHAR[lstrlenW(str.m_str) + 1];
	lstrcpyW(m_str, str.m_str);	//m_strには切り取った後の文字列が入る
	if(*str.m_str || *destination.m_str)
		Terminate = TRUE;		//残がNULLだけでも切り取った文字列があれば続ける
	else
		Terminate = FALSE;		//NULLだけなら終わり
	return Terminate;			//成功･失敗フラグを返す
}

//この行末まで進む
bool CSTRW::GetLine(CSTRW& destination) {

	bool Terminate = TRUE;
	WCHAR* ptr = m_str;
	WCHAR* des = ptr;
	while(*des && *des != L'\n')
		des++;
	if(!*des) {
		destination = L"";
		Terminate = FALSE;	//文字列の終了サイン
	}
	else {
		*des = L'\0'; des++;				//NULL終端させ、一つ進める
		delete [] destination.m_str;
		destination.m_str = new WCHAR[des - ptr + 1];
		lstrcpyW(destination.m_str, ptr);
		destination.m_str[des - ptr - 1] = L'\n';	//NULLにした'\n'を付加
		destination.m_str[des - ptr] = L'\0';		//NULL終端
	}
	if(Terminate) {					//まだ文字列があれば残りを入れる
		CSTRW str = des;
		delete [] m_str;
		m_str = new WCHAR[lstrlenW(str.m_str) + 1];
		lstrcpyW(m_str, str.m_str);	//m_strには切り取った後の文字列が入る
	}
	if(*destination.m_str)			//NULLに出会ったが、
		Terminate = TRUE;			//切り取った文字列があれば続ける
	else
		Terminate = FALSE;			//NULLだけなら終わり
	return Terminate;				//成功･失敗フラグを返す
}

//次の行頭まで進む
bool CSTRW::NextLine() {

	WCHAR* ptr = m_str;
	while(*ptr != L'\n') {		//改行コードまで進む
		if(!*ptr) {				//途中でNULL終端したならFALSEを返す
			return FALSE;
		}
		ptr++;
	}
	ptr++;
	CSTRW str = ptr;
	delete [] m_str;
	m_str = new WCHAR[lstrlenW(str.m_str) + 1];
	lstrcpyW(m_str, str.m_str);	//m_strには切り取った後の文字列が入る
	return TRUE;				//成功･失敗フラグを返す
}

//文字列の検索
WCHAR* CSTRW::Find(WCHAR* ToFind, WCHAR* sp = 0) {

	if(!sp)
		sp = m_str;
	return StrStrW(sp, ToFind);
}

#endif
