// ModalWindowTimer.cpp: implementation of the CModalWindowTimers class.
//
// Автор - Алексей Федоров aka АльФ (с) 2005-2007
// Доработка - Артур Аюханов aka artbear (c) 2007
//
//	работа с модальными окнами типа MessageBox - Предупреждение, Вопрос + таймауты на них
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
//#include "addin.h"
#include "ModalWindowTimer.h"
#include "MethodsWrapper.h"
#include "..\InitFormEx.h"
#include "Timer.h"

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

// работа с таймером для MessageBox
class CMessageBoxTimerStrategy
{
	UINT m_uIDEvent;
	int m_uFullTimer;
	BOOL m_bKilled;
	CDialog * m_pDlgToKill;
	CString m_strOriginCaption;
	BOOL m_bNeedOriginCaption;

public:
	typedef sh_ptr<CMessageBoxTimerStrategy> PMessageBoxTimerStrategy;

	CMessageBoxTimerStrategy(UINT time,class CDialog * pDlg);
	virtual void Exec(HWND hWnd, UINT nMsg, DWORD dwTime);

	bool IsKilled() const { return 0 != m_bKilled; }
private:
};

class CModalWindowTimersBaseImpl
{
public:
	virtual ~CModalWindowTimersBaseImpl() {};

	int SetMessageIcon(BOOL bSetFlag, UINT IconNumber, BOOL bFlagClearByDefValue);
	int SetMessageDefButton(BOOL bSetFlag, UINT ButtonNumber, BOOL bFlagClearByDefValue);
	CString SetMessageCaption(BOOL bSetFlag, const CString& strCaption, BOOL bFlagClearByDefValue);

	void SetAutoKillTimers(UINT iTimeout);
	void RestoreAutoKillTimers();

protected:
	CModalWindowTimersBaseImpl();

	int DoMessageBox( CBLModule7* pMod, LPCSTR lpcstr, UINT type, UINT timer);
	int DoMessageBox( CAppFrame* This, LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt);

	int SpecialShowMessageBox( bool bSimpleWindowMessageBox, UINT timer, LPCSTR lpcstr, const CString& strCaption, DWORD style );
	void AddDefButtonToBox(int UsersButtonKind,  DWORD& style );
	void AddIconToBox( int IconKind, DWORD& style );
	void AddButtonsToBox( UINT type, DWORD& style );

	int m_nAutoKillTimerInterval;
	int m_bEnableAutoKillAllWindowsTimer;
	int m_nAutoKillTimer_last_temp;

	bool m_bUsersIcon;
	bool m_bUsersButton;
	bool m_bUsersCaption;
	bool m_bUsersIconSetDef;
	bool m_bUsersButtonSetDef;
	bool m_bUsersCaptionSetDef;

	int m_nUsersIcon;
	int m_nUsersButton;
	CString m_szUsersCaption;
};

class CModalWindowTimersImpl : public CModalWindowTimersBaseImpl
{
public:
	typedef sh_ptr<CModalWindowTimersImpl> PCModalWindowTimersImpl;

	static PCModalWindowTimersImpl GetSingleton() 
	{ 
		if (pSingletone.isNull()) 
			pSingletone = PCModalWindowTimersImpl(new CModalWindowTimersImpl);
		return pSingletone; 
	}
	static void DestroySingletone(void) { pSingletone = PCModalWindowTimersImpl(NULL); }

	virtual ~CModalWindowTimersImpl();

private:
	CModalWindowTimersImpl();

	static int OwnRunDlgKiller(UINT time,class CDialog * pDlg);
	static int OwnStopDlgKiller(int result);

	void SetHooks(void);
	void DestroyHooks(void);

	typedef CTimerWithStrategy<CMessageBoxTimerStrategy, CTimerSetupStrategy> CMessageBoxTimerWithStrategy;
	sh_ptr<CMessageBoxTimerWithStrategy> TimerWithStrategy;
	CMessageBoxTimerStrategy::PMessageBoxTimerStrategy MessageBoxTimerStrategy;

	static PCModalWindowTimersImpl pSingletone;

 	int Wrap_CAppFrame_OnDoMessageBox(LPCSTR lpcstr, UINT type, UINT timer);
	//int Wrap_CBLModule_OnDoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt);
	int Wrap_CBLModule_OnDoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT timer);

	static int Wrap_RunDlgKiller(UINT time,class CDialog * pDlg);
	static int Wrap_StopDlgKiller(int result);

	typedef	int (CAppFrame::*CAppFrame_PF_OnDoMessageBox)(LPCSTR lpcstr, UINT type, UINT timer);
	CDllMethodWrapper<CAppFrame_PF_OnDoMessageBox> wrapper_CAppFrame_OnDoMessageBox;

// 	//typedef	int (CBLModule7::*CBLModule7_PF_OnDoMessageBox)(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt);
// 	typedef	int (CBLModule7::*CBLModule7_PF_OnDoMessageBox)(LPCTSTR lpszPrompt, UINT nType);
// 	CDllMethodWrapper<CBLModule7_PF_OnDoMessageBox> wrapper_CBLModule_DoMessageBox;
	// typedef int(CBLModule::*PF_ONDOMESSAGEBOX)(LPCSTR, UINT, UINT);
	typedef	int (CBLModule::*CBLModule_PF_OnDoMessageBox)(LPCTSTR lpszPrompt, UINT nType, UINT timer);
	CDllMethodWrapper<CBLModule_PF_OnDoMessageBox> wrapper_CBLModule_DoMessageBox;

	typedef	int (*PF_RunDlgKiller)(UINT time,class CDialog * pDlg);
	CDllMethodWrapper<PF_RunDlgKiller> wrapper_RunDlgKiller;

	typedef	int (*PF_StopDlgKiller)(int result);
	CDllMethodWrapper<PF_StopDlgKiller> wrapper_StopDlgKiller;
};

CModalWindowTimersImpl::PCModalWindowTimersImpl CModalWindowTimersImpl::pSingletone;

//////////////////////////////////////////////////////////////////////
// CMessageBoxTimerStrategy
//////////////////////////////////////////////////////////////////////

CMessageBoxTimerStrategy::CMessageBoxTimerStrategy(UINT time,class CDialog * pDlg):
	m_uFullTimer(time), m_pDlgToKill(pDlg), m_bKilled(FALSE), 
	m_strOriginCaption(), m_bNeedOriginCaption(TRUE)
{
};

void CMessageBoxTimerStrategy::Exec(HWND hWnd,UINT iMsg, DWORD dwTime)
{
	if(m_uFullTimer < 1)
	{
		m_bKilled = TRUE;
		if(m_pDlgToKill)
			m_pDlgToKill->EndDialog(0);
		else
		{
			HWND hDlg = GetActiveWindow();
			if(hDlg) EndDialog(hDlg, 0);
		}
	}
	else
	{
		if(m_pDlgToKill)
		{
			if(m_bNeedOriginCaption)
			{
				m_pDlgToKill->GetWindowText(m_strOriginCaption);
				m_bNeedOriginCaption = FALSE;
			}
			CString str = "";
			str.Format("[ %d ] %s", (m_uFullTimer), m_strOriginCaption);
			//str.Format("%s", m_strOriginCaption);
			m_pDlgToKill->SetWindowText(str);
		}
		else
		{
			HWND hDlg = GetActiveWindow();
			if(hDlg)
			{
				CWnd* pWnd = CWnd::FromHandle(hDlg);
				if(m_bNeedOriginCaption)
				{
					pWnd->GetWindowText(m_strOriginCaption);
					m_bNeedOriginCaption = FALSE;
				}
				CString str = "";
				str.Format("[ %d ] %s", (m_uFullTimer), m_strOriginCaption);
				//str.Format("%s", m_strOriginCaption);
				pWnd->SetWindowText(str);
			}
		}
		m_uFullTimer --;
	}
}

//////////////////////////////////////////////////////////////////////
// CModalWindowTimers
//////////////////////////////////////////////////////////////////////

CModalWindowTimers::CModalWindowTimers()
{
}

CModalWindowTimers::~CModalWindowTimers()
{
	DestroyHooks();
}

void CModalWindowTimers::SetHooks(void)
{
	pImpl = CModalWindowTimersImpl::GetSingleton();
}

void CModalWindowTimers::DestroyHooks(void)
{
	pImpl = CModalWindowTimersImpl::PCModalWindowTimersImpl(NULL);
	CModalWindowTimersImpl::DestroySingletone();
}

int CModalWindowTimers::SetMessageIcon(const CValue& ValueSetFlag, const CValue& ValueIconNumber, const CValue& ValueFlagClearByDefValue)
{
	return pImpl->SetMessageIcon(ValueSetFlag.GetNumeric(), ValueIconNumber.GetNumeric(), ValueFlagClearByDefValue.GetNumeric());
}

int CModalWindowTimers::SetMessageDefButton(const CValue& ValueSetFlag, const CValue& ValueButtonNumber, const CValue& ValueFlagClearByDefValue)
{
	return pImpl->SetMessageDefButton(ValueSetFlag.GetNumeric(), ValueButtonNumber.GetNumeric(), ValueFlagClearByDefValue.GetNumeric());
}

CString CModalWindowTimers::SetMessageCaption(const CValue& ValueSetFlag, const CValue& ValueCaption, const CValue& ValueFlagClearByDefValue)
{
	return pImpl->SetMessageCaption(ValueSetFlag.GetNumeric(), ValueCaption.GetString(), ValueFlagClearByDefValue.GetNumeric());
}

void CModalWindowTimers::SetAutoKillTimers(const CValue& ValueTimeout)
{
	pImpl->SetAutoKillTimers(ValueTimeout.GetNumeric());
}

void CModalWindowTimers::RestoreAutoKillTimers()
{
	pImpl->RestoreAutoKillTimers();
}

//////////////////////////////////////////////////////////////////////
// CModalWindowTimersBaseImpl
//////////////////////////////////////////////////////////////////////

CModalWindowTimersBaseImpl::CModalWindowTimersBaseImpl()
{
	m_nAutoKillTimerInterval = -1;
	m_bEnableAutoKillAllWindowsTimer = false;

	m_bUsersIcon = false;
	m_bUsersButton = false;
	m_bUsersCaption = false;
	m_bUsersIconSetDef = true;
	m_bUsersButtonSetDef = true;
	m_bUsersCaptionSetDef = true;
	
	m_nUsersIcon = 0;
	m_nUsersButton = 0;
	m_szUsersCaption = "";
}

int CModalWindowTimersBaseImpl::DoMessageBox( CBLModule7* pMod, LPCSTR lpcstr, UINT type, UINT timer )
{
	//SET_ORIGIN_FUNC(OnDoMessageBox)
	//SET_WRAP_FUNC(OnDoMessageBox)
	
	//CModalWindowTimersImpl* pImpl = GetSingleton();
	//CBLModule7* This = (CBLModule7*)this;
	if(((timer == 0) || (m_bEnableAutoKillAllWindowsTimer)) && (m_nAutoKillTimerInterval >= 0))
	{
		if(m_nAutoKillTimerInterval == 0)
			return 0;
		
		timer = m_nAutoKillTimerInterval;
	}

	DWORD style = 0;

	CValue valueMessageIcon;
	BOOL bVarIconExists = FALSE;
	if(pMod->FindStaticVar("FormEx_ИконкаПредупреждения") >= 0)
		bVarIconExists = pMod->EvalDebugExpr("FormEx_ИконкаПредупреждения", valueMessageIcon);
	if(m_bUsersIcon)
	{
		AddIconToBox(m_nUsersIcon, style);
	}
	else if(bVarIconExists)
	{
		AddIconToBox(valueMessageIcon.GetNumeric(), style);
	}

	bool bUser_icon = (style > 0);

	CValue valueMessageDefButton;
	BOOL bVarDefButtonExists = FALSE;
	if(pMod->FindStaticVar("FormEx_ОсновнаяКнопкаПредупреждения") >= 0)
		bVarDefButtonExists = pMod->EvalDebugExpr("FormEx_ОсновнаяКнопкаПредупреждения", valueMessageDefButton);
	if(m_bUsersButton)
	{
		AddDefButtonToBox(m_nUsersButton, style);
	}
	else if(bVarDefButtonExists)
	{
		AddDefButtonToBox(valueMessageDefButton.GetNumeric(), style);
	}
	
	if(bUser_icon)
	{
		AddButtonsToBox(type, style);
	}
	else
	{
		AddButtonsToBox(type, style);

		switch(type)
		{
			case 0:
				style |= MB_ICONASTERISK;
				break;
			case 1:
				style |= MB_ICONEXCLAMATION;
				break;
			case 2:
				style |= MB_ICONQUESTION;
				break;
			case 3:
				style |= MB_ICONQUESTION;
				break;
			case 4:
				style |= MB_ICONQUESTION;
				break;
			case 5:
				style |= MB_ICONQUESTION;
				break;
			default:
				style |= MB_ICONEXCLAMATION;
		}
	}
	
	int ret = 0;

	CValue valueMessageCaption;
	BOOL bVarCaptionExists = FALSE;
	if(pMod->FindStaticVar("FormEx_ЗаголовокПредупреждения") >= 0)
		bVarCaptionExists = pMod->EvalDebugExpr("FormEx_ЗаголовокПредупреждения", valueMessageCaption);
	if(m_bUsersCaption)
	{
		ret = SpecialShowMessageBox(true, timer, lpcstr, m_szUsersCaption, style);
	}
	else if(bVarCaptionExists)
	{
		ret = SpecialShowMessageBox(true, timer, lpcstr, valueMessageCaption.GetString(), style);
	}
	else
	{
		ret = SpecialShowMessageBox(false, timer, lpcstr, m_szUsersCaption, style);
	}

	if(m_bUsersIconSetDef)
		m_bUsersIcon = false;
	if(m_bUsersButtonSetDef)
		m_bUsersButton = false;
	if(m_bUsersCaptionSetDef)
		m_bUsersCaption = false;

	return ret;
}

int CModalWindowTimersBaseImpl::SpecialShowMessageBox( bool bSimpleWindowMessageBox, UINT timer, LPCSTR lpcstr, const CString& strCaption, DWORD style )
{
	int ret	= 0;
	if (bSimpleWindowMessageBox){
		CWnd* pWnd = GetMainFrame();
		HWND hWnd = NULL;
		if(pWnd)
			hWnd = pWnd->GetSafeHwnd();

		if(timer)
		{
			RunDlgKiller(timer, NULL);
			return StopDlgKiller(MessageBox(hWnd, lpcstr, strCaption, style));
		}
		else
			return MessageBox(hWnd, lpcstr, strCaption, style);
	}
	else
		if(timer)
		{
			RunDlgKiller(timer, NULL);
			return StopDlgKiller(AfxMessageBox(lpcstr, style, 0));
		}
		else
			return AfxMessageBox(lpcstr, style, 0);

	return ret;
}

void CModalWindowTimersBaseImpl::AddDefButtonToBox(int UsersButtonKind, DWORD& style )
{
	switch(UsersButtonKind)
	{
	case 1:
		style |= MB_DEFBUTTON1;
		break;
	case 2:
		style |= MB_DEFBUTTON2;
		break;
	case 3:
		style |= MB_DEFBUTTON3;
		break;
	case 4:
		style |= MB_DEFBUTTON4;
		break;
	}
}

void CModalWindowTimersBaseImpl::AddIconToBox( int IconKind, DWORD& style )
{
	switch(IconKind)
	{
	case 1:
		style = MB_ICONHAND;
		break;
	case 2:
		style = MB_ICONQUESTION;
		break;
	case 3:
		style = MB_ICONEXCLAMATION;
		break;
	case 4:
		style = MB_ICONASTERISK;
		break;
	default:
		style = 0;
	}
}

void CModalWindowTimersBaseImpl::AddButtonsToBox( UINT type, DWORD& style )
{
	switch(type)
	{
	case 1:
		style |= MB_OKCANCEL;
		break;
	case 2:
		style |= MB_ABORTRETRYIGNORE;
		break;
	case 3:
		style |= MB_YESNOCANCEL;
		break;
	case 4:
		style |= MB_YESNO;
		break;
	case 5:
		style |= MB_RETRYCANCEL;
		break;
	default:
		style |= MB_OK;
	}
}

int CModalWindowTimersBaseImpl::DoMessageBox( CAppFrame* pAppFrame, LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt )
{
// 	//SET_ORIGIN_FUNC(DoMessageBox)
// 	CDllMethodWrapperGuard<CBLModule7_PF_DoMessageBox> wrapper(wrapper_CBLModule_DoMessageBox);
// 
// 	CAppFrame* This = (CAppFrame*)this;
	int ret = MB_OK;
	UINT timer = 0;
	if(m_nAutoKillTimerInterval >= 0)
	{
		if(m_nAutoKillTimerInterval == 0)
			return MB_OK;
		
		timer = m_nAutoKillTimerInterval;
	}
	if(timer)
	{
		RunDlgKiller(timer, NULL);
		ret = StopDlgKiller(pAppFrame->DoMessageBox(lpszPrompt, nType, nIDPrompt));
	}
	else
		ret = pAppFrame->DoMessageBox(lpszPrompt, nType, nIDPrompt);

	//SET_WRAP_FUNC(DoMessageBox)

	return ret;
}

int CModalWindowTimersBaseImpl::SetMessageIcon( BOOL bSetFlag, UINT IconNumber, BOOL bFlagClearByDefValue )
{
	int ret = -1;

	if (m_bUsersIcon)
		ret = m_nUsersIcon;

	if(bSetFlag == 1L)
	{
		m_bUsersIcon = true;
		m_nUsersIcon = IconNumber;
		if(bFlagClearByDefValue != -1)
			m_bUsersIconSetDef = (bFlagClearByDefValue != 0);
	}
	
	return ret;
}

int CModalWindowTimersBaseImpl::SetMessageDefButton(BOOL bSetFlag, UINT ButtonNumber, BOOL bFlagClearByDefValue)
{
	int ret = -1;

	if (m_bUsersButton)
		ret = m_nUsersButton;

	if(bSetFlag == 1L)
	{
		m_bUsersButton = true;
		m_nUsersButton = ButtonNumber;
		if(bFlagClearByDefValue != -1)
			m_bUsersButtonSetDef = (bFlagClearByDefValue != 0);
	}

	return ret;
}

CString CModalWindowTimersBaseImpl::SetMessageCaption(BOOL bSetFlag, const CString& strCaption, BOOL bFlagClearByDefValue)
{
	CString ret = ""; // TODO в исходном коде зачем-то возвращается -1

	if (m_bUsersCaption)
		ret = m_szUsersCaption;

	if(bSetFlag == 1L)
	{
		m_bUsersCaption = true;
		m_szUsersCaption = strCaption;
		if(bFlagClearByDefValue != -1)
			m_bUsersCaptionSetDef = (bFlagClearByDefValue != 0);
	}
	
	return ret;
}

void CModalWindowTimersBaseImpl::SetAutoKillTimers(UINT iTimeout)
{
	m_nAutoKillTimer_last_temp = m_nAutoKillTimerInterval;
	m_bEnableAutoKillAllWindowsTimer = true;
	m_nAutoKillTimerInterval = iTimeout;
}

void CModalWindowTimersBaseImpl::RestoreAutoKillTimers()
{
	m_bEnableAutoKillAllWindowsTimer = false;
	m_nAutoKillTimerInterval = m_nAutoKillTimer_last_temp;
}

//////////////////////////////////////////////////////////////////////
// CModalWindowTimersImpl
//////////////////////////////////////////////////////////////////////

CModalWindowTimersImpl::CModalWindowTimersImpl()
{
	SetHooks();
}

CModalWindowTimersImpl::~CModalWindowTimersImpl()
{
	DestroyHooks();
}

void CModalWindowTimersImpl::SetHooks(void)
{
	HINSTANCE hBl	= GetModuleHandle("blang.dll");
	HINSTANCE hFrame= GetModuleHandle("frame.dll");
	HINSTANCE hBk	= GetModuleHandle("bkend.dll");
	HINSTANCE hSeven	= GetModuleHandle("seven.dll");

	wrapper_CAppFrame_OnDoMessageBox.DoWrap(hFrame, "?DoMessageBox@CAppFrame@@UAEHPBDII@Z", (CAppFrame_PF_OnDoMessageBox)Wrap_CAppFrame_OnDoMessageBox);
	//wrapper_CBLModule_DoMessageBox.DoWrap(hSeven, "?OnDoMessageBox@CBLModule7@@UAEHPBDI@Z", (CBLModule7_PF_OnDoMessageBox)Wrap_CBLModule7_OnDoMessageBox);
	wrapper_CBLModule_DoMessageBox.DoWrap(hBl, "?OnDoMessageBox@CBLModule@@UAEHPBDII@Z", (CBLModule_PF_OnDoMessageBox)Wrap_CBLModule_OnDoMessageBox);


	wrapper_RunDlgKiller.DoWrap(hBk, "?RunDlgKiller@@YAHIPAVCDialog@@@Z", (PF_RunDlgKiller)Wrap_RunDlgKiller);
	wrapper_StopDlgKiller.DoWrap(hBk, "?StopDlgKiller@@YAHH@Z", (PF_StopDlgKiller)Wrap_StopDlgKiller);
}

void CModalWindowTimersImpl::DestroyHooks(void)
{
	wrapper_CAppFrame_OnDoMessageBox.RestoreOrigAddress();
	wrapper_CBLModule_DoMessageBox.RestoreOrigAddress();

	wrapper_RunDlgKiller.RestoreOrigAddress();
	wrapper_StopDlgKiller.RestoreOrigAddress();
}

int CModalWindowTimersImpl::Wrap_CAppFrame_OnDoMessageBox( LPCSTR lpcstr, UINT type, UINT timer )
{
	PCModalWindowTimersImpl pImpl = GetSingleton();

	CDllMethodWrapperGuard<CAppFrame_PF_OnDoMessageBox> wrapper(pImpl->wrapper_CAppFrame_OnDoMessageBox);
	//CBLModule7* This = reinterpret_cast<CBLModule7*>(this);

	CAppFrame* This = reinterpret_cast<CAppFrame*>(this);

	return pImpl->DoMessageBox( This, lpcstr, type, timer);
}

int CModalWindowTimersImpl::Wrap_CBLModule_OnDoMessageBox( LPCTSTR lpszPrompt, UINT nType , UINT timer)
{
	PCModalWindowTimersImpl pImpl = GetSingleton();

	//SET_ORIGIN_FUNC(DoMessageBox)
	CDllMethodWrapperGuard<CBLModule_PF_OnDoMessageBox> wrapper(pImpl->wrapper_CBLModule_DoMessageBox);

	//CAppFrame* This = reinterpret_cast<CAppFrame*>(this);
	
	CBLModule7* This = reinterpret_cast<CBLModule7*>(this);

	//return pImpl->DoMessageBox(This, lpszPrompt, nType, nIDPrompt);
	return pImpl->DoMessageBox(This, lpszPrompt, nType, timer);
	//return pImpl->DoMessageBox(This, lpszPrompt, nType, m_nAutoKillTimerInterval);

	//SET_WRAP_FUNC(DoMessageBox)
}

int CModalWindowTimersImpl::Wrap_RunDlgKiller( UINT time,class CDialog * pDlg )
{
	PCModalWindowTimersImpl pImpl = GetSingleton();

	pImpl->MessageBoxTimerStrategy = CMessageBoxTimerStrategy::PMessageBoxTimerStrategy(new CMessageBoxTimerStrategy(time, pDlg));

	// TODO почему-то сначала вызывается создание нового таймера, а уж потом удаление старого :(, в итоге исключение
	// поэтому нужно вручную вызвать удаление, а потом уже создавать таймер с таким же ИД
	pImpl->TimerWithStrategy = sh_ptr<CMessageBoxTimerWithStrategy>(NULL);
	pImpl->TimerWithStrategy = CMessageBoxTimerWithStrategy::CreateTimer(*pImpl->MessageBoxTimerStrategy, 0, 1000);

 	return pImpl->TimerWithStrategy->GetSystemTimerID(); // TODO

}

int CModalWindowTimersImpl::Wrap_StopDlgKiller( int result )
{
	PCModalWindowTimersImpl pImpl = GetSingleton();

	pImpl->TimerWithStrategy->Stop();// = sh_ptr<CMessageBoxTimerWithStrategy>(NULL);

	if(pImpl->MessageBoxTimerStrategy->IsKilled()) return -1;

	return result;
}