#include "stdafx.h"
#include "Rnsf7.h"
#include "lvcsv.h"
#include "opendlg.h"

// meanの配列数をカウントする
int lvCountColumn(const char **means)
{
	int nColumn;

	for(nColumn=0 ; means[nColumn] ; ++nColumn)
		;

	return nColumn;
}


// リストビューの内容をCSVファイルへ出力する
// 次のルールに従う。
// ・セルはカンマで区切る
// ・セル内にカンマかダブルコーティションが含まれている場合は、ダブルコーティションで囲む
// ・セル内にダブルコーティションが含まれている場合は、ダブルコーティションを重ねる
// ・セル内に改行コードが含まれている場合は、戻せないかもしれない旨警告する
void lvSaveToCsv(HWND hWnd, HWND hListView, int nColumn)
{
	char fname[MAX_PATH*2] = "";
	int n, i, c;
	char *pstr;

	if ( saveDialog( hWnd, "CSVファイル(*.csv)\0*.csv\0すべてのファイル(*.*)\0*.*\0", fname, _countof(fname), "CSVファイルへ保存", "csv" ) ) {
		FILE *fp;
		int  iErr = 0;

		if (0 == fopen_s(&fp, fname, "wt")) {
			n = ListView_GetItemCount(hListView);
			for(i=0 ; i<n ; ++i) {
				for(c=0 ; c<nColumn ; ++c) {
					// カンマ区切りの実現
					if (c > 0) {
						if (EOF == fputc(',', fp)) {
							iErr = errno;
							break;
						}
					}
					pstr = lvGetItemString(hListView, i, c);
					if (pstr) {
						if (strpbrk(pstr, ",\"\r\n") == NULL) {
							// " か , か改行が含まれていない場合は、ダブルコーテーションで囲まない
							if (EOF == fputs(pstr, fp)) {
								iErr = errno;
								break;
							}
						}
						else {
							// " か , か改行が含まれている場合は、ダブルコーテーションで囲む(開き)
							if (EOF == fputc('"', fp)) {
								iErr = errno;
								break;
							}

							for(int j=0 ; pstr[j] ; ++j) {
								// 内容を１文字ずつ出力
								if (EOF == fputc(pstr[j], fp)) {
									iErr = errno;
									break;
								}

								// "の場合は2重にして出力
								if (pstr[j] == '"') {
									if (EOF == fputc(pstr[j], fp)) {
										iErr = errno;
										break;
									}
								}
							}
							if (iErr) {
								break;
							}

							// " か , か改行が含まれている場合は、ダブルコーテーションで囲む(閉じ)
							if (EOF == fputc('"', fp)) {
								iErr = errno;
								break;
							}
						}
						safefree(pstr);
					}
				}
				if (iErr) {
					break;
				}
				if (EOF == fputc('\n', fp)) {
					iErr = errno;
					break;
				}
			}
			fclose(fp);

			if (iErr) {
				DeleteFile(fname);

				char mes[1025];
#pragma warning( disable : 4996 )
				wsprintf(mes, "%s へ書き込みできませんでした。\r\n%s", fname, _sys_errlist[iErr]);
#pragma warning( default : 4996 )
				MessageBox(hWnd, mes, "CSVファイルへ出力", MB_OK | MB_ICONSTOP);
			}
			else {
				MessageBox(hWnd, "CSVファイルへの出力を行いました。", "CSVファイルへ出力", MB_OK | MB_ICONINFORMATION);
			}
		}
		else {
			iErr = errno;

			char mes[1025];
#pragma warning( disable : 4996 )
			wsprintf(mes, "%s を開けません。\r\n%s", fname, _sys_errlist[iErr]);
#pragma warning( default : 4996 )
			MessageBox(hWnd, mes, "CSVファイルへ出力", MB_OK | MB_ICONSTOP);
		}
	}
}

// リストビューの内容をCSVから読み取る共通関数
// 次のルールに従う。
// ・セルはカンマで区切る
// ・セル内にカンマかダブルコーティションが含まれている場合は、ダブルコーティションで囲む
// ・セル内にダブルコーティションが含まれている場合は、ダブルコーティションを重ねる
// ・行の途中に改行があった場合、以降のカラムは空となる。
// ・１行のカラム数がリストビューのカラム数を上回る場合、上回った分は無視される。
// ・１カラム目に重複のある行は、警告を表示し取り扱わない。
// ・１行のサイズは2KBまで。
bool lvLoadFromCsvCom(HWND hWnd, HWND hListView, int nColumn, bool bClear)
{
	char fname[MAX_PATH*2] = "";

	if ( openDialog( hWnd, "CSVファイル(*.csv)\0*.csv\0すべてのファイル(*.*)\0*.*\0", fname, _countof(fname), "CSVファイルから読み出し", "csv" ) ) {
		FILE *fp;
		int  iErr = 0;
		bool bChofuku = 0;

		if (0 == fopen_s(&fp, fname, "rt")) {
			LVITEM lvitem;
			char buf[2050], szPart[2050], *pPart;
			int  c, j, new_i = 0, k;

			if (bClear) {
				// 事前にクリア
				ListView_DeleteAllItems(hListView);
			}

			while(!feof(fp)) {
				ZeroMemory(buf, sizeof(buf));

				// 1行読み込み
				if (NULL == fgets(buf, _countof(buf)-1, fp)) {
					if (!feof(fp)) {
						iErr = errno;
					}
					break;
				}

				// 1行の解析
				for(c=j=0 ; c<nColumn && buf[j] ; ++c) {
					
					if (buf[j] == '"') {
						// ダブルコーテーションで始まっている場合 … 注意して読み出し
						pPart =  szPart;
						ZeroMemory(szPart, sizeof(szPart));
						k = 0;
						++j;

						for(k=0 ;; ++j) {
							if (buf[j] == '"' && buf[j+1] == '"') {
								// " + " = "
								szPart[k] = '"';
								++j;
								++k;
							}
							else if (buf[j] == '"' && buf[j+1] == ',') {
								// " + , = ,
								j += 2;
								++k;
								break;
							}
							else if (buf[j] == '"' && (buf[j+1] == '\r' || buf[j+1] == '\n' || buf[j+1] == '\0')) {
								// " + 行終端
								break;
							}
							else if (buf[j] == '\0') {
								// いきなりあらわれた終端 … 次の行へ続行
								// 1行読み込み
								if (NULL == fgets(buf, _countof(buf)-1, fp)) {
									if (!feof(fp)) {
										iErr = errno;
									}
									break;
								}
								j = -1;
							}
							else {
								szPart[k] = buf[j];
								++k;
							}
						}

						if (iErr) {
							break;
						}
					}
					else {
						// ダブルコーテーションで始まっていない場合 … カンマか改行（終端）まで読み出し
						pPart = &buf[j];
						while(!(buf[j] == ',' || buf[j] == '\r' || buf[j] == '\n' || buf[j] == '\0'))
							++j;

						if (buf[j] == ',') {
							buf[j] = '\0';
							++j;
						}
						else if (buf[j] == '\r' || buf[j] == '\n') {
							buf[j] = '\0';
						}
					}

					if(c == 0) {
						// 最初のカラムならインサート

						// 第１カラムに重複があったら警告
						char *pstr;
						bool bChofuku1 = 0;
						int n = ListView_GetItemCount(hListView);
						for(int i=0 ; i<n ; ++i) {
							pstr = lvGetItemString(hListView, i, 0);
							if (pstr) {
								if ( _stricmp(pPart, pstr) == 0 ) {
									free(pstr);
									bChofuku1 = 1;
									break;
								}
								free(pstr);
							}
						}

						if (bChofuku1) {
							bChofuku = 1;
							break; // 重複があったらスキップ
						}
						
						// インサート
						ZeroMemory(&lvitem, sizeof(LVITEM));
						lvitem.mask = LVIF_TEXT;
						lvitem.iItem = n;
						lvitem.iSubItem = 0;
						lvitem.pszText = pPart;
						new_i = ListView_InsertItem(hListView, &lvitem);
					}
					else {
						// そうでなければセット
						ListView_SetItemText(hListView, new_i, c, pPart);
					}
				}
			}

			fclose(fp);

			if (iErr) {
				char mes[1025];
#pragma warning( disable : 4996 )
				wsprintf(mes, "%s から読み込みできませんでした。\r\n%s", fname, _sys_errlist[iErr]);
#pragma warning( default : 4996 )
				MessageBox(hWnd, mes, "CSVファイルから入力", MB_OK | MB_ICONSTOP);
			}
			else if (bChofuku) {
				MessageBox(hWnd, "CSVファイルから入力を行いました。\r\n第1カラムに重複のあったデータは除外しました。", "CSVファイルから入力", MB_OK | MB_ICONWARNING);
			}
			else {
				MessageBox(hWnd, "CSVファイルから入力を行いました。", "CSVファイルから入力", MB_OK | MB_ICONINFORMATION);
			}

			return 1;
		}
		else {
			iErr = errno;

			char mes[1025];
#pragma warning( disable : 4996 )
			wsprintf(mes, "%s を開けません。\r\n%s", fname, _sys_errlist[iErr]);
#pragma warning( default : 4996 )
			MessageBox(hWnd, mes, "CSVファイルから入力", MB_OK | MB_ICONSTOP);

			return 0;
		}
	}
	return 0;
}


// リストビューの内容をCSVファイルから入力する。元にあったデータは失われる。
bool lvLoadFromCsv(HWND hWnd, HWND hListView, int nColumn)
{
	return lvLoadFromCsvCom(hWnd, hListView, nColumn, 1);
}

// リストビューの内容をCSVファイルから追加入力する。元にあったデータは失われない。
bool lvAppendFromCsv(HWND hWnd, HWND hListView, int nColumn)
{
	return lvLoadFromCsvCom(hWnd, hListView, nColumn, 0);
}
