#include "StdAfx.h"
#include "config.h"
#import <msxml.dll>
#include "configxml.h"

#define	APP_TYPE	"04WebServer"

//	クリティカルセクション(タイムアウト付き)
#define	SCOPE_LOCK_CCONFIG()\
	CSingleLock	__scopeLock(&m_scopeLock);\
	__scopeLock.Lock();



CConfig::CConfig(void)
{
	m_tree.m_name = APP_TYPE;
	m_swap = NULL;
}

CConfig::CConfig(CConfig &other)
{
	//	Swap以外をコピー
	m_tree = other.m_tree;
	m_swap = NULL;
}

CConfig::~CConfig(void)
{
	if(m_swap)
		delete m_swap;
	m_swap = NULL;
}


/*!
 *	アクセスクラスの取得
 *
 *	@param path	パス
 *	@return		CConfigAccess *
 */
CConfigAccess CConfig::GetAccess(CString path)
{
	return CConfigAccess(this,path);
}

/*!
 *	アクセスクラスの取得とクリア
 *
 *	@param path	パス
 *	@return		CConfigAccess *
 */
CConfigAccess CConfig::GetCleanAccess(CString path)
{
	CConfigAccess ret = CConfigAccess(this,path);
	ret.Clear();
	return ret;
}

/*!
 *	データの設定
 *
 *	@param path	パス
 *	@param data	設定するデータ
 */
void CConfig::SetConfig(CString path, CString data)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	node->SetConfig(key,data);
}


/*!
 *	データの取得
 *
 *	@param path			パス
 *	@param noCase		大文字小文字の区別
 *	@param defaultData	デフォルト
 */
CString CConfig::GetConfig(CString path, CString defaultData,int noCase)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	return(node->GetConfig(key,defaultData,m_swap,noCase));
}


/*!
 *	全てのキーを取得
 *
 *	@param path	パス
 *	@param list	キーリスト
 *	@return		キー数
 */
int CConfig::GetAllKey(CString path,CStringArray &list)
{
	SCOPE_LOCK_CCONFIG();

	path = ToNodePath(path);

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	return(node->GetAllKey(list));
}


/*!
 *	全てのサブノードを取得
 *
 *	@param path	パス
 *	@param list	キーリスト
 *	@return		キー数
 */
int CConfig::GetAllSubNode(CString path,CStringArray &list)
{
	SCOPE_LOCK_CCONFIG();

	path = ToNodePath(path);

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	return(node->GetAllSubNode(list));
}

/*!
 *	全てのキー数を取得
 *
 *	@param path	パス
 *	@return		キー数
 */
int CConfig::GetKeyCount(CString path)
{
	SCOPE_LOCK_CCONFIG();

	path = ToNodePath(path);

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	return(node->GetKeyCount());
}


/*!
 *	全てのサブノード数を取得
 *
 *	@param path	パス
 *	@return		キー数
 */
int CConfig::GetSubNodeCount(CString path)
{
	SCOPE_LOCK_CCONFIG();

	path = ToNodePath(path);

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	return(node->GetSubNodeCount());
}


/*!
 *	クリア
 *
 *	@param path	パス
 */
void CConfig::Clear(CString path)
{
	SCOPE_LOCK_CCONFIG();

	path = ToNodePath(path);

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	node->Clear();
}

/*!
 *	ノードの削除
 *
 *	@param path	パス
 */
void CConfig::DeleteNode(CString path)
{
	SCOPE_LOCK_CCONFIG();
	path = ToNodePath(path);
	path = path.Mid(0,path.GetLength()-2);

	if(path.IsEmpty())
	{
		m_tree.Clear();
	}
	else
	{
		CString key;
		CConfigNode * node = m_tree.GetSubNode(path,key);
		node->DeleteNode(key);
	}
}

/*!
 *	キーの削除
 *
 *	@param path	パス
 */
void CConfig::DeleteKey(CString path)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);
	node->DeleteKey(key);
}

/*!
 *	コピー
 *
 *	@param destPath		コピー先パス
 *	@param srcPath		コピー元パス
 *	@param srcConfig	コピー元（省略可）
 */
void CConfig::Copy(CString destPath, CString srcPath, CConfig* srcConfig)
{
	SCOPE_LOCK_CCONFIG();

	destPath = ToNodePath(destPath);
	srcPath = ToNodePath(srcPath);

	if(srcConfig==NULL)
		srcConfig = this;

	CString key;
	CConfigNode * dNode = m_tree.GetSubNode(destPath,key);
	srcConfig->Copy(dNode,srcPath);
}

/*!
 *	コピー
 *
 *	@param dest			コピー先
 *	@param srcPath		コピー元パス
 */
void CConfig::Copy(CConfigNode *dest, CString srcPath)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * sNode = m_tree.GetSubNode(srcPath,key);

	//	名前以外コピー
	CString	name = dest->m_name;
	*dest = *sNode;
	dest->m_name = name;
}

/*!
 *	全てのキーをコピー
 *
 *	@param path			コピー元パス
 *	@param keyData		コピー先
 */
void CConfig::CopyAllKeys(CString path,CSortArray<CConfigNode::CConfigKey,CConfigNode::CConfigKey&> &keyData)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * sNode = m_tree.GetSubNode(path,key);

	sNode->CopyAllKeys(keyData);
}

/*!
 *	XMLファイルへ出力
 *
 *	@param path		ファイルパス
 *	@param savePath	パス
 *	@return		0：正常、-1：エラー
 */
int CConfig::SaveToFile(CString path,CString savePath)
{
	SCOPE_LOCK_CCONFIG();

	try
	{
		//	MSXML インスタンスの生成
		MSXML::IXMLDOMDocumentPtr pXMLDom(CLSID_DOMDocument);

		//	ProcessingInstruction の追加
		MSXML::IXMLDOMProcessingInstructionPtr pi;
		pi = pXMLDom->createProcessingInstruction("xml", "version='1.0'");
		pXMLDom->appendChild(pi);
		pi.Release();

		//	コメントの追加
		MSXML::IXMLDOMCommentPtr pc;
		pc = pXMLDom->createComment(APP_TYPE" CConfig class.");
		pXMLDom->appendChild(pc);
		pc.Release();

		// ルートを追加.
		MSXML::IXMLDOMElementPtr pe;
		pe = pXMLDom->createElement("CConfigClass");

		//	アトリビュート
		MSXML::IXMLDOMAttributePtr pa;
		pa = pXMLDom->createAttribute("type");
		pa->value = APP_TYPE;
		pe->setAttributeNode(pa);
		pa.Release();

		//	データの保存
		CString key;
		CConfigNode * node = m_tree.GetSubNode(savePath,key);
		node->SaveToXml(pXMLDom,pe);
		pe->appendChild(pXMLDom->createTextNode("\n"));

		pXMLDom->appendChild(pe);
		pe.Release();


		//	保存
		pXMLDom->save(_bstr_t(path));
		pXMLDom.Release();

		return(0);
	}
	catch(const _com_error& err)
	{
		TRACE("Error in [CConfig::SaveToFile]\nCOM Error %s\n",err.ErrorMessage());
		return(-1);
	}
}

//	XML文字列へ出力
CString CConfig::ToXMLString(CString savePath)
{
	SCOPE_LOCK_CCONFIG();

	try
	{
		//	MSXML インスタンスの生成
		MSXML::IXMLDOMDocumentPtr pXMLDom(CLSID_DOMDocument);

		//	ProcessingInstruction の追加
		MSXML::IXMLDOMProcessingInstructionPtr pi;
		pi = pXMLDom->createProcessingInstruction("xml", "version='1.0'");
		pXMLDom->appendChild(pi);
		pi.Release();

		//	コメントの追加
		MSXML::IXMLDOMCommentPtr pc;
		pc = pXMLDom->createComment(APP_TYPE" CConfig class.");
		pXMLDom->appendChild(pc);
		pc.Release();

		// ルートを追加.
		MSXML::IXMLDOMElementPtr pe;
		pe = pXMLDom->createElement("CConfigClass");

		//	アトリビュート
		MSXML::IXMLDOMAttributePtr pa;
		pa = pXMLDom->createAttribute("type");
		pa->value = APP_TYPE;
		pe->setAttributeNode(pa);
		pa.Release();

		//	データの保存
		CString key;
		CConfigNode * node = m_tree.GetSubNode(savePath,key);
		node->SaveToXml(pXMLDom,pe);
		pe->appendChild(pXMLDom->createTextNode("\n"));

		pXMLDom->appendChild(pe);
		pe.Release();


		//	保存
		CString ret = pXMLDom->xml;
		pXMLDom.Release();

		return(ret);
	}
	catch(const _com_error& err)
	{
		TRACE("Error in [CConfig::SaveToFile]\nCOM Error %s\n",err.ErrorMessage());
		return("");
	}
}


//	XML文字列から生成
int CConfig::FromXMLString(CString xml)
{
	SCOPE_LOCK_CCONFIG();

	//	クリア
	m_tree.Clear();

	try
	{
		//	MSXML インスタンスの生成
		MSXML::IXMLDOMDocumentPtr pXMLDom(CLSID_DOMDocument);

		//	ロード
		pXMLDom->loadXML(_bstr_t(xml));
		
		//	CConfigClass検索
		MSXML::IXMLDOMNodeListPtr rootList;
		rootList = pXMLDom->getElementsByTagName("CConfigClass");
		for(int i=0;i<rootList->length;i++)
		{
			MSXML::IXMLDOMNodePtr node = rootList->item[i];
			if(CConfigXml::GetAttrv(pXMLDom,node,"type") == APP_TYPE)
			{
				//	ノードリストを取得
				MSXML::IXMLDOMNodeListPtr	nodeList = node->childNodes;

				if(nodeList->length)
				{
					//	ロードする
					if(nodeList->item[0]->nodeName == _bstr_t("Node"))
					{
						m_tree.LoadFromXml(pXMLDom,nodeList->item[0]);
						node.Release();
						break;
					}
				}
			}
			node.Release();
		}


		rootList.Release();
		pXMLDom.Release();

		return(0);
	}
	catch(const _com_error& err)
	{
		TRACE("Error in [CConfig::LoadFromFile]\nCOM Error %s\n",err.ErrorMessage());
		return(-1);
	}
}


/*!
 *	XMLファイルからロード
 *
 *	@param path	パス
 *	@return		0：正常、-1：エラー
 */
int CConfig::LoadFromFile(CString path)
{
	SCOPE_LOCK_CCONFIG();

	//	クリア
	m_tree.Clear();

	try
	{
		//	MSXML インスタンスの生成
		MSXML::IXMLDOMDocumentPtr pXMLDom(CLSID_DOMDocument);

		//	ロード
		pXMLDom->load(_bstr_t(path));
		
		//	CConfigClass検索
		MSXML::IXMLDOMNodeListPtr rootList;
		rootList = pXMLDom->getElementsByTagName("CConfigClass");
		for(int i=0;i<rootList->length;i++)
		{
			MSXML::IXMLDOMNodePtr node = rootList->item[i];
			if(CConfigXml::GetAttrv(pXMLDom,node,"type") == APP_TYPE)
			{
				//	ノードリストを取得
				MSXML::IXMLDOMNodeListPtr	nodeList = node->childNodes;

				if(nodeList->length)
				{
					//	ロードする
					if(nodeList->item[0]->nodeName == _bstr_t("Node"))
					{
						m_tree.LoadFromXml(pXMLDom,nodeList->item[0]);
						node.Release();
						break;
					}
				}
			}
			node.Release();
		}


		rootList.Release();
		pXMLDom.Release();

		return(0);
	}
	catch(const _com_error& err)
	{
		TRACE("Error in [CConfig::LoadFromFile]\nCOM Error %s\n",err.ErrorMessage());
		return(-1);
	}
}


/*!
 *	ノードパスに変更
 *
 *	@param path	パス
 *	@return		パス
 */
CString CConfig::ToNodePath(CString path)
{
	if(path.GetLength() >= 2)
	{
		if(path.Right(2) != "::")
			path += "::";
	}
	else
		path += "::";

	return path;
}


/*!
 *	キー名で置き換えた文字列を取得
 *
 *	@param path		パス
 *	@param str		文字列
 *	@param noData	該当しない置き換えの処置
 *	@return			文字列
 */
CString CConfig::GetKeySwapString(CString path,CString str,CString noData,CConfigSwapBase *swap)
{
	SCOPE_LOCK_CCONFIG();

	CString key;
	CConfigNode * node = m_tree.GetSubNode(path,key);

	if(m_swap)
	{
		CConfigSwapBase *push = m_swap->m_next;

		m_swap->m_next = swap;
		CString	ret = node->GetKeySwapString(str,noData,m_swap);
		m_swap->m_next = push;

		return(ret);
	}
	else
	{
		return(node->GetKeySwapString(str,noData,m_swap));
	}
}

/*!
 *	置き換えコールバックを設定
 */
void CConfig::EnableAutoSwap(CConfigSwapBase *swap)
{
	if(m_swap)
		delete m_swap;
	m_swap = swap;
}

//	生データに変換
CString	CConfig::ToString(CString savePath)
{
	SCOPE_LOCK_CCONFIG();

	CBuffer	rawImage(0);

	//	データの保存
	CString key;
	CConfigNode * node = m_tree.GetSubNode(savePath,key);
	node->ToString(rawImage);

	//	終了
	rawImage += (char)0;

	return(rawImage);
}

//	生データから
int CConfig::FromString(CString load)
{
	SCOPE_LOCK_CCONFIG();

	m_tree.Clear();

	char *ptr = load.GetBuffer(0);

	CString	node = GetNextNode(&ptr);
	if(node.IsEmpty())
		return(-1);

	return(m_tree.FromString(&ptr));
}

//	ノードを取得
CString CConfig::GetNextNode(char **buf)
{
	char code = **buf;
	(*buf)++;

	if(code == CConfigNode::START_NODE)
	{
		int escape=0;
		for(int i=0;;i++)
		{
			if((*buf)[i] == 0)
				return("");
			
			if((*buf)[i] == '\\')
			{
				if(escape == 0)
					escape = 1;
				else
					escape = 0;	//２連続
			}
			else if((*buf)[i] == '0')
			{
				if(escape)
					break;
			}
			else
			{
				escape = 0;
			}
		}

		CString	ret;
		char *str = ret.GetBuffer(i + 1);
		memcpy(str,*buf,i-1);
		(*buf) += (i + 1);
		str[i-1] = 0;
		ret.ReleaseBuffer();
		ret.Replace("\\\\","\\");

		return(ret);
	}
	return("");
}

