/*!
 *	@(#)CConfigNode.cpp		1.00 2003/03/05
 *
 *	Copyright (c) 2003 Yuh
 */

#include "StdAfx.h"
#include "confignode.h"
#include "configxml.h"

CConfigNode::CConfigNode(void)
{
}

CConfigNode::~CConfigNode(void)
{
}

CConfigSwapBase::~CConfigSwapBase()
{
}



/*!
 *	自分のサブノードを取得
 *
 *	@param path	パス
 *	@param key	最後のキー
 *	@return		サブノード
 */
CConfigNode *CConfigNode::GetSubNode(CString path, CString &key)
{
	CString	topPath,subPath;
	if(ParsePath(path,topPath,subPath) == 0)
	{
		//	自分
		key = subPath;
		return this;
	}
	else
	{
		//	無かったら追加
		int	index = GetSubNodeIndex(topPath);
		if(index == -1)
		{
			index = (int)m_subNode.GetSize();
			m_subNode.Add(CConfigNode());
			m_subNode[index].m_name = topPath;
		}

		//	下位を検索
		return(m_subNode[index].GetSubNode(subPath,key));
	}
}


/*!
 *	データの設定
 *
 *	@param key	キー
 *	@param data	設定するデータ
 */
void CConfigNode::SetConfig(CString key, CString data)
{
	int	index = GetKeyIndex(key,FALSE);
	if(index == -1)
	{
		m_curData.Add(CConfigKey(key,data));
	}
	else
	{
		m_curData[index].m_data = data;
	}
}

/*!
 *	データの取得
 *
 *	@param key			キー
 *	@param noCase		大文字小文字を区別しない
 *	@param defaultData	デフォルト
 */
CString CConfigNode::GetConfig(CString key, CString defaultData,CConfigSwapBase *swap,int noCase)
{
	if(swap==NULL)
	{
		int	index = GetKeyIndex(key,noCase);
		if(index == -1)
		{
			return(defaultData);
		}
		else
		{
			return(m_curData[index].m_data);
		}
	}
	else
	{
		int	index = GetKeyIndex(key,noCase);
		if(index == -1)
		{
			return(swap->SwapString(key,defaultData));
		}
		else
		{
			return(swap->SwapString(key,m_curData[index].m_data));
		}
	}
}


/*!
 *	全てのキーを取得
 *
 *	@param list	キーリスト
 *	@return		キー数
 */
int CConfigNode::GetAllKey(CStringArray &list)
{
	list.RemoveAll();

	for(int i=0;i<m_curData.GetSize();i++)
		list.Add(m_curData[i].m_key);
	return((int)list.GetSize());
}

/*!
 *	全てのサブノードを取得
 *
 *	@param list	ノードリスト
 *	@return		ノード数
 */
int CConfigNode::GetAllSubNode(CStringArray &list)
{
	list.RemoveAll();

	//	自分が処理
	for(int i=0;i<m_subNode.GetSize();i++)
		list.Add(m_subNode[i].m_name);
	return((int)list.GetSize());
}

/*!
 *	全てのキー数を取得
 *
 *	@return		キー数
 */
int CConfigNode::GetKeyCount()
{
	return((int)m_curData.GetSize());
}

/*!
 *	全てのサブノード数を取得
 *
 *	@return		ノード数
 */
int CConfigNode::GetSubNodeCount()
{
	return((int)m_subNode.GetSize());
}

/*!
 *	パスの解析
 *
 *	@param path		パス
 *	@param topPath	トップレベルのパス
 *	@param subPath	残り
 *	@return			0:自分のキー、1:まだサブキーがある
 */
int CConfigNode::ParsePath(CString path, CString & topPath, CString & subPath)
{
	int	index = path.Find("::");
	if(index == -1)
	{
		topPath = "";
		subPath = path;
		return(0);
	}
	else
	{
		if(path == "::")
		{
			topPath = "";
			subPath = "";
			return(0);
		}

		topPath = path.Mid(0,index);
		subPath = path.Mid(index + 2);
		return(1);
	}
}

/*!
 *	自分のキーを検索
 *
 *	@param key		キー
 *	@param noCase	TRUE:大文字小文字を区別しない
 *	@return			-1:見つからない、その他：インデックス
 */
int CConfigNode::GetKeyIndex(CString key,int noCase)
{
	if(!noCase)
	{
		for(int i=0;i<m_curData.GetSize();i++)
		{
			if(m_curData[i].m_key == key)
				return(i);
		}
	}
	else
	{
		for(int i=0;i<m_curData.GetSize();i++)
		{
			if(m_curData[i].m_key.CompareNoCase(key) == 0)
				return(i);
		}
	}
	return -1;
}

/*!
 *	サブノードを検索
 *
 *	@param name		名前
 *	@return			-1:見つからない、その他：インデックス
 */
int CConfigNode::GetSubNodeIndex(CString name)
{
	for(int i=0;i<m_subNode.GetSize();i++)
	{
		if(m_subNode[i].m_name == name)
			return(i);
	}
	return -1;
}

/*!
 *	全てクリア
 *
 */
void CConfigNode::Clear(void)
{
	m_subNode.RemoveAll();
	m_curData.RemoveAll();
}

/*!
 *	ノード削除
 *
 */
void CConfigNode::DeleteNode(CString name)
{
	int index = GetSubNodeIndex(name);
	if(index != -1)
		m_subNode.RemoveAt(index);
}

/*!
 *	キー削除
 *
 */
void CConfigNode::DeleteKey(CString name)
{
	int index = GetKeyIndex(name,FALSE);
	if(index != -1)
		m_curData.RemoveAt(index);
}

/*!
 *	XMLデータにセーブ
 *
 *	@param element	保存先
 */
void CConfigNode::SaveToXml(MSXML::IXMLDOMDocumentPtr doc, MSXML::IXMLDOMElementPtr element, CString indent)
{
	// ノードを追加
	element->appendChild(doc->createTextNode(_bstr_t(indent)));
	MSXML::IXMLDOMElementPtr node;
	node = doc->createElement("Node");

	//	アトリビュート
	MSXML::IXMLDOMAttributePtr attr;
	attr = doc->createAttribute("name");
	attr->value = _bstr_t(m_name);
	node->setAttributeNode(attr);
	attr.Release();

	//	自分のデータを追加
	CString	leafIndent = indent + "\t";
	for(int i=0;i<m_curData.GetSize();i++)
	{
		node->appendChild(doc->createTextNode(_bstr_t(leafIndent)));
		CConfigXml::AddLeaf(doc,node,m_curData[i].m_key,m_curData[i].m_data);
	}

	//	サブノードのデータを追加
	for(int i=0;i<m_subNode.GetSize();i++)
	{
		m_subNode[i].SaveToXml(doc,node,indent + "\t");
	}

	node->appendChild(doc->createTextNode(_bstr_t(indent)));
	element->appendChild(node);

	node.Release();
}


/*!
 *	XMLデータからロード
 *
 *	@param element	保存先
 */
void CConfigNode::LoadFromXml(MSXML::IXMLDOMDocumentPtr doc,MSXML::IXMLDOMNodePtr cur)
{
	//	ノードリストを取得
	MSXML::IXMLDOMNodeListPtr	nodeList = cur->childNodes;

	for(int i=0;i<nodeList->length;i++)
	{
		MSXML::IXMLDOMNodePtr	node = nodeList->item[i];

		//	サブノード
		if(node->nodeName == _bstr_t("Node"))
		{
			CString name = CConfigXml::GetAttrv(doc,node,"name");
			m_subNode.Add(CConfigNode());
			m_subNode[m_subNode.GetSize()-1].m_name = name;
			m_subNode[m_subNode.GetSize()-1].LoadFromXml(doc,node);
		}
		else if(node->nodeName == _bstr_t("Leaf"))
		{
			CString key = CConfigXml::GetAttrv(doc,node,"key");
			SetConfig(key,(PCSTR)node->text);
		}

		node.Release();
	}

	nodeList.Release();
}


/*!
 *	キー名で置き換えた文字列を取得
 *
 *	@param str		文字列
 *	@param noData	該当しない置き換えの処置
 *	@return			文字列
 */
CString CConfigNode::GetKeySwapString(CString str, CString noData,CConfigSwapBase *swap)
{
	if(swap==NULL)
	{
		//	全部検索
		int	i;
		for(i=0;i<(int)m_curData.GetSize();i++)
		{
			int index = str.Find("%" + m_curData[i].m_key + "%");

			while(index != -1)
			{
				str = str.Mid(0,index) + m_curData[i].m_data + str.Mid(index + 2 + m_curData[i].m_key.GetLength());
				index = str.Find("%" + m_curData[i].m_key + "%");
			}
		}
	}
	else
	{
		//	全部検索
		int	i;
		for(i=0;i<(int)m_curData.GetSize();i++)
		{
			int index = str.Find("%" + m_curData[i].m_key + "%");

			while(index != -1)
			{
				CString data = swap->SwapString(m_curData[i].m_key,m_curData[i].m_data);
				str = str.Mid(0,index) + data + str.Mid(index + 2 + m_curData[i].m_key.GetLength());
				index = str.Find("%" + m_curData[i].m_key + "%");
			}
		}
	}

	//	該当しない置き換えを削除
	if(!noData.IsEmpty() && noData.Find("%") == -1)
	{
		CString	tmp;
		while(1)
		{
			int index = str.Find("%");
			if(index == -1)
			{
				tmp += str;
				break;
			}
			
			tmp += str.Mid(0,index);
			str = str.Mid(index+1);

			int index2 = str.Find("%");
			if(index == -1)
			{
				tmp += str;
				break;
			}

			//	%%?
			if(index2==0)
			{
				tmp += "%";
			}
			else
			{
				tmp += noData;

			}

			str = str.Mid(index2+1);
		}
		str = tmp;
	}


	return(str);
}

//	RAW Imageに
void CConfigNode::ToString(CBuffer &buf)
{
	// ノードを追加
	AddStartNode(buf);	

	//	自分のデータを追加
	for(int i=0;i<m_curData.GetSize();i++)
		AddLeaf(buf,m_curData[i].m_key,m_curData[i].m_data);

	//	サブノードのデータを追加
	for(int i=0;i<m_subNode.GetSize();i++)
		m_subNode[i].ToString(buf);
	
	//	終了
	AddEndNode(buf);
}


//	ノードを追加
void CConfigNode::AddStartNode(CBuffer &buf)
{
	buf += (char)START_NODE;
	AddOneText(buf,m_name);
}

//	ノードを終了
void CConfigNode::AddEndNode(CBuffer &buf)
{
	buf += (char)END_NODE;
}

//	Leafを追加
void CConfigNode::AddLeaf(CBuffer &buf,CString &key,CString &data)
{
	buf += (char)LEAF;
	AddOneText(buf,key);
	AddOneText(buf,data);
}

//	Textを追加
void CConfigNode::AddOneText(CBuffer &buf,CString &str)
{
	long len = str.GetLength();

	str.Replace("\\","\\\\");
	buf += str;
	buf += "\\0";
}

//	イメージからロード
int CConfigNode::FromString(char **ptr)
{
	while(1)
	{
		char code = **ptr;
		(*ptr)++;

		if(code == START_NODE)
		{
			CString name = GetOneText(ptr);
			m_subNode.Add(CConfigNode());
			m_subNode[m_subNode.GetSize()-1].m_name = name;
			m_subNode[m_subNode.GetSize()-1].FromString(ptr);
		}
		else if(code == LEAF)
		{
			CString key = GetOneText(ptr);
			SetConfig(key,GetOneText(ptr));
		}
		else if(code == END_NODE)
		{
			return(0);
		}
		else
		{
			return(-1);
		}
	}
}

//	テキスト読み込み
CString CConfigNode::GetOneText(char **buf)
{
	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);

}


