//
// CSVEntry
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"

#include "CsvEntry.h"
#include "CRes.h"

/// <summary>
/// コンストラクタ
/// </summary>
CsvEntry::CsvEntry()
{
	command = "";
	params.RemoveAll();
	paramsOrig.RemoveAll();
}

/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="cmd"></param>
CsvEntry::CsvEntry(const char* cmd)
{
	command = cmd;
	params.RemoveAll();
	paramsOrig.RemoveAll();
}

/// <summary>
/// コピーコンストラクタ
/// </summary>
/// <param name="entry"></param>
CsvEntry::CsvEntry(const CsvEntry& entry)
{
	command = entry.command;
	params.RemoveAll();
	paramsOrig.RemoveAll();

	for (int i = 0; i < entry.params.GetCount(); i++) {
		params.Add(entry.params[i]);
	}

	for (int i = 0; i < entry.paramsOrig.GetCount(); i++) {
		paramsOrig.Add(entry.paramsOrig[i]);
	}
}

/// <summary>
/// 代入演算子
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
CsvEntry& CsvEntry::operator=(const CsvEntry& entry)
{
	command = entry.command;
	params.RemoveAll();
	paramsOrig.RemoveAll();

	for (int i = 0; i < entry.params.GetCount(); i++) {
		params.Add(entry.params[i]);
	}

	for (int i = 0; i < entry.paramsOrig.GetCount(); i++) {
		paramsOrig.Add(entry.paramsOrig[i]);
	}

	return *this;
}

/// <summary>
/// デストラクタ
/// </summary>
CsvEntry::~CsvEntry()
{
	command = "";
	params.RemoveAll();
	paramsOrig.RemoveAll();
}

/// <summary>
/// CSVの文字列を返す
/// </summary>
/// <returns></returns>
CString CsvEntry::GetCsvLine()
{
	CString line;

	// コメントの場合は特殊な処理
	if (command.Compare(CSVCMD_COMMENT) == 0) {
		line = CString("#");
		if (params.GetSize() > 0) {
			line += GetParamStr(0);
		}
		return line;
	}

	// CSVを処理
	line = command;
	for (int i = 0; i < params.GetSize(); i++) {
		line += ",";
		line += params[i];
		if (params[i].GetLength() == 0) {
			// 空文字列の場合は特定の文字列で補完
			line += RESERVED_EMPTY;
		}
	}
	return line;
}

/// <summary>
/// パラメータを追加
/// </summary>
/// <param name="param"></param>
void CsvEntry::AddParam(CString param, bool trim)
{
	// 空文字列なら予約語で置換する
	if (trim) {
		if (param.Trim().GetLength() == 0) {
			param = RESERVED_EMPTY;
		}
	}
	else {
		if (param.GetLength() == 0) {
			param = RESERVED_EMPTY;
		}
	}

	// 要素を追加する
	params.Add(param);
	paramsOrig.Add(param);
}

/// <summary>
/// パラメータを追加
/// </summary>
/// <param name="param"></param>
void CsvEntry::AddParam(const char* param, bool trim)
{
	this->AddParam(CString(param), trim);
}

void CsvEntry::AddParam(int param)
{
	CString val, valOrig;
	val.Format("%d", param);
	valOrig.Format("%d", param);
	params.Add(val);
	paramsOrig.Add(valOrig);
}

void CsvEntry::AddParamBool(BOOL param)
{
	CString val, valOrig;
	if (param) {
		val = CString("true");
		valOrig = CString("true");
	}
	else {
		val = CString("false");
		valOrig = CString("false");
	}
	params.Add(val);
	paramsOrig.Add(valOrig);
}

/// <summary>
/// ベースパスを外しフルパス表現を相対パスに変更する
/// </summary>
/// <param name="basePath"></param>
void CsvEntry::AdjustBasePath(const char* basePath)
{
	// コマンドによっては何もしない
	if (false) {
		return;
	}

	// パラメータごとに処理
	params.RemoveAll();
	for (int i = 0; i < paramsOrig.GetSize(); i++) {
		CString val = paramsOrig[i];
		val = AdjustBasePathCore(basePath, (LPCTSTR)val);
		params.Add(val);
	}
}

/// <summary>
/// パスを調整する
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
CString CsvEntry::AdjustBasePathCore(const char* basePath, const char* value)
{
	CString val = value;
	CString base = basePath;

	// ベースパス指定がなければそのまま終了
	if (base.GetLength() <= 0) {
		return val;
	}

	if (base.GetAt(base.GetLength() - 1) != '\\') {
		base.Append("\\");
	}
	
	int pos = val.Find(base);
	if (pos == 0) {
		// ベースパスで開始している場合はパスを外す
		val = ((LPCTSTR)val) + base.GetLength();
		while ((val.GetAt(0) == '\\') || (val.GetAt(0) == '/')) {
			val = ((LPCTSTR)val) + 1;
		}
	}

	return val;
}

int CsvEntry::GetParamInt(int index)
{
	CString str = params.GetAt(index);
	int val = 0;
	int count = sscanf((LPCTSTR)str, "%d", &val);
	if (count <= 0) {
		return 0;
	}
	return val;
}

int CsvEntry::GetParamHex(int index)
{
	CString str = params.GetAt(index);
	int val = 0;
	int count = sscanf((LPCTSTR)str, "%02X", &val);
	if (count <= 0) {
		return 0;
	}
	return val;
}

CString CsvEntry::GetParamStr(int index)
{
	return params.GetAt(index);
}

BOOL CsvEntry::GetParamBool(int index)
{
	CString str = params.GetAt(index);
	CString upper = str.MakeUpper();
	if (upper.Compare("TRUE")==0) {
		return TRUE;
	}
	return FALSE;
}


/// <summary>
/// 文字列からCsvEntityを構築する
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
CsvEntry CsvEntry::FromString(CString line)
{
	// 1個目のトークンを得る
	int nTokenPos = 0;
	CString strToken = line.Tokenize(_T(","), nTokenPos).Trim();
	if (strToken.GetAt(0) == '#') {
		// コメント
		int strLength = strToken.GetLength();
		CString comment = strToken.Right(strLength-1);

		CsvEntry result;
		result.command = CSVCMD_COMMENT;
		if (comment.GetLength() > 0) {
			result.AddParam(comment, false);
		}
		return result;
	}
	CString command = CString((LPCTSTR)strToken);

	// パラメータを取り込む
	CStringArray params;
	strToken = line.Tokenize(_T(","), nTokenPos).Trim();
	while (!strToken.IsEmpty()) {
		// トークンが予約語の空文字列なら変換する
		if (strToken == RESERVED_EMPTY) {
			strToken = "";
		}

		// 要素を追加
		params.Add(strToken);
		strToken = line.Tokenize(_T(","), nTokenPos);
	}

	// CsvEntityを構築
	CsvEntry entity;
	entity.command = command;
	for (int i = 0; i < params.GetCount(); i++) {
		entity.params.Add(params[i]);
		entity.paramsOrig.Add(params[i]);
	}

	// 結果を返す
	return entity;
}

/// <summary>
/// ファイルからCSVを取り込んでtargetに入れる
/// </summary>
/// <param name="filename"></param>
/// <param name="target"></param>
/// <returns></returns>
CRes CsvEntry::FromFile(CString filename, CArray<CsvEntry>& target)
{
	// まず初期化
	target.RemoveAll();

	// ファイルを開く
	FILE* fn = fopen(filename, "rt");

	// 1行ずつ読んで処理する
	char buffer[256];
	while (fgets(buffer, 255, fn) != NULL) {
		CString line(buffer);
		line.Trim();
		if (line.GetLength() == 0) {
			// 空行無視
			continue;
		}

		// CsvEntryに変換する
		CsvEntry entry = CsvEntry::FromString(line);
		if (strcmp((LPCTSTR)entry.command, CSVCMD_NOP)==0) {
			// NOPなら無視
			continue;
		}
		if (strcmp((LPCTSTR)entry.command, CSVCMD_ERROR) == 0) {
			// ERRORなら中断
			CString msg;
			msg.Format("CSVのパースに失敗しました。[%s]", (LPCTSTR)line);
			CRes result = CRes::CreateError((LPCTSTR)line);
			fclose(fn);
			return result;
		}

		// リストに追加
		target.Add(entry);
	}
	return CRes::CreateOk();
}

/// <summary>
/// CSVのテキストから取り込んでtargetに入れる
/// </summary>
/// <param name="csvText"></param>
/// <param name="target"></param>
/// <returns></returns>
CRes CsvEntry::FromCsvText(CString csvText, CArray<CsvEntry>& target)
{
	// まず初期化
	target.RemoveAll();
	CString source = csvText;
	source.Trim();
	source.Replace("\r\n", "\n");

	// 1行ずつ読んで処理する
	int nTokenPos = 0;
	CString line = source.Tokenize(_T("\n"), nTokenPos).Trim();
	while (!line.IsEmpty()) {

		line.Trim();

		// CsvEntryに変換する
		CsvEntry entry = CsvEntry::FromString(line);
		if (strcmp((LPCTSTR)entry.command, CSVCMD_NOP) == 0) {
			// NOPなら無視
			line = source.Tokenize(_T("\n"), nTokenPos).Trim();
			continue;
		}
		if (strcmp((LPCTSTR)entry.command, CSVCMD_ERROR) == 0) {
			// ERRORなら中断
			CString msg;
			msg.Format("CSVのパースに失敗しました。[%s]", (LPCTSTR)line);
			CRes result = CRes::CreateError((LPCTSTR)line);
			return result;
		}

		// リストに追加
		target.Add(entry);

		// 次の行
		line = source.Tokenize(_T("\n"), nTokenPos);
	}
	return CRes::CreateOk();
}

/// <summary>
/// CArray<CsvEntry> から、テキストエリア用のCSVのテキストを構築して返す
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
CString CsvEntry::BuildCsvText(CArray<CsvEntry>& target)
{
	CString result = "";
	for (int i = 0; i < target.GetSize(); i++) {
		CsvEntry entry = target.GetAt(i);
		if (result.GetLength() != 0) {
			result += "\r\n";
		}
		result += entry.GetCsvLine();
	}

	// 結果を返す
	return result;
}

/// <summary>
/// CArray<CsvEntry> から、ベースパス指定の項目を見つけてベースパスを返す
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
CString CsvEntry::AdjustAboutBasePath(CArray<CsvEntry>& target)
{
	CString basePath = "";

	int listSize = (int)( target.GetSize() );
	for (int i = listSize - 1; i >= 0 ; i--) {
		CsvEntry entry = target.GetAt(i);
		if (strcmp((LPCTSTR)(entry.command), CSVCMD_SET_BASEPATH) == 0) {
			basePath = entry.params.GetAt(i);
			target.RemoveAt(i);
		}
	}

	// 結果を返す
	return basePath;
}
