#ifndef __MSG_H__
#define __MSG_H__

#include <stdio.h>
#include <string.h>

#include "exception.h"
#include "cstream.h"
#include "cstring.h"
#include "cint.h"
#include "cdbl.h"
#include "cbool.h"
#include "cdate.h"
#include "str_array.h"
#include "int_array.h"
#include "dbl_array.h"
#include "bool_array.h"
#include "date_array.h"

#include "msg_error.h"
#include "msg_dic.h"

class CMsgValue;
class CMsgArray;
class CMsg;


extern V2_DLL_API CMsg *MA_NewMsg(int p_iMsgNum);
extern V2_DLL_API void MA_FreeMsg(CMsg *p_pMsg);

class V2_DLL_API CMsg : public CLastError
{
public:
	CMsg(int p_iSelfNum=0, int p_iRootNum=0, int p_iSelfTagNum=0, CMsgDic *p_pMsgDic=MA_GetMsgDic());
	~CMsg();

	int GetValue(int p_iTagNum, CInt *p_pInt);
	int GetValue(int p_iTagNum, CDbl *p_pDbl);
	int GetValue(int p_iTagNum, CBool *p_pBool);

	int GetValue(char *p_szTagName, CInt *p_pInt);
	int GetValue(char *p_szTagName, CDbl *p_pDbl);
	int GetValue(char *p_szTagName, CBool *p_pBool);

	int SetValue(int p_iTagNum, CInt p_iInt);
	int SetValue(int p_iTagNum, CDbl p_dDbl);
	int SetValue(int p_iTagNum, CBool p_bBool);

	int SetValue(char *p_szTagName, CInt p_iInt);
	int SetValue(char *p_szTagName, CDbl p_dDbl);
	int SetValue(char *p_szTagName, CBool p_bBool);

	// Get/Set by TagNum
	int GetValue(int p_iTagNum, CString *p_pStr);
	int GetValue(int p_iTagNum, int *p_pInt);
	int GetValue(int p_iTagNum, double *p_pDbl);
	int GetValue(int p_iTagNum, bool *p_pBool);
	int GetValue(int p_iTagNum, CDate *p_pDate);
	int GetValue(int p_iTagNum, CMsg **p_pSubMsg);
	CMsg *GetValue(int p_iTagNum);

	int GetValue(int p_iTagNum, CStrArray *p_pStrArr);
	int GetValue(int p_iTagNum, CIntArray *p_pIntArr);
	int GetValue(int p_iTagNum, CDblArray *p_pDblArr);
	int GetValue(int p_iTagNum, CBoolArray *p_pBoolArr);
	int GetValue(int p_iTagNum, CDateArray *p_pDateArr);
	int GetValue(int p_iTagNum, CMsgArray *p_pMsgArr);
	
	int SetValue(int p_iTagNum, CString &p_sStr) { return SetValue(p_iTagNum, (const char *)p_sStr); }
	int SetValue(int p_iTagNum, const char *p_pStr);
	int SetValue(int p_iTagNum, int p_iInt);
	int SetValue(int p_iTagNum, double p_dDbl);
	int SetValue(int p_iTagNum, bool p_bBool);
	int SetValue(int p_iTagNum, CDate p_nDate);
	int SetValue(int p_iTagNum, CMsg *p_pSubMsg);

	// Get/Set by TagName
	int GetValue(char *p_szTagName, CString *p_pStr);
	int GetValue(char *p_szTagName, int *p_pInt);
	int GetValue(char *p_szTagName, double *p_pDbl);
	int GetValue(char *p_szTagName, bool *p_pBool);
	int GetValue(char *p_szTagName, CDate *p_pDate);
	int GetValue(char *p_szTagName, CMsg **p_pSubMsg);
	CMsg *GetValue(char *p_szTagName);
	
	int GetValue(char *p_szTagName, CStrArray *p_pStrArr);
	int GetValue(char *p_szTagName, CIntArray *p_pIntArr);
	int GetValue(char *p_szTagName, CDblArray *p_pDblArr);
	int GetValue(char *p_szTagName, CBoolArray *p_pBoolArr);
	int GetValue(char *p_szTagName, CDateArray *p_pDateArr);
	int GetValue(char *p_szTagName, CMsgArray *p_pMsgArr);

	int SetValue(char *p_szTagName, CString &p_sStr) { return SetValue(p_szTagName, (const char *)p_sStr); }
	int SetValue(char *p_szTagName, const char *p_pStr);
	int SetValue(char *p_szTagName, int p_iInt);
	int SetValue(char *p_szTagName, double p_dDbl);
	int SetValue(char *p_szTagName, bool p_bBool);
	int SetValue(char *p_szTagName, CDate p_nDate);
	int SetValue(char *p_szTagName, CMsg *p_pSubMsg);


	void Init(int p_iSelfNum, int p_iRootNum=0, int p_iSelfTagNum=0);		// throw CException
	void SetParsedData(int p_iTagNum, CString p_sData);	// throw CException
	CMsg *MakeSubMsgEntry(int p_iTagNum);				// throw CException

	void SetVer(CString &p_sVer);
	void SetRootNum(int p_iRootNum) { m_iRootNum = p_iRootNum; }
	void SetParentMsg(CMsg *p_pParent) { m_pParent = p_pParent; }
	int AddChild(CMsg *p_pChild);

	int GetMsgNum(void) { return m_iRootNum; }
	int GetSelfNum(void) { return m_iSelfNum; }
	int GetSelfTagNum(void) { return m_iSelfTagNum; }
	CMsg *GetParentMsg(void) { return m_pParent; }

	int LoadXml(char *p_szXmlData);
	int ToXml(CStream *p_pBuf, bool p_bArrPrefix=false, bool p_bTagByName=false, int p_iDepth=1);

	void Dump(CStream *p_pBuffer, bool p_bTagByName=false, int p_iDepth=1);
	void DumpXml(CStream *p_pBuffer, bool p_bTagByName=false, int p_iDepth=1);

	bool IsEmpty() const { return m_bEmpty; }

	//============================
	int GetArrSize(int p_iTagNum);
	int GetValueAt(int p_iTagNum, int p_iIdx, CString *p_pStr);
	int GetValueAt(int p_iTagNum, int p_iIdx, int *p_pInt);
	int GetValueAt(int p_iTagNum, int p_iIdx, double *p_pDbl);
	int GetValueAt(int p_iTagNum, int p_iIdx, bool *p_pBool);
	int GetValueAt(int p_iTagNum, int p_iIdx, CDate *p_pDate);
	int GetValueAt(int p_iTagNum, int p_iIdx, CMsg **p_pSubMsg);

private:
	CMsgValue *GetHashBag(int p_iTagNum);	// inline	Codes locate in the end of this file
	void SetHashBag(int p_iTagNum, CMsgValue *p_pValue);	// inline

private:
	int m_iRootNum;			// root msgnum
	int m_iSelfNum;			// self msgnum
	int m_iSelfTagNum;		// instance tagnum
	bool m_bEmpty;			// if msg does not have any value or submsg then true
	char m_szVer[16+1];		// msg version
	CMsgValue *m_paHashBag[MAX_HASHBAG_SIZE];

	CMsg *m_pParent;		// parent msg pointer
	CMsgArray *m_pChild;	// children msg array
	CMsgDic *m_pMsgDic;		// dictionary manager
	CMsgInfo *m_pMsgInfo;	// msg dictionary info
};

//============================================================================
class V2_DLL_API CMsgValue
{
public:
	int m_iTagNum;
	E_TagType m_eType;
	bool m_bSet;	// true : value assigned, false : not assigned
	bool m_bMulti;
	union {
		CString *pStr;	// --+
		int iInt;		//   |
		double dDbl;	//   |- use when m_bMulti == false
		bool bBool;		//   |
		CDate *pDate;	//   |
		CMsg *pSubMsg;	// --+
		CStrArray *pStrArr;		// --+
		CIntArray *pIntArr;		//   |
		CDblArray *pDblArr;		//   |- use when m_bMulti == true
		CBoolArray *pBoolArr;	//   |
		CDateArray *pDateArr;	//   |
		CMsgArray *pSubMsgArr;	// --+
	} m_u;
	CMsgValue *m_pNext;

	CMsgValue();
	~CMsgValue();
};



//============================================================================
class V2_DLL_API CMsgArray : public CLastError
{
public:
	CMsgArray(bool p_bAdoptFlag=true);
	~CMsgArray();

	int Add(CMsg *p_pNew);
	void RemoveAll(void);
	int GetSize(void);
	void SetSize(int p_iSize);
	void SetBufferSize(int p_iBufSize);
	CMsg *GetAt(int p_iIndex);
	int SetAt(int p_iIndex, CMsg *p_pNew);
	void SetAdoptFlag(bool p_bAdoptFlag);
private:
	void GrowBuffer(int p_iSize=0);

private:
	CMsg **m_ppData;
	int m_iSize;
	int m_iBufferSize;
	bool m_bAdoptFlag;	// if this is false, don't free each elements pointer
};

#endif	// __MSG_H__
