#include "StdAfx.h"
#include "Workspace.h"
#include "WorkspaceXMLReader.h"
#include "WorkspaceXMLWriter.h"
#include "WorkspaceNodeWalker.h"
#include "WorkspaceNode.h"
#include "UTF8StringConvert.h"

//	デバッグ補助
#ifndef DEBUG_OUT
	#define	DEBUG_OUT	TRACE_U8
#endif

//	標準名
#define	DEFAULT_CLASS_TYPE	_U8("CWorkspace")

//	リードオンリーフラグ確認
#define	WRITE_FUNCTION_CHECK()	if(m_readOnly) { ASSERT(0); return; };


/***********************************************************************************
	コンストラクタ
***********************************************************************************/
CWorkspace::CWorkspace(CUTF8String className)
{
	if(className.IsEmpty())
		m_className = DEFAULT_CLASS_TYPE;
	else
		m_className = className;

	//	ルート名設定
	m_root = new CWorkspaceNode();
	m_root->SetNodeName(m_className);

	//	読み込み専用ではない
	m_readOnly = 0;
}

CWorkspace::~CWorkspace(void)
{
	m_root->Clear();
	delete m_root;
}


/***********************************************************************************
	アクセス可能な関数
***********************************************************************************/
/*!
	アクセスクラスを取得する
*/
CWorkspaceAccess CWorkspace::GetAccess(CUTF8String path)
{
	return CWorkspaceAccess(this,path);
}


/*!
	アクセスクラスを取得し、中身をクリア
*/
CWorkspaceAccess CWorkspace::GetCleanAccess(CUTF8String path)
{
	CWorkspaceAccess ret = CWorkspaceAccess(this, path);
	ret.Clear();
	return ret;
}


/////////////////////////////////////////////////////////////////////////////////////
//	入出力に関する処理
/////////////////////////////////////////////////////////////////////////////////////
/*!
	XMLファイルへ出力
*/
int CWorkspace::SaveToXMLFile(CUTF8String filePath)
{
	CRITICAL_SECTION_SCOPE();

	//	保存
	CWorkspaceXMLWriter	writer;
	return(writer.SaveToFile(m_root, filePath, m_className));
}

/*!
	XMLファイルよりロード
*/
int CWorkspace::LoadFromXMLFile(CUTF8String filePath)
{
	CRITICAL_SECTION_SCOPE();

	try
	{
		//	クリア
		m_root->Clear();

		//	読み込み
		CWorkspaceXMLReader	reader;
		reader.LoadFromFile(m_root, filePath, m_className);
	}
	catch(CXMLTextParserException err)
	{
		DEBUG_OUT(_U16("XML LoadError : %s\n"), err.GetErrorInfo());
		return(-1);
	}
	return(0);
}



/*!
	XML文字列へ出力
*/
CUTF8String CWorkspace::ToXMLString(CUTF8String savePath)
{
	CRITICAL_SECTION_SCOPE();

	//	保存ノードの取得
	CWorkspaceNode * node = GetSubNode(savePath);

	//	保存
	CWorkspaceXMLWriter	writer;
	return(writer.ToString(node, m_className));
}

/*!
	XML文字列から生成
*/
int CWorkspace::FromXMLString(CUTF8String xml)
{
	CRITICAL_SECTION_SCOPE();


	try
	{
		//	クリア
		m_root->Clear();

		//	読み込み
		CWorkspaceXMLReader	reader;
		reader.FromString(m_root, xml, m_className);
	}
	catch(CXMLTextParserException err)
	{
		DEBUG_OUT(_U16("XML LoadError : %s\n"), err.GetErrorInfo());
		return(-1);
	}
	return(0);
}


/*!
	別のCWorkspaceを複製
*/
void CWorkspace::Duplicate(CWorkspace &copyFrom, CUTF8String srcPath, CUTF8String destPath)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	copyFrom.Lock();

	//	入出力先を取得
	CWorkspaceNode * dNode = GetSubNode(destPath);
	CWorkspaceNode * sNode = copyFrom.GetSubNode(srcPath);

	//	名前以外コピー
	CUTF8String	name = dNode->GetNodeName();
	*dNode = *sNode;
	dNode->SetNodeName(name);

	copyFrom.Unlock();
}


/***********************************************************************************
	初期化など
***********************************************************************************/
/*!
	全データクリア
*/
void CWorkspace::Clear(LPCU8STR _path)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	CUTF8String path = ToNodePath(_path);
	CWorkspaceNode * node = GetSubNode(path);

	node->Clear();
}


/*!
	コピー

	srcConfigに別のCWorkspaceクラスを指定すると、別のクラスへコピーできる
*/
void CWorkspace::Copy(LPCU8STR _destPath, LPCU8STR _srcPath, IWorkspacePtr srcWorkspace)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	CUTF8String destPath = ToNodePath(_destPath);
	CUTF8String srcPath = ToNodePath(_srcPath);

	if(srcWorkspace==NULL || srcWorkspace==this)
	{
		//	内部コピー
		CWorkspaceNode * dNode = GetSubNode(destPath);
		InternalCopy(dNode,srcPath);
	}
	else
	{
		//	作業用バッファ
		CUTF8StringArray	keys, datas;
		CIStringArrayWrap	_keys(keys), _datas(datas);

		//	出力先取得
		CWorkspaceNode * dNode = GetSubNode(destPath);

		//	実際にコピー
		srcWorkspace->Lock();
		ExternalCopy(srcWorkspace, srcPath, dNode, _keys, _datas);
		srcWorkspace->Unlock();
	}
}


/*!
	リードオンリー
*/
void CWorkspace::SetReadOnly(int readOnly)
{
	CRITICAL_SECTION_SCOPE();
	m_readOnly = readOnly;
}



/***********************************************************************************
	Keyに関する処理
***********************************************************************************/
/*!
	データの設定
*/
void CWorkspace::SetKeyData(LPCU8STR path, LPCU8STR data, int noCase)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	CUTF8String key;
	CWorkspaceNode * node = GetSubNode(path, key);

	node->SetKeyData(key, data, noCase);
}


/*!
	データの取得
*/
void CWorkspace::GetKeyData(IStringPtr dest, LPCU8STR path, LPCU8STR defaultData, int noCase)
{
	CRITICAL_SECTION_SCOPE();

	CUTF8String key;
	CWorkspaceNode * node = GetSubNode(path, key);

	if(!m_autoSwap.IsEmpty())
		dest->SetString(m_autoSwap->GetKeyDataSwap(this, key, node->GetKeyData(key, defaultData, noCase)), -1);
	else
	{
		if(!m_autoSwapDefault.IsEmpty())
			dest->SetString(node->GetKeyData(key, m_autoSwapDefault->GetKeyDataSwap(this, key, defaultData), noCase), -1);
		else
			dest->SetString(node->GetKeyData(key, defaultData, noCase), -1);
	}
}



/*!
	データの削除
*/
void CWorkspace::DeleteKey(LPCU8STR path)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	CUTF8String key;
	CWorkspaceNode * node = GetSubNode(path,key);

	node->DeleteKey(key);
}


/*!
	キー数を取得
*/
int CWorkspace::GetKeyCount(LPCU8STR _path)
{
	CRITICAL_SECTION_SCOPE();

	//	パスがノードを示していると仮定する
	CUTF8String path = ToNodePath(_path);
	CWorkspaceNode * node = GetSubNode(path);

	return(node->GetKeyCount());
}


/*!
	全てのキー名を文字列として取得
*/
int CWorkspace::GetAllKeyPair(LPCU8STR _path, IStringArrayPtr keys, IStringArrayPtr datas)
{
	CRITICAL_SECTION_SCOPE();

	//	パスがノードを示していると仮定する
	CUTF8String path = ToNodePath(_path);

	//	ノード取得
	CWorkspaceNode * node = GetSubNode(path);

	//	取得
	return(node->GetAllKeyPair(keys, datas));
}


/***********************************************************************************
	サブノードに関する処理
***********************************************************************************/
/*!
	ノードを削除する
*/
void CWorkspace::DeleteNode(LPCU8STR _path)
{
	CRITICAL_SECTION_SCOPE();
	WRITE_FUNCTION_CHECK();

	//	パスがノードを示していると仮定する
	CUTF8String path = ToNodePath(_path);

	//	最後の::を削除
	path = path.Mid(0,path.GetLength()-2);


	if(path.IsEmpty())
	{
		//	ルートを削除？
		m_root->Clear();
	}
	else
	{
		CUTF8String key;
		CWorkspaceNode * node = GetSubNode(path,key);
		node->DeleteNode(key);
	}
}


/*!
	サブノード数を得る
*/
int CWorkspace::GetSubNodeCount(LPCU8STR _path)
{
	CRITICAL_SECTION_SCOPE();

	//	パスがノードを示していると仮定する
	CUTF8String path = ToNodePath(_path);

	CWorkspaceNode * node = GetSubNode(path);

	return(node->GetSubNodeCount());
}


/*!
	全てのサブノード名を文字列として取得
*/
int CWorkspace::GetAllSubNode(LPCU8STR _path,IStringArrayPtr list)
{
	CRITICAL_SECTION_SCOPE();

	//	パスがノードを示していると仮定する
	CUTF8String path = ToNodePath(_path);

	CWorkspaceNode * node = GetSubNode(path);

	return(node->GetAllSubNode(list));

}



/***********************************************************************************
	その他の処理
***********************************************************************************/
/*!
 	ノードパスに変更
 
 	@param path	パス
 	@return		パス
*/
CUTF8String CWorkspace::ToNodePath(CUTF8String path)
{
	if(path.GetLength() >= 2)
	{
		if(path.Right(2) != _U8("::"))
			path += _U8("::");
	}
	else
		path += _U8("::");

	//	"::"は不正
	if(path.Left(2) == _U8("::"))
		return path.Mid(2);
	return path;
}

/*!
	サブノードのポインタを取得
*/
CWorkspaceNode *CWorkspace::GetSubNode(LPCU8STR path, CUTF8String &key)
{
	CRITICAL_SECTION_SCOPE();

	CWorkspaceNodeWalker	nodeWalker;
	nodeWalker.SetPath(path);

	CWorkspaceNode *node = m_root->GetSubNode(nodeWalker, key);
	ASSERT(node != NULL);

	return(node);
}


/*!
	コピー内部処理
*/
void CWorkspace::InternalCopy(CWorkspaceNode *dest, CUTF8String srcPath)
{
	CWorkspaceNode * sNode = GetSubNode(srcPath);

	//	名前以外コピー
	CUTF8String	name = dest->GetNodeName();
	*dest = *sNode;
	dest->SetNodeName(name);
}


/*!
	コピー内部処理
*/
void CWorkspace::ExternalCopy(IWorkspace *src, CUTF8String srcPath, CWorkspaceNode *dNode, CIStringArrayWrap &tempKeys, CIStringArrayWrap &tempDatas)
{
	//
	//	全キーとデータをコピー
	//
	//	クリア
	dNode->Clear();

	//	キーペア取得
	src->GetAllKeyPair(srcPath, &tempKeys, &tempDatas);

	//	対象ノードへキーをコピー
	dNode->ClearAndSetKeys(tempKeys.GetStringArray(), tempDatas.GetStringArray());


	//
	//	サブノードをコピー
	//
	CUTF8StringArray		nodes;
	CIStringArrayWrap	_nodes(nodes);

	//	ノードリスト取得
	src->GetAllSubNode(srcPath, &_nodes);

	//	サブノードのコピー
	for(int i=0;i<nodes.GetCount();i++)
	{
		//	サブノード追加
		CWorkspaceNode *dSubNode = dNode->GetSubNodeByName(nodes[i]);

		//	再帰
		ExternalCopy(src, srcPath + nodes[i] + _U8("::"), dSubNode, tempKeys, tempDatas);
	}
}
