#ifndef __V2_MSGBASE_H__
#define __V2_MSGBASE_H__

#include "cstring.h"
#include "cstream.h"
#include "msg.h"

/*
 * CMsg를 recursive하게 delete하는 방법에 대해 고려가 필요하다고 생각했는데
 * CMsg destructor에서 알아서 delete하고 있는 것으로 판명났다. 문제없음
 */


/* ================================================================ */
class V2_DLL_API CMsgBase
{
public:
	int m_iMsgNum;
	CString m_sMsgName;

public:
	CMsgBase(void) { m_iMsgNum = 0; }
	virtual ~CMsgBase() { }
	virtual int Load(CMsg *p_pMsg) = 0;
	virtual CMsg *ToMsg(void) = 0;
	virtual void Dump(CStream *p_pBuffer) const = 0;
	CString Repr(void) const { return CFormat("M_%s(CMsg%04d)", (char *)m_sMsgName, m_iMsgNum); }
	CString GetLastError() { return m_sError; }

protected:
	CString m_sError;
private:
	// make this class to noncopyable
	CMsgBase(CMsgBase &p_Obj) { m_iMsgNum = p_Obj.m_iMsgNum; }
	CMsgBase &operator=(const CMsgBase &p_Obj) { m_iMsgNum = p_Obj.m_iMsgNum; return *this; }
};


class V2_DLL_API CMsgBase_Array
{
public:
	virtual ~CMsgBase_Array() = 0;
	virtual void RemoveAll(void) = 0;
	virtual int Load(CMsgArray *p_pMsgArr) = 0;
	virtual CMsg *ToMsg(int p_idx) = 0;
	virtual int SetSize(int p_iSize) = 0;
	virtual int Grow(void) = 0;
	virtual int GetSize(void) const = 0;
	virtual CMsgBase *GetAt(int p_idx) const = 0;
	virtual CMsgBase &operator[](int p_idx) const = 0;
	virtual void Dump(CStream *p_pBuffer) const = 0;
	CString GetLastError() { return m_sError; }

protected:
	CString m_sMsgName;
	CString m_sError;
};

inline CMsgBase_Array::~CMsgBase_Array() {}

#define MSG_NULL_CHECK(var)	\
	if (var == NULL) { \
		m_sError.Format("%s : Out of memory", (char *)m_sMsgName); \
		return MA_ER_OUTOF_MEM; \
	}

#define _DUMP(stmt) \
	stmt; \
	p_pBuffer->AppendData("\n", 1); \
	p_pBuffer->AppendData(s.ToStr(), s.GetSize())

/* ================================================================ */

#ifndef MSG_GET_SET_MACROS
#define MSG_GET_SET_MACROS

/*
 *	=== GET/SET Macros ===
 *
 *	GET_VAL_ONE(MSG, TAG, VAR);
 *	GET_VAL_ARR(MSG, TAG, VAR);
 *	GET_SUB_ONE(MSG, TAG, VAR);
 *	GET_SUB_ARR(MSG, TAG, VAR);
 *
 *	SET_VAL_ONE(MSG, TAG, VAR);
 *	SET_VAL_ARR(MSG, TAG, VAR);
 *	SET_SUB_ONE(MSG, TAG, VAR);
 *	SET_SUB_ARR(MSG, TAG, VAR);
 */

// need int iRet;
#define GET_VAL_ONE(MSG, TAG, VAR) \
	iRet = MSG->GetValue(TAG, &VAR); \
	if (iRet < 0 && iRet != MA_ER_NO_DATA) { \
		m_sError.Format("%s:GET_VAL_ONE(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
		return iRet; \
	}

// need int iRet;
#define GET_VAL_ARR(MSG, TAG, VAR)	GET_VAL_ONE(MSG, TAG, VAR)

// need CMsg *pSub = NULL; int iRet;
#define GET_SUB_ONE(MSG, TAG, VAR) \
	iRet = MSG->GetValue(TAG, &pSub); \
	if (iRet >= 0) { \
		iRet = VAR.Load(pSub); \
		if (iRet < 0) { \
			m_sError.Format("%s:GET_SUB_ONE(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
			return iRet; \
		} \
	}

// need CMsgArray vaSubArr; int iRet;
#define GET_SUB_ARR(MSG, TAG, VAR) \
	iRet = MSG->GetValue(TAG, &vaSubArr); \
	if (iRet >= 0) { \
		iRet = VAR.Load(&vaSubArr); \
		if (iRet < 0) { \
			m_sError.Format("%s:GET_SUB_ARR(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
			return iRet; \
		} \
	}



// need int iRet;
#define SET_VAL_ONE(MSG, TAG, VAR) \
	iRet = MSG->SetValue(TAG, VAR); \
	if (iRet < 0) { \
		m_sError.Format("%s:SET_VAL_ONE(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
		return NULL; \
	}

// need int i; int iRet;
#define SET_VAL_ARR(MSG, TAG, VAR) \
	for (i = 0; i < VAR.GetSize(); i++) { \
		SET_VAL_ONE(MSG, TAG, VAR.GetAt(i)); \
	}

// need CMsg *pSub = NULL; int iRet;
#define SET_SUB_ONE(MSG, TAG, VAR) \
	pSub = VAR.ToMsg(); \
	if (pSub == NULL) { \
		m_sError.Format("%s:SET_SUB_ONE(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
		return NULL; \
	} else { \
		SET_VAL_ONE(MSG, TAG, pSub); \
	}

// need int i; CMsg *pSub == NULL; int iRet;
#define SET_SUB_ARR(MSG, TAG, VAR) \
	for (i = 0; i < VAR.GetSize(); i++) { \
		pSub = VAR.ToMsg(i); \
		if (pSub == NULL) { \
			m_sError.Format("%s:SET_SUB_ARR(M%04d.T%d) fail", (char *)m_sMsgName, MSG->GetSelfNum(), TAG); \
			return NULL; \
		} else { \
			SET_VAL_ONE(MSG, TAG, pSub); \
		} \
	}
#endif	// MSG_GET_SET_MACROS



#endif	// __V2_MSGBASE_H__
