// TimerContext.cpp: implementation of the CTimerContext class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TimerContext.h"
#include "Events.h"
#include "SafeContext.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

/////////////////////////////////////////////////////////////////////////////
// CBaseTimerForTimerContext
/////////////////////////////////////////////////////////////////////////////

class CBaseTimerForTimerContext : public CBaseTimerWithEvent
{
	friend class CTimerContext;

	typedef CBaseTimerWithEvent CBaseClass;
	typedef sh_ptr<CBaseTimerForTimerContext> PCTimer;
protected:
	CBaseTimerForTimerContext(CTimerContext* pOwner) : 
	  CBaseClass(GetUniqueID(), GetUniqueID()), m_pOwner(pOwner)
	  {}

	virtual void SendEvent() = 0;

	UINT GetUniqueID() const { return reinterpret_cast<UINT>(this); };

protected:
	CTimerContext* m_pOwner;
	void ClearOwnerTimerData() 
	{
		m_pOwner->ClearTimerData();
	}

};

class CNullTimer : public CBaseTimerForTimerContext
{
public:
	static void CreateStoppedTimer(PCTimer& pDestTimer, CTimerContext* pOwner)
	{
		pDestTimer = PCTimer(NULL);
		pDestTimer = PCTimer(new CNullTimer(pOwner));
	}

protected:
	CNullTimer(CTimerContext* pOwner) : 
	  CBaseTimerForTimerContext(pOwner)
	  {}

	  virtual void SendEvent() {};
};

class CContextHandlerTimer : public CBaseTimerForTimerContext
{
public:
	static void CreateStoppedTimer(PCTimer& pDestTimer, CTimerContext* pOwner, CBLContext* pCont, LPCSTR szMethodName)
	{
		pDestTimer = PCTimer(NULL);
		pDestTimer = PCTimer(new CContextHandlerTimer(pOwner, pCont, szMethodName));
	}
protected:
	CContextHandlerTimer(CTimerContext* pOwner, CBLContext* pCont, LPCSTR szMethodName) : 
		 CBaseTimerForTimerContext(pOwner), m_pCont(pCont), m_iMethodNum(-1)
	{
		ASSERT(pCont);

		m_iMethodNum = m_pCont->FindMethod(szMethodName);

		if (-1 == m_iMethodNum)
			RuntimeError("ВНИМАНИЕ: создание таймера - не найден метод <%s> переданного контекста", szMethodName);
		if (0 != m_pCont->GetNParams(m_iMethodNum))
			RuntimeError("ВНИМАНИЕ: создание таймера - у метода <%s> переданного контекста не должно быть параметров", szMethodName);
	}

	virtual void SendEvent()
	{
		if (-1 == m_iMethodNum)
			return;
		m_pCont->CallAsProc(m_iMethodNum, NULL);
	}

	CBLPtr<CBLContext> m_pCont;
	int m_iMethodNum;
};

class CModuleHandlerTimer : public CBaseTimerForTimerContext
{
protected:
	enum{ meOnEvent };
	
	CModuleHandlerTimer(CTimerContext* pOwner, CBLModule* pMod, LPCSTR szMethodName) : 
		 CBaseTimerForTimerContext(pOwner), m_ModuleEventManager(pMod), 
			m_strMethodName(szMethodName)
	{
		ASSERT(pMod);
	}

	virtual bool Init(void)
	{
		stEventData Events[]  =  
		{
			{meOnEvent, m_strMethodName, m_strMethodName, 0, ekOnlyGlobalModules, false, false, 0},
		};
		m_ModuleEventManager.AddEvent(Events[0]);
		if (!m_ModuleEventManager.IsHandlerExists(meOnEvent))
			return false;
		return true;
	}

	virtual void SendEvent()
	{
		if (!m_ModuleEventManager.IsHandlerExists(meOnEvent))
			return;
		m_ModuleEventManager.SendEvent(meOnEvent, NULL);
	}

	CModuleEventManagerWithVerifyParamsCount m_ModuleEventManager;
	CString m_strMethodName; // обязательно храним ссылку на строку
};

class CGlobalModuleHandlerTimer : public CModuleHandlerTimer
{
public:
	static void CreateStoppedTimer(PCTimer& pDestTimer, CTimerContext* pOwner, LPCSTR szMethodName)
	{
		pDestTimer = PCTimer(NULL);
		pDestTimer = PCTimer(new CGlobalModuleHandlerTimer(pOwner, szMethodName));
		reinterpret_cast<CGlobalModuleHandlerTimer*>(pDestTimer.get())->Init();
	}
protected:
	CGlobalModuleHandlerTimer(CTimerContext* pOwner, LPCSTR szMethodName) : 
		 CModuleHandlerTimer(pOwner, GetGlobalModule(), szMethodName)
	{}

	virtual bool Init(void)
	{
		if (!CModuleHandlerTimer::Init())
 			RuntimeError("ВНИМАНИЕ: создание таймера - не найден метод <%s> в глобальном модуле или у него количество параметров более нуля", LPCSTR(m_strMethodName));
		return true;
	}
};

class CFormModuleHandlerTimer : public CModuleHandlerTimer
{
public:
	static void CreateStoppedTimer(PCTimer& pDestTimer, CTimerContext* pOwner, CGroupContext* pCont, LPCSTR szMethodName)
	{
		pDestTimer = PCTimer(NULL);
		pDestTimer = PCTimer(new CFormModuleHandlerTimer(pOwner, pCont, szMethodName));
		reinterpret_cast<CFormModuleHandlerTimer*>(pDestTimer.get())->Init();
	}
protected:
	CFormModuleHandlerTimer(CTimerContext* pOwner, CGroupContext* pCont, LPCSTR szMethodName) : 
		 CModuleHandlerTimer(pOwner, GetModuleByContext(pCont), szMethodName), 
			m_pCont(pCont)
	{}

	virtual bool Init(void)
	{
		if (!CModuleHandlerTimer::Init())
 			RuntimeError("ВНИМАНИЕ: создание таймера - не найден метод <%s> в модуле формы или у него количество параметров более нуля", LPCSTR(m_strMethodName));
		return true;
	}

	virtual void SendEvent()
	{
		if (IfFormIsClosedThenClearTimerData())
			return;
		
		CModuleHandlerTimer::SendEvent();
	}

	bool IfFormIsClosedThenClearTimerData()
	{
		if (m_pCont)
			return false;

		m_ModuleEventManager.ClearEvents();
		ClearOwnerTimerData();
		
		return true;
	}

	CSafeContextPtr<CGroupContext> m_pCont;
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
BL_INIT_CONTEXT(CTimerContext);

CTimerContext::CTimerContext() : m_Interval(0), m_IsStarted(FALSE), m_kind(tkNone)
{
	CNullTimer::CreateStoppedTimer(m_pTimer, this);
}

CTimerContext::~CTimerContext()
{
	//Msg("CTimerContext::~CTimerContext");
	//m_pTimer = CNullTimer::PCTimer(NULL);
}

void CTimerContext::VerifyTimer() const
{
	if (m_pTimer.isNull())
		RuntimeError("Таймер не создан или создан с ошибкой!");
}

int CTimerContext::Debug_RunTimerEvent(CValue** ppParams)
{
	//if (m_IsStarted && m_Interval && m_pTimer.isValid())
	if (m_IsStarted && m_Interval)
		m_pTimer->SendEvent();
	
	return TRUE;
}

void CTimerContext::SetNewTimerKind(const TimerKind newTimerKind)
{
	if (tkNone != m_kind)
 		if (m_IsStarted && (newTimerKind != m_kind))
 			RuntimeError("У запущенного таймера нельзя менять обработчик на обработчик другого типа");

	m_kind = newTimerKind;
}

int CTimerContext::Start(CValue** ppParams)
{
	if (m_IsStarted)
		RuntimeError("Таймер уже запущен, второй раз его нельзя запускать");
// 	if (tkNone == m_kind)
// 		; // TODO //RuntimeError("У таймера не задан обработчик");
	VerifyTimer();

	m_Interval = ppParams[0]->GetNumeric();

	m_pTimer->Start(m_Interval);
	m_IsStarted = TRUE;
	
	return TRUE;
}

int CTimerContext::Stop(CValue** ppParams)
{
	VerifyTimer();

	m_pTimer->Stop();
	m_IsStarted = FALSE;
	return TRUE;
}

void CTimerContext::ClearTimerData()
{
	CNullTimer::CreateStoppedTimer(m_pTimer, this);
	m_IsStarted = 0;
	m_Interval = 0;
}

int CTimerContext::DeleteHandler(CValue** ppParams)
{
	CNullTimer::CreateStoppedTimer(m_pTimer, this);
	m_kind = tkNone;
	return TRUE;
}

int CTimerContext::SetContextHandler(CValue** ppParams)
{
	ValidateParamContext(*ppParams[0], "В метод ЗадатьОбработчикВОбъекте необходимо передать объект, созданный через СоздатьОбъект");
	ValidateParamNotEmptyString(*ppParams[1], "В метод ЗадатьОбработчикВОбъекте необходимо передать наименование метода");
	
	CBLContext* pCont = ppParams[0]->GetContext();
	CString strMethodName = ppParams[1]->GetString();

	SetNewTimerKind(tkContext);
	CContextHandlerTimer::CreateStoppedTimer(m_pTimer, this, pCont, strMethodName);

	return TRUE;
}

int CTimerContext::SetGlobalModuleHandler(CValue** ppParams)
{
	ValidateParamNotEmptyString(*ppParams[0], "В метод ЗадатьОбработчикВГлобальномМодуле необходимо передать наименование метода");
	
	CString strMethodName = ppParams[0]->GetString();

	SetNewTimerKind(tkGlobalModule);
	CGlobalModuleHandlerTimer::CreateStoppedTimer(m_pTimer, this, strMethodName);

	return TRUE;
}

int CTimerContext::SetFormModuleHandler(CValue** ppParams)
{
	ValidateParamGroupContext(*ppParams[0], "В метод ЗадатьОбработчикВГлобальномМодуле необходимо передать контекст формы");
	ValidateParamNotEmptyString(*ppParams[1], "В метод ЗадатьОбработчикВГлобальномМодуле необходимо передать наименование метода");
	
	CGroupContext* pCont = static_cast<CGroupContext*>(ppParams[0]->GetContext());
	CString strMethodName = ppParams[1]->GetString();

	SetNewTimerKind(tkGroupContext);
	CFormModuleHandlerTimer::CreateStoppedTimer(m_pTimer, this, pCont, strMethodName);

	return TRUE;
}
