//////////////////////////////////////////////////////////////////////
//
//	System.cpp
//	Implementation of CSystem class
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "System.h"
#include "..\VTExt\VTExtended.h"
#include "GlobalMethods.h"
#include "..\BLModuleWrapper.h"

//#define __CSystem_ENABLE_MSG
//_____________________________________________________________________________
//
// CSystem Maps
//_____________________________________________________________________________
//
BEGIN_BL_METH_MAP(CSystem)

    BL_METH("MemInfo",			"Память",					2, NULL,		funcMemInfo,			defsMemInfo)
    BL_METH("Priority",			"Приоритет",				1, NULL,		funcPriority,			NULL)
    BL_METH("Purify",			"Purify",					1, NULL,		funcPurify,				NULL)
    BL_METH("Test",				"Тест",						2, NULL,		funcTest,				NULL)

    BL_METH("EnableNewLine",	"РазрешитьДобавлениеСтрок",	1, NULL,		funcEnableNewLine,		NULL)

    BL_METH("StartBatch",		"StartBatch",				1, NULL,		funcStartBatch,			NULL)
    BL_METH("EndBatch",			"EndBatch",					1, NULL,		funcEndBatch,			NULL)

    BL_METH("EnableActivation",	"РазрешитьАктивацию",		0, NULL,		funcEnableActivation,	NULL)
    BL_METH("Activate",			"Активизировать",			1, NULL,		funcActivate,			NULL)

    BL_METH("CommandLine",		"КоманднаяСтрока",		0, NULL,			funcCommandLine,		NULL)
    BL_METH("GetAppPath",		"ПутьПриложения1С",		0, NULL,			funcGetAppPath,		NULL)

    BL_FUNC_DEF_PARAM("CloseForm",		"ЗакрытьФорму",		2, funcCloseForm, defsCloseForm)

END_BL_METH_MAP()

BEGIN_BL_PROP_MAP(CSystem)
//    BL_PROP("propEng", "propRus", propGet, propSet)
END_BL_PROP_MAP()

IMPLEMENT_MY_CONTEXT(CSystem, "System", "Система", 1, NULL, NULL, -1);
//______________________________________________________________________________
//
// static members
//______________________________________________________________________________
//
DWORD CSystem::dwPriorityClasses[] =
{
	IDLE_PRIORITY_CLASS
,	BELOW_NORMAL_PRIORITY_CLASS
,	NORMAL_PRIORITY_CLASS
,	ABOVE_NORMAL_PRIORITY_CLASS
,	HIGH_PRIORITY_CLASS
,	REALTIME_PRIORITY_CLASS
};
//______________________________________________________________________________
//
// static members для механизма активации
//______________________________________________________________________________
//
CDllMethodWrapper<CSystem::PF_CGetDoc7_OnEnter> CSystem::w_CGetDoc7_OnEnter;
CDllMethodWrapper<CSystem::PF_CDocForm_OnEnter> CSystem::w_CDocForm_OnEnter;
CDllMethodWrapper<CSystem::PF_CEditGet_Activate> CSystem::w_CEditGet_Activate;

CString CSystem::m_strActivationRequested;
BOOL CSystem::m_bActivationRequested;
//______________________________________________________________________________
//
LONG CSystem::crtwalk(bool bShowMsg)
{
	_HEAPINFO hinfo;
	int heapstatus;
	char *buf;
	DWORD dwUsed = 0;
	DWORD dwFree = 0;
	int nUsed = 0, nFree = 0;

	hinfo._pentry = NULL;
	while( (heapstatus = _heapwalk( &hinfo )) == _HEAPOK )
	{
		if  ( hinfo._useflag == _USEDENTRY )
		{
			dwUsed += hinfo._size;
			nUsed++;
		}
		else
		{
			dwFree += hinfo._size;
			nFree++;
		}
	}
	switch( heapstatus )
	{
	case _HEAPEMPTY:
		buf = "OK - empty heap";
		break;
	case _HEAPEND:
		buf = "OK";
		break;
	case _HEAPBADPTR:
		buf = "ERROR - bad pointer to heap";
		break;
	case _HEAPBADBEGIN:
		buf = "ERROR - bad start of heap";
		break;
	case _HEAPBADNODE:
		buf = "ERROR - bad node in heap";
		break;
	}

	if( bShowMsg )
	{
		Msg("Heap( ----- CRT ----- ): Total: %u ( %u block(s) ); Used: %u ( %u block(s) ); Free: %u; Status: %s;",
			dwUsed + dwFree, nUsed + nFree, dwUsed, nUsed, dwFree, buf);
	}

	return dwUsed + dwFree;
}
//______________________________________________________________________________
//
LONG CSystem::heapwalk(HANDLE hHeap, bool bShowMsg)
{
	PROCESS_HEAP_ENTRY heapEntry;
	LONG nTotal = 0;
	DWORD dwOverhead = 0;
	DWORD dwCommitted = 0;
	DWORD dwUnCommitted = 0;
	int nEntries = 0;

	if (hHeap)
	{
		heapEntry.lpData = NULL;
		heapEntry.wFlags = PROCESS_HEAP_REGION;

		while ( HeapWalk( hHeap, &heapEntry ) )
		{
			if ( heapEntry.wFlags & PROCESS_HEAP_REGION )
			{
				dwCommitted += heapEntry.Region.dwCommittedSize;
				dwUnCommitted += heapEntry.Region.dwUnCommittedSize;
			}
			if ( heapEntry.wFlags & PROCESS_HEAP_ENTRY_BUSY )
			{
				nTotal += heapEntry.cbData;
				dwOverhead += heapEntry.cbOverhead;
				nEntries++;
			}
		}
		if( bShowMsg )
			Msg( "Heap( 0x%08x ): Used: %u ( %u block(s) ); Overhead: %u; Committed: %u; UnCommitted: %u",
			hHeap, nTotal, nEntries, dwOverhead, dwCommitted, dwUnCommitted );
	}

	return nTotal;
}
//______________________________________________________________________________
//
BOOL CSystem::defsMemInfo(int nParam, CValue* param) const
{
	switch( nParam )
	{
	case 1: *param = -1L; break;
	}
	return TRUE;
}

//______________________________________________________________________________
//
// funcMemInfo(флПоказыватьСообщения, битЗатребованнаяИнформация)
// флПоказыватьСообщения == [1 / 0]
// битЗатребованнаяИнформация == [бит 0: CRT Heap; бит 1: VTExtended Heap]
//______________________________________________________________________________
//
BOOL CSystem::funcMemInfo(CValue& RetVal, CValue **params)
{
	bool bShowMsg = ( (int)params[0]->GetNumeric() != 0 );
	int nRequestedInfo = (int)params[1]->GetNumeric();
	LONG nTotal = 0;
	HANDLE hHeap;

	// CRT
	if (nRequestedInfo & 1)
	{
		HMODULE hMod = GetModuleHandle("msvcrt.dll");
		FARPROC _get_heap_handle_explicit = GetProcAddress(hMod, "_get_heap_handle");
		if (_get_heap_handle_explicit)
		{
			// via heapwalk
//			hHeap = reinterpret_cast<HANDLE>(_get_heap_handle());
			hHeap = reinterpret_cast<HANDLE>(_get_heap_handle_explicit());
			nTotal += heapwalk(hHeap, bShowMsg);
		}
		else
		{
			// via crtwalk
			nTotal += crtwalk(bShowMsg);
		}
	}
	// CVTExtHeap
	if (nRequestedInfo & 2)
	{
		hHeap = CVTExtHeap::Get();
		nTotal += heapwalk(hHeap, bShowMsg);
	}

	RetVal = nTotal;
	return TRUE;
}
//______________________________________________________________________________
//
// параметры:
//	0: число от 0 до 4: установить приоритет; не число - получить приоритет
//______________________________________________________________________________
//
BOOL CSystem::funcPriority(CValue& RetVal, CValue **params)
{

	RetVal = -1;

	if (params[0]->GetTypeCode() == NUMBER_TYPE_1C)
	{
		// установка класса приоритета
		UINT nPriority = (UINT)params[0]->GetNumeric().operator long();
		if (nPriority < nPriorityClassCnt)
		{
			if (SetPriorityClass(GetCurrentProcess(), dwPriorityClasses[nPriority]))
				RetVal = 1L;
		} else
		{
			RuntimeError("Неверный приоритет!");
		}
	}
	else
	{
		// получение класса приоритета
		DWORD dwPriority = GetPriorityClass(GetCurrentProcess());
		if (dwPriority)
			for (int i = 0; i < nPriorityClassCnt; ++i)
				if (dwPriorityClasses[i] == dwPriority)
				{
					RetVal = static_cast<long>(i);
					break;
				}
	}

	return TRUE;
}
//______________________________________________________________________________
//
BOOL CSystem::funcPurify(CValue& RetVal, CValue **params)
{
#ifdef _DEBUG_PURE

	int param = (int) params[0]->GetNumeric();
	switch (param)
	{
	case 1:
		PurifyNewLeaks();
		break;
	case 2:
		PurifyNewInuse();
		break;
	case 3:
		PurifyAllLeaks();
		break;
	case 4:
		PurifyAllInuse();
		break;
	default:
		Msg( "Incorrect param" );
	}

#endif // _DEBUG_PURE

	RetVal =0L;
	return TRUE;
}
//______________________________________________________________________________
//
__declspec(naked) __inline int IsNotPowerOf2(int num)
{
	_asm
	{
		mov EAX, [ESP][4];
		test EAX, EAX;
		jz l_ret;
l_shr:
		shr EAX, 1;
		jnc l_shr;
l_ret:
		ret;
	}
}
//______________________________________________________________________________
//
BOOL CSystem::funcTest(CValue& RetVal, CValue **params)
{
	int lim = 100000;

	CIStringMapToInt map;
	char buf[100];
	int i;

	UINT64 t1, t2;
	CTSC tsc;

	// тест istrmap
	tsc.Start();
	for (i = 0; i <lim; ++i)
	{
		_itoa(i, buf, 10);
		map[buf] = i;
	}

//	crtwalk(true);

	int useful = 0;
	for (i = 0; i <lim; ++i)
	{
		_itoa(i, buf, 10);
		useful += map[buf];
//		map.RemoveKey(buf);
	}

	tsc.Stop();
	tsc.Report("istrmap");
	t1 = tsc.Get();

//	map.GetCollisionCount(true);

	// тест blmap
	CBLMap blmap;
	tsc.Start();
	for (i = 0; i <lim; ++i)
	{
		_itoa(i, buf, 10);
		blmap.InsertKey(buf, i);
	}

	useful = 0;
	for (i = 0; i <lim; ++i)
	{
		_itoa(i, buf, 10);
		useful += blmap.GetKey(buf);
	}
	blmap.RemoveAll();

	tsc.Stop();
	tsc.Report("blmap.");
	t2 = tsc.Get();

	Msg("blmap/istrmap TSC ratio: %i", t2 /t1);

	lim = 1000000;

	// тест коллизий istrmap
	map.RemoveAll();
	for (i = 0; i <lim; ++i)
	{
		if ( !IsNotPowerOf2(i) )
		{
			map.GetCollisionCount(true);
			_itoa(i, buf, 10);
			map[buf] = i;
			map.GetCollisionCount(true);
			++i;
		}
		_itoa(i, buf, 10);
		map[buf] = i;
	}
	map.GetCollisionCount(true);
	map.RemoveAll();

	return TRUE;
}
//______________________________________________________________________________
//
// разрешение добавления строк для контекста документа
// (необходимо для обратных вызовов функций формы документа, модифицирующих мнч)

// параметры:
//	0: контекст документа

// возврат:
//	1: добавление строк разрешено
//	0: контекст документа не найден
//______________________________________________________________________________
//
BOOL CSystem::funcEnableNewLine(CValue& RetVal, CValue **params)
{
	int nCount = 0;

	CGroupContext *pGroupCont = (CGroupContext *)params[0]->GetContext();
	if(pGroupCont)
	{
		CBLContext *pCont;
		for(int i = 0; i < pGroupCont->GetSize(); ++i)
		{
			pCont = pGroupCont->GetContext(i);

			LPCSTR pName = pCont->GetRuntimeClass()->m_lpszClassName;

#if defined __CSystem_ENABLE_MSG
			Msg("%s", pName);
#endif

			if(!strcmp(pName, "CDocContext"))
			{
				((CDocContext*)pCont)->SetEnableNewLine(1);
				++nCount;
			}
		}
	}

	RetVal = (nCount) ? 1L : 0L;
	return TRUE;
}
//______________________________________________________________________________
//
// техническая функция управления OnStartBatch()/OnEndBatch()

// параметры:
//	CValue *контекст;
//	тип обработки: int, 1 = OnStartBatch; 0 = OnEndBatch;
//______________________________________________________________________________
//
BOOL process_batch(CValue *cv, int nFunction)
{
	BOOL bProcessed = FALSE;

	CGroupContext *pGroupCont = (CGroupContext *)cv->GetContext();
	if(pGroupCont)
	{
		CBLContext *pCont;
		for(int i = 0; i < pGroupCont->GetSize(); ++i)
		{
			pCont = pGroupCont->GetContext(i);

			LPCSTR pName = pCont->GetRuntimeClass()->m_lpszClassName;

#if defined __CSystem_ENABLE_MSG
			Msg("%s", pName);
#endif

			if(!strcmp(pName, "CFormContext"))
			{
				CFormContext *pFormCont = (CFormContext*)pCont;
				CGetDoc7 *pDoc7 = pFormCont -> m_pDocFC;

				if (nFunction)
				{

#if defined __CSystem_ENABLE_MSG
					Msg("StartBatch");
#endif

					pDoc7->OnStartBatch();
				}
				else
				{

#if defined __CSystem_ENABLE_MSG
					Msg("EndBatch");
#endif

					pDoc7->OnEndBatch(0);
				}
				bProcessed = TRUE;
			}
		}
	}
	return bProcessed;
}
//______________________________________________________________________________
//
// техническая функция

// параметры:
//	0: контекст формы

// возврат:
//	1: выполнено успешно
//	0: контекст формы не найден
//______________________________________________________________________________
//
BOOL CSystem::funcStartBatch(CValue& RetVal, CValue **params)
{
	BOOL bProcessed = process_batch(params[0], 1);

	RetVal = (bProcessed) ? 1L : 0L;
	return TRUE;
}
//______________________________________________________________________________
//
// техническая функция

// параметры:
//	0: контекст формы

// возврат:
//	1: выполнено успешно
//	0: контекст формы не найден
//______________________________________________________________________________
//
BOOL CSystem::funcEndBatch(CValue& RetVal, CValue **params)
{
	BOOL bProcessed = process_batch(params[0], 0);

	RetVal = (bProcessed) ? 1L : 0L;
	return TRUE;
}
//______________________________________________________________________________
//
// установка запрошенного к активации элемента диалога
//______________________________________________________________________________
//
BOOL CSystem::funcActivate(CValue& RetVal, CValue **params)
{
	// запомним значение
	m_strActivationRequested = params[0]->GetString();
	m_bActivationRequested = FALSE;

	// проверка параметров
	CBLModule * pMod = CBLModule::GetExecutedModule();
	CGroupContext* pGroupCont = (CGroupContext *)CBLModuleWrapper::GetContextFromModule(pMod);
	if (pGroupCont)
	{
		CBLContext *pCont;
		for(int i = 0; i < pGroupCont->GetSize(); ++i)
		{
			pCont = pGroupCont->GetContext(i);
			CGetDoc7 *pDoc7;

			LPCSTR pName = pCont->GetRuntimeClass()->m_lpszClassName;

#if defined __CSystem_ENABLE_MSG
			Msg("%s", pName);
#endif
			// отчеты и справочники
			if(!strcmp(pName, "CFormContext"))
			{
				CFormContext *pFormCont = (CFormContext*)pCont;
				pDoc7 = pFormCont->m_pDocFC;
			}
			// документы
			else if (!strcmp(pName, "CFormContextForDoc"))
			{
				CFormContextForDoc *pFormContForDoc = (CFormContextForDoc*)pCont;
				pDoc7 = pFormContForDoc->m_pDocFC;
			}
			else
				continue;

#if defined __CSystem_ENABLE_MSG
			pName = pDoc7->GetRuntimeClass()->m_lpszClassName;
			Msg("pDoc7 class: %s", pName);
#endif

			CGetFieldsArray *pGetFieldsArray = &pDoc7->m_cGetFieldsArray1;

			LPCSTR lpsz = m_strActivationRequested.operator LPCSTR();

			int num;
			for(num = 0; num < pGetFieldsArray->GetSize(); num++)
			{
				CGetField *pGetField = pGetFieldsArray->GetAt(num);
				LPCSTR pCodeName = (LPCSTR)pGetField->GetCtrlInfo()->GetCodeName();
				if(!stricmp(lpsz, pCodeName))
				{
					m_bActivationRequested = TRUE;
					break;
				}
			}
			if (!m_bActivationRequested)
				RuntimeError("Неверное имя атрибута: %s", m_strActivationRequested);
		}
	}

	RetVal = 1L;
	return TRUE;
}
//______________________________________________________________________________
//
// установить перехваты для механизмов активации
//
// возврат:
//	1: активация установлена
//	0: активация уже установлена ранее
//______________________________________________________________________________
//
BOOL CSystem::funcEnableActivation(CValue& RetVal, CValue **params)
{
	if (w_CGetDoc7_OnEnter.IsWrapped() ||
		w_CDocForm_OnEnter.IsWrapped() ||
		w_CEditGet_Activate.IsWrapped())
		RetVal = 0L;

	else
	{
		// установка перехватов
		HINSTANCE hm;

		hm = GetModuleHandle("basic.dll");
		w_CGetDoc7_OnEnter.DoWrap(hm, "?OnEnter@CGetDoc7@@IAEXXZ", (PF_CGetDoc7_OnEnter)Wrap_CGetDoc7_OnEnter);
		w_CDocForm_OnEnter.DoWrap(hm, "?OnEnter@CDocForm@@IAEXXZ", (PF_CDocForm_OnEnter)Wrap_CDocForm_OnEnter);

		hm = GetModuleHandle("br32.dll");
		w_CEditGet_Activate.DoWrap(hm, "?Activate@CEditGet@@UAEHXZ", (PF_CEditGet_Activate)Wrap_CEditGet_Activate);

		RetVal = 1L;
	}
	return TRUE;
}
//______________________________________________________________________________
//
// часть механизма активации из формул элементов диалога
//______________________________________________________________________________
//
int CSystem::Wrap_CEditGet_Activate()
{
	int ret;

	// запрещаем только в блоке OnEnter в случае, если запрошена особая активация
	// иначе работает штатный механизм
	if (m_bActivationRequested &&
		(w_CGetDoc7_OnEnter.IsActive() || w_CDocForm_OnEnter.IsActive()))
	{

#if defined __CSystem_ENABLE_MSG
		Msg("CEditGet::Activate: Skip");
#endif

		ret = 1;
	}
	else
	{

#if defined __CSystem_ENABLE_MSG
		Msg("CEditGet::Activate");
#endif

		w_CEditGet_Activate.RestoreOrigAddress();
		ret = (((CEditGet *)this->*w_CEditGet_Activate.GetOrigMethod()))();
		w_CEditGet_Activate.RestoreWrapAddress();
	}

	return ret;
}
//______________________________________________________________________________
//
// часть механизма активации из формул элементов диалога
//______________________________________________________________________________
//
void CSystem::Wrap_CGetDoc7_OnEnter()
{

#if defined __CSystem_ENABLE_MSG
	 Msg("CGetDoc7::OnEnter enter");
#endif

	// начнем с нуля
	m_bActivationRequested = FALSE;

	w_CGetDoc7_OnEnter.RestoreOrigAddress();
	(((CGetDoc7 *)this->*w_CGetDoc7_OnEnter.GetOrigMethod()))();
	w_CGetDoc7_OnEnter.RestoreWrapAddress();

#if defined __CSystem_ENABLE_MSG
	Msg("CGetDoc7::OnEnter leave");
#endif

	if (m_bActivationRequested)
	{
		CGetDoc7 *pDoc7 = (CGetDoc7 *)this;
		pDoc7->TrySetFocusOnCtrl(m_strActivationRequested, 1);
	}
}
//______________________________________________________________________________
//
// часть механизма активации из формул элементов диалога
//______________________________________________________________________________
//
void CSystem::Wrap_CDocForm_OnEnter()
{

#if defined __CSystem_ENABLE_MSG
	Msg("CDocForm::OnEnter enter");
#endif

	// начнем с нуля
	m_bActivationRequested = FALSE;

	w_CDocForm_OnEnter.RestoreOrigAddress();
	(((CDocForm *)this->*w_CDocForm_OnEnter.GetOrigMethod()))();
	w_CDocForm_OnEnter.RestoreWrapAddress();

#if defined __CSystem_ENABLE_MSG
	Msg("CDocForm::OnEnter leave");
#endif

	if (m_bActivationRequested)
	{
		CGetDoc7 *pDoc7 = (CGetDoc7 *)this;
		pDoc7->TrySetFocusOnCtrl(m_strActivationRequested, 1);
	}
}

// получить командную строку 1С (взято из FormEx)
BOOL  CSystem::funcCommandLine(CValue& RetVal, CValue **params)
{
	CApp7* pApp7 = (CApp7*) AfxGetApp();
	RetVal = pApp7->m_lpCmdLine;

			/*
			CApp7* pApp7 = (CApp7*) AfxGetApp();
			CProfile7* pProfile7 = pApp7->GetProps();

			CString Prop0 = pProfile7->GetStringProp(0x00); // каталог, в который установлен движок
			CString Prop1 = pProfile7->GetStringProp(0x01); // каталог базы
			CString Prop2 = pProfile7->GetStringProp(0x02); // каталог пользователя
			CString Prop3 = pProfile7->GetStringProp(0x03); // временный каталог
			CString Prop4 = pProfile7->GetStringProp(0x04); // полный путь к файлу 1Cv7.CFG (с именем файла)
			CString Prop5 = pProfile7->GetStringProp(0x05); // полный путь к файлу 1Cv7.DD (с именем файла)
			CString Prop6 = pProfile7->GetStringProp(0x06); // полный путь к файлу 1Cv7.DDS (с именем файла)
			CString Prop7 = pProfile7->GetStringProp(0x07); // полный путь к файлу 1Cv7.DBA (с именем файла)
			CString Prop8 = pProfile7->GetStringProp(0x08); // полный путь к файлу 1Cv7.MD (с именем файла)
			CString Prop9 = pProfile7->GetStringProp(0x09); // полный путь к файлу 1Cv7.LCK (с именем файла)
			CString PropA = pProfile7->GetStringProp(0x0A); // ???
			CString PropB = pProfile7->GetStringProp(0x0B); // ???
			CString PropC = pProfile7->GetStringProp(0x0C); // имя текущей ИБ в списке баз
			CString PropD = pProfile7->GetStringProp(0x0D); // имя текущего пользователя
			CString PropE = pProfile7->GetStringProp(0x0E); // полное имя текущего пользователя
			CString PropF = pProfile7->GetStringProp(0xF); // ???
			CString Prop10 = pProfile7->GetStringProp(0x10); // название набора прав текущего пользователя
			CString Prop11 = pProfile7->GetStringProp(0x11); // название интерфейса текущего пользователя
			CString Prop12 = pProfile7->GetStringProp(0x12); // путь в реестре к установкам текущего пользователя (относительно HKEY_CURRENT_USER\Software\1C\1Cv7\7.7)
			*/
/*

		LPCTSTR m_pszAppName = pApp7->m_pszAppName;  // human readable name
									//  (from constructor or AFX_IDS_APP_TITLE)
		LPCTSTR m_pszExeName = pApp7->m_pszExeName;       // executable name (no spaces)
		LPCTSTR m_pszHelpFilePath = pApp7->m_pszHelpFilePath;  // default based on module path
		LPCTSTR m_pszProfileName = pApp7->m_pszProfileName;   // default based on app name

		m_pszAppName <1С:Предприятие>
		m_pszExeName <1CV7>
		m_pszHelpFilePath <C:\Program Files\1Cv77\Bin25\BIN\1CV7.HLP>
		m_pszProfileName <1Cv7\7.7\Testing 1C++\V7\Default>
		sAppPath <C:\Program Files\1Cv77\Bin25\BIN\1CV7.exe>
*/

	return true;
}

BOOL  CSystem::funcGetAppPath(CValue& RetVal, CValue **params)
{
	CApp7* pApp7 = (CApp7*) AfxGetApp();
	CProfile7* pProfile7 = pApp7->GetProps();
	CString sAppPath = pProfile7->GetStringProp(0x00) + pApp7->m_pszExeName + ".exe";

	RetVal = sAppPath;
	return true;
}

void CSystem::ValidateParamGroupContext(const CValue& GroupContValue) const
{
	CBLContext* pGroupCont = GroupContValue.GetContext();

	if (!IS_KINDOF_CGroupContext(pGroupCont))
		RuntimeError(CString("Класс <")+GetTypeString()+"> - должен быть передан групповой контекст");
}

void CSystem::ValidateParamNumeric(const CValue& NumericValue) const
{
	if (1 != NumericValue.GetTypeCode())
		RuntimeError(CString("Класс <")+GetTypeString()+"> - второй параметр должен быть числом");
}

CFormAllCtrlsContext* GetFormContext(const CBLContext* pCont)
{
	if (!pCont)
		return NULL;

	CBLContext* pCtxForm = NULL;//TContPtr pCtxForm = NULL;
	if(IS_KINDOF_RUNTIME_CLASS(pCont, CGroupContext))
	{
		int nForm=pCont->FindProp("Форма");
		if(nForm>=0)
		{
			CValue vForm;
			pCont->GetPropVal(nForm, vForm);
			pCtxForm=vForm.GetContext();
		}
	}
	else
	{
// 		pCtxForm = const_cast<TContPtr>(pCont);
		pCtxForm = const_cast<CBLContext*>(pCont);
	}

	if(pCtxForm && !strcmp(pCtxForm->GetRuntimeClass()->m_lpszClassName, "CFormAllCtrlsContext"))
	{
		return static_cast<CFormAllCtrlsContext*>(pCtxForm);
	}

	return NULL;
}

CGetDoc7* GetDoc7(const CBLContext* pCont)
{
	CFormAllCtrlsContext* pFormContext = GetFormContext(pCont);
	return !pFormContext ? NULL : reinterpret_cast<CGetDoc7*>(pFormContext->m_GetDoc);
}

// немедленное закрытие формы, но с отработкой всех положенных событий
// использован код АльФ-а из ВК FormEx
//
/*BOOL CSystem::funcCloseForm(CValue& RetVal, CValue **ppValue)
{
	RetVal = 0L;

	CValue& GroupContValue = *ppValue[0];
	ValidateParamGroupContext(GroupContValue);
	CBLContext* pGroupCont = GroupContValue.GetContext();

	CGetDoc7* pDoc = GetDoc7(pGroupCont);
	if(pDoc)
	{
		pDoc->DoCloseForm();

 		// АльФ: т.к. в попытке немедленное закрытие валит 1С, нужна след.проверка
		// artbear - я пробовал, но 1С не падает, все ок
// extern DWORD real_vtable;
// DWORD real_vtable;
//
// class CBLModuleRaise : public CBLModule7
// {
// public:
//
// 	CBLModuleRaise(class CBLContext * c,char const * s, DWORD vtable):CBLModule7(c, s)
// 	{
// 		real_vtable = vtable;
// 	};
//
// 	virtual int  OnRuntimeError(void)
// 	{
// 		*(DWORD*)this = real_vtable;
// 		CBLModule7::RollbackAll();
// 		return 0;
// 	};
// };
//
// 		CBLModule* pMod = CBLModule::GetExecutedModule();
// 		if(pMod)
// 		{
// 			CBLModuleRaise cModRaise(NULL, NULL, *(DWORD*)pMod);
// 			*(DWORD*)pMod = *(DWORD*)&cModRaise;
// 			CExecutedModule* pExeMod = pMod->pIntInfo->pExecutedModule;
// 			if(pExeMod->m_DWordArray2.GetSize() > pExeMod->val_23)
// 				pExeMod->val_23 = pExeMod->m_DWordArray2.GetSize();
// 		}
// 		CBLModule::RaiseExtRuntimeError(NULL, FALSE);

		RetVal = 1L;
	}

	return TRUE;
}*/

BOOL CSystem::funcCloseForm(CValue& RetVal, CValue **ppValue)
{
	RetVal = 0L;

	CValue& GroupContValue = *ppValue[0];
	ValidateParamGroupContext(GroupContValue);

	CValue& NumericValue = *ppValue[1];
	ValidateParamNumeric(NumericValue );
	
	CBLContext* pGroupCont = GroupContValue.GetContext();

	CGetDoc7* pDoc = GetDoc7(pGroupCont);
	if(pDoc)
	{
		if (!NumericValue.GetNumeric().operator long())
			// TODO возможно, первый параметр должен быть другим числом, 
			// но у меня ни на тестах, ни в реальной базе не падает
			pDoc->SetWantClose(0, 1); // не задавать вопросов при закрытии несохраненных документов/элементов справочников
		
		// нужно самостоятельно обнулить все ссылки на КонтекстФормы.Форма (1С этого почему-то не делает)
		// иначе если сохранить его в отдельной переменной до закрытия, 
		// а потом пытаться использовать после физического закрытия - будет баг
		// также 1С будет падать, если эту переменную передать 
		// в какой-нибудь метод любой ВК, которая принимает форму
		// например, АктивИкс.УстановитьАтрибут или Форма.УстановитьФорму

		CFormAllCtrlsContext* pForm = GetFormContext(pGroupCont);
		if (!pForm)
			return TRUE;
		
		CValuePtrArray& CValueArray = pForm->m_Array;
		for(int i = 0; i < CValueArray.GetSize(); ++i)
		{
			CValue* pValue = CValueArray.GetAt(i);
			pValue->Reset();
		}

		// мой вариант artbear
		pDoc->GetFrame()->SendMessage(WM_CLOSE, 0, 0);

		RetVal = 1L;
	}
	
	return TRUE;
}

BOOL CSystem::defsCloseForm(int nParam, CValue* param) const
{
	if (1 == nParam)
 		*param = 0L;

	return TRUE;
}
