#ifndef __MSG_H_
#define __MSG_H_

#include "netobj.h"

class CStringNum : public CString
{
public:
	CStringNum() {}
	CStringNum(CString& s) : CString(s) {}
	CStringNum(LPCTSTR  s) : CString(s) {}
	CStringNum(int    n) { Format("%d" ,n); }
	CStringNum(long   n) { Format("%ld",n); }
	CStringNum(double n) { Format("%g" ,n); }
	CStringNum& operator=(LPCTSTR s){ CString::operator=(s); return *this; }
	CStringNum& operator=(int    n) { Format("%d" ,n); return *this; }
	CStringNum& operator=(long   n) { Format("%ld",n); return *this; }
	CStringNum& operator=(double n) { Format("%g" ,n); return *this; }
	operator int   () { return atoi((LPCTSTR)this); }
	operator long  () { return atol((LPCTSTR)this); }
	operator double() { return atof((LPCTSTR)this); }
};

class Msg : public CNetObject
{
	DECLARE_SERIAL(Msg)
protected:
	CString	m_sSourceType;
	CString	m_sSourceId;
	CString	m_sAction;
	CStringArray	   m_aParams;
public:
	CMapStringToString m_mapParams;

	Msg(LPCTSTR source = "", LPCTSTR id = "", LPCTSTR action = "")
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;
	}

	Msg(LPCTSTR source, LPCTSTR id, LPCTSTR action, LPCTSTR params)
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;

		StringToParams(params);
	}

	Msg(Msg& msg)
	{
		operator=(msg);
	}

    virtual ~Msg() {}

	void Create(LPCTSTR source = "", LPCTSTR id = "", LPCTSTR action = "")
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;
	}

	void Create(LPCTSTR source = "", int id = 0, LPCTSTR action = "")
	{
		m_sSourceType	= source;
		m_sSourceId.Format("%d",id);
		m_sAction		= action;
	}

	Msg& operator=(Msg& msg)
	{
		m_sSourceType	= msg.m_sSourceType;
		m_sSourceId		= msg.m_sSourceId;
		m_sAction		= msg.m_sAction;
		m_mapParams.RemoveAll();
		CString key,val;
		POSITION p = msg.m_mapParams.GetStartPosition();
		while (p)
		{
			msg.m_mapParams.GetNextAssoc(p,key,val);
			m_mapParams.SetAt(key,val);
		}

		return *this;
	}

	void SetParams(CMapStringToString& params)
	{
		CString key,val;
		POSITION p = params.GetStartPosition();
		while (p)
		{
			params.GetNextAssoc(p,key,val);
			m_mapParams.SetAt(key,val);
		}
	}

	void GetParams(CMapStringToString& params)
	{
		CString key,val;
		POSITION p = m_mapParams.GetStartPosition();
		while (p)
		{
			m_mapParams.GetNextAssoc(p,key,val);
			params.SetAt(key,val);
		}
	}

	int GetParamCount()
	{
		return m_mapParams.GetCount();
	}

	POSITION GetParamStartPosition()
	{
		return m_mapParams.GetStartPosition();
	}

	void GetNextParam(CString& key, CString& val, POSITION& pos)
	{
		m_mapParams.GetNextAssoc(pos,key,val);
	}

	CString GetSourceType() { return m_sSourceType; }
	CString GetSourceId()	{ return m_sSourceId; }
	CString GetAction()		{ return m_sAction; }

	void SetSourceType(LPCTSTR s){ m_sSourceType = s; }
	void SetSourceId(LPCTSTR s)	 { m_sSourceId   = s; }
	void SetAction(LPCTSTR s)	 { m_sAction     = s; }

	CString GetParam(int n)
	{
		if (n >= 0 && n < m_aParams.GetSize())
			return GetParam(m_aParams[n]);
		return CString();
	}

	CString GetParam(LPCTSTR param)
	{
		CString s;
		m_mapParams.Lookup(param, s);
		return s;
	}

	int		GetParamInt   (LPCTSTR param) { return atoi(GetParam(param)); }
	long	GetParamLong  (LPCTSTR param) { return atol(GetParam(param)); }
	double	GetParamDouble(LPCTSTR param) { return atof(GetParam(param)); }

	void RemoveParam(LPCTSTR param)
	{
		m_mapParams.RemoveKey(param);
	}

	void SetParam(LPCTSTR param, LPCTSTR value, int n = -1)
	{ 
		m_mapParams.SetAt(param, value); 
		if (n >= 0)
		{
			if (m_aParams.GetSize() <= n)
				m_aParams.SetSize(n+1);

			m_aParams[n] = param;
		}
	}

	void SetParamInt   (LPCTSTR param, int	  value, int n = -1) { SetParam(param, CStringNum(value), n); }
	void SetParamLong  (LPCTSTR param, long   value, int n = -1) { SetParam(param, CStringNum(value), n); }
	void SetParamDouble(LPCTSTR param, double value, int n = -1) { SetParam(param, CStringNum(value), n); }

	BOOL IsParamExist(LPCTSTR name)
	{
		CString param;
		return m_mapParams.Lookup(name,param);
	}

	BOOL Check(LPCTSTR type, LPCTSTR action)
	{ return m_sSourceType==type && m_sAction==action; }

	BOOL Check(LPCTSTR type, LPCTSTR id, LPCTSTR action)
	{ return m_sSourceType==type && m_sSourceId==id && m_sAction==action; }

	BOOL operator==(Msg& msg)
	{
		return MsgToString()==msg.MsgToString();
	}

	BOOL operator!=(Msg& msg)
	{
		return MsgToString()!=msg.MsgToString();
	}

	CString MsgToString()
	{
		CString s;
		s.Format("%s|%s|%s|%s",m_sSourceType,m_sSourceId,m_sAction,Debug_ParamsToString());
		return s;
	}

	void StringToMsg(LPCTSTR str)
	{
		LPCTSTR params;
		AfxExtractSubString(m_sSourceType,  str, 0, '|');
		AfxExtractSubString(m_sSourceId  ,  str, 1, '|');
		AfxExtractSubString(m_sAction    ,  str, 2, '|');
//		AfxExtractSubString(params       ,  str, 3, '|');
		params = str+strlen(m_sSourceType)+strlen(m_sSourceId)+strlen(m_sAction)+2;

		m_mapParams.RemoveAll();

		if (*params == '|' && *(params+1))
			StringToParams(params+1);
	}

	void StringToParams(LPCTSTR params)
	{
		CString s,s2;

		m_mapParams.RemoveAll();

		for (int i=0;AfxExtractSubString(s, params, i, ',');++i)		
		{
			int n = s.Find('<');
			while  (s.Find('>')<0 && AfxExtractSubString(s2, params, ++i, ',')) s += ","+s2;
			m_mapParams.SetAt(s.Left(n), s.Mid(n+1, s.GetLength()-n-2));
		}
	}

	CString Debug_ParamsToString()
	{
		CString res;
		POSITION p = m_mapParams.GetStartPosition();
		while (p)
		{
			CString key,val;
			m_mapParams.GetNextAssoc(p,key,val);

			if (!res.IsEmpty())
				 res += CString(",");

			res += key + "<" + val + ">";
		}
		return res;
	}

	virtual void Serialize(CArchive& ar);
};

class MsgFile : public CNetObject
{
	BOOL m_bOK;

	DECLARE_SERIAL(MsgFile)
public:
	MsgFile(LPCTSTR file_from = "", LPCTSTR file_to = "");

    virtual ~MsgFile() {}

	BOOL IsOK() { return m_bOK; }

	virtual void Serialize(CArchive& ar);

	CString  m_sFileName;
	CString  m_sFileNameSrc;
	FILETIME m_ftCreationTime;
	FILETIME m_ftLastAccessTime;
	FILETIME m_ftLastWriteTime;
	DWORD	 m_dwSize;
};

class React : public Msg
{
	DECLARE_SERIAL(React)
public:
	React(LPCTSTR source = "", LPCTSTR id = "", LPCTSTR action = "")  : Msg()
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;
	}
	React(LPCTSTR source, LPCTSTR id, LPCTSTR action, LPCTSTR params) : Msg()
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;

		StringToParams(params);
	}
	React(Msg& msg) : Msg(msg) {}

	Msg& operator=(Msg& msg) { return Msg::operator=(msg); }

    virtual ~React() {}
};

class Event : public Msg
{
	DECLARE_SERIAL(Event)
public:
	Event(LPCTSTR source, int id, LPCTSTR action) : Msg()
	{
		m_sSourceType	= source;
		m_sSourceId.Format("%d",id);
		m_sAction		= action;
	}
	Event(LPCTSTR source = "", LPCTSTR id = "", LPCTSTR action = "")  : Msg()
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;
	}
	Event(LPCTSTR source, LPCTSTR id, LPCTSTR action, LPCTSTR params) : Msg()
	{
		m_sSourceType	= source;
		m_sSourceId		= id;
		m_sAction		= action;

		StringToParams(params);
	}
	Event(Msg& msg) : Msg(msg) {}

	void ConvertReactToCoreEvent(React& react)
	{
		Create("CORE","","DO_REACT");
		SetParam("source_type",react.GetSourceType());
		SetParam("source_id",react.GetSourceId());
		SetParam("action",react.GetAction());

		int n=0;
		POSITION p = react.m_mapParams.GetStartPosition();
		while (p)
		{
			CString key,val;
			react.m_mapParams.GetNextAssoc(p,key,val);

			CString s; s.Format("%d",n++);
			SetParam("param"+s+"_name",key);
			SetParam("param"+s+"_val" ,val);
		}
		SetParamInt("params",n);
	}

	Msg& operator=(Msg& msg) { return Msg::operator=(msg); }

    virtual ~Event() {}
};

#endif