// Informer.cpp: implementation of the CInformer class.
// develop by artbear
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "..\\SetOfHierarchy.h"
#include "Informer.h"

//_____________________________________________________________________________
//
// CInformer Maps
//_____________________________________________________________________________
//
BEGIN_BL_METH_MAP(CInformer) 
    BL_METH_FUNC("GetMethodsTable", "ПолучитьТаблицуМетодов", 1, funcGetMethodsTable)
    BL_METH_FUNC("GetPropsTable", "ПолучитьТаблицуСвойств", 1, funcGetPropsTable)

    BL_METH_FUNC("MethodExists", "МетодСуществует", 2, funcHaveContextMethod)
    BL_METH_FUNC("PropExists", "СвойствоСуществует", 2, funcHaveContextProp)

	// ЯвляетсяОбъектом(ПроверяемыйОбъектИлиНаименованиеТипа, НаименованиеТипаНаКоторыйПроверяем)
    BL_METH_FUNC("TheObjectIs", "ЯвляетсяОбъектом", 2, funcTheObjectIs)

	// ЯвляетсяФункцией(ПроверяемыйОбъект, НаименованиеМетодаКоторыйПроверяем)
    BL_METH_FUNC("IsFunction", "ЯвляетсяФункцией", 2, funcIsFunction)

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

    BL_METH_FUNC("GetInternalTypeName",		"ТипЗначенияСтрВнутр",	1, funcGetInternalTypeName)
	
END_BL_METH_MAP() 

BEGIN_BL_PROP_MAP(CInformer)
END_BL_PROP_MAP()

IMPLEMENT_MY_CREATE_CONTEXT(CInformer, "Informer", "Информатор");

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

BOOL CInformer::funcGetMethodsTable(CValue &RetVal, CValue **ppValue)
{
	GetMethodsTable(*ppValue[0], RetVal);
	
	return TRUE;
}

BOOL CInformer::funcGetPropsTable(CValue &RetVal, CValue **ppValue)
{
	GetPropsTable(*ppValue[0], RetVal);

	return TRUE;
}

bool CInformer::GetMethodsTable(const CValue& ContextValue, CValue& RetVal)
{
	return GetTableFromContext(true, ContextValue, RetVal);
}

bool CInformer::GetPropsTable(const CValue& ContextValue, CValue& RetVal)
{
	return GetTableFromContext(false, ContextValue, RetVal);
}

bool CInformer::GetTableFromContext(bool bNeedMethods, const CValue& ContextValue, CValue& VTValue)
{
	CBLContext* pCont = ContextValue.GetContext();

	CValueTable* pVT = NULL;
	if (VTValue.IsEmpty())
	{
		VTValue.CreateObject("ТаблицаЗначений");
		pVT = ((CValueTableContextData*)(VTValue.GetContext()->GetInternalData()))->GetValueTable();
	}
	else
		pVT = CValue2VT(VTValue);

	if (pCont)
		return GetTableFromContext(bNeedMethods, pCont, pVT);

	return false;
}

bool CInformer::GetTableFromContext(bool bNeedMethods, CBLContext* pCont, CValueTable* pVT)
{
	if (!pVT)
		return false;

	CString str;
	CType ctNum(NUMBER_TYPE_1C);
	CType ctStr(STRING_TYPE_1C);
	CValue *pv = new CValue [5];
	CValue **ppv = new CValue* [5];

	for (int i = 0; i < 5; ++i)
		ppv[i] = pv + i;

	pVT->Clear(TRUE);
	pVT->AddColumn(str = "Number",		ctNum, str, 0, "", 0);
	pVT->AddColumn(str = "NameEng",	ctStr, str, 0, "", 0);
	pVT->AddColumn(str = "NameRus",	ctStr, str, 0, "", 0);
	if (bNeedMethods)
	{
		pVT->AddColumn(str = "HasRetVal",	ctNum, str, 0, "", 0);
		pVT->AddColumn(str = "NParams",	ctNum, str, 0, "", 0);
	}
	else
	{
		pVT->AddColumn(str = "IsReadable",	ctNum, str, 0, "", 0);
		pVT->AddColumn(str = "IsWritable",	ctNum, str, 0, "", 0);
	}

	if (bNeedMethods)
		for (i = 0; i < pCont->GetNMethods(); ++i)
		{
			pv[0] = static_cast<long>(i);
			pv[1] = pCont->GetMethodName(i, 0);
			pv[2] = pCont->GetMethodName(i, 1);
			pv[3] = static_cast<long>(pCont->HasRetVal(i));
			pv[4] = pCont->GetNParams(i);
			pVT->AddRow(ppv, -1);
		}
	else
		for (i = 0; i < pCont->GetNProps(); ++i)
		{
			pv[0] = static_cast<long>(i);
			pv[1] = pCont->GetPropName(i, 0);
			pv[2] = pCont->GetPropName(i, 1);
			pv[3] = static_cast<long>(pCont->IsPropReadable(i));
			pv[4] = static_cast<long>(pCont->IsPropWritable(i));
			pVT->AddRow(ppv, -1);
		}
	//}
	delete [] pv;
	delete [] ppv;

	return true;
}

void CInformer::GetMethodOfContext(class CValue * * ppValue)
{
	CBLContext* pCont = ppValue[0]->GetContext();

	if (pCont)
	{
		CValueTable *pVTm = CValue2VT(*ppValue[1]);
		CValueTable *pVTp = CValue2VT(*ppValue[2]);
		if (pVTm || pVTp)
		{
			if (pVTm)
				GetMethodsTable(*ppValue[0], *ppValue[1]);

			if (pVTp)
				GetPropsTable(*ppValue[0], *ppValue[2]);
		}
		else
		{
			// original section by Deb
			pBkEndUI->DoMessageLine(pCont->GetRuntimeClass()->m_lpszClassName, mmInformation);
			
			int nMeth = pCont->GetNMethods();
			if (nMeth > 0)
				pBkEndUI->DoMessageLine("==============================\r\nMethods\r\n==============================", mmInformation);
			for (int i = 0; i < nMeth; i++)
			{
				char buf[1000] = {0};
				char whatIs[100] = {0};
				if (pCont->HasRetVal(i)==1)
					strcpy(whatIs, "Function ");
				else
					strcpy(whatIs, "Procedure ");
				
				int nParams = pCont->GetNParams(i);
				
				sprintf(buf, "%d. %s [%s]  [%s] nParams = %d",i,whatIs,pCont->GetMethodName(i, 0), pCont->GetMethodName(i, 1), nParams);
				pBkEndUI->DoMessageLine(buf, mmInformation);
			}        
			int nProps = pCont->GetNProps();
			if (nProps > 0)
				pBkEndUI->DoMessageLine("==============================\r\nProperties\r\n==============================", mmInformation);
			
			for (i = 0; i < nProps; i++)
			{
				char buf[1000] = {0};
				char whatIs[100] = {0};
				
				if (pCont->IsPropReadable(i)==0)
					strcpy(whatIs, "Write only ");
				if (pCont->IsPropWritable(i)==0)
					strcpy(whatIs, "Read only ");
				
				int nParams = pCont->GetNParams(i);
				
				sprintf(buf, "%d. [%s]  [%s]  %s",i,pCont->GetPropName(i, 0), pCont->GetPropName(i, 1), whatIs);
				pBkEndUI->DoMessageLine(buf, mmInformation);
			}
		}
	}
}

BOOL CInformer::HaveContextMethodOrProp(bool bNeedMethods, CValue &RetVal, CValue **ppValue)
{
	CBLContext* pCont = ppValue[0]->GetContext();
	if (!pCont)
		if (bNeedMethods)
			RuntimeError("Информатор::МетодКонтекстаСуществует - первым параметром нужно передать контекст");
		else
			RuntimeError("Информатор::СвойствоКонтекстаСуществует - первым параметром нужно передать контекст");

	CValue& ValueParam2 = *ppValue[1];

	if (2 != ValueParam2.GetTypeCode())
		if (bNeedMethods)
			RuntimeError("Информатор::МетодКонтекстаСуществует - вторым параметром нужно передать название метода (Строка)");
		else
			RuntimeError("Информатор::СвойствоКонтекстаСуществует - вторым параметром нужно передать название метода (Строка)");

	int iMethodNum = -1;
	if (bNeedMethods)
		iMethodNum = pCont->FindMethod(ValueParam2.GetString());
	else
		iMethodNum = pCont->FindProp(ValueParam2.GetString());

	RetVal = CNumeric(-1 != iMethodNum? 1 : 0);

	return TRUE;
}

BOOL CInformer::funcHaveContextMethod(CValue &RetVal, CValue **ppValue)
{
	return HaveContextMethodOrProp(true, RetVal, ppValue);
}

BOOL CInformer::funcHaveContextProp(CValue &RetVal, CValue **ppValue)
{
	return HaveContextMethodOrProp(false, RetVal, ppValue);
}

// ЯвляетсяОбъектом(ПроверяемыйОбъектИлиНаименованиеТипа, НаименованиеТипаНаКоторыйПроверяем)
BOOL CInformer::funcTheObjectIs(CValue &RetVal, CValue **ppValue)
{
	RetVal = CNumeric(0);

	CValue& valParam1 = *ppValue[0];
	CValue& valParam2 = *ppValue[1];

	if (2 != valParam2.GetTypeCode())
	{
		return true; // TODO возможно, нужно исключение
	}
	CString strPar2 = valParam2.GetString();
	if (strPar2.IsEmpty())
	{
		//CBLModule::RaiseExtRuntimeError("В метод необходимо передавать имя класса",0);
		return true; // TODO возможно, нужно исключение
	}

	CString strPar1;
	int iTypeCodeParam1 = valParam1.GetTypeCode();

	bool bIsString = 2 == iTypeCodeParam1;
	if (bIsString)
	{

		strPar1 = valParam1.GetString();
		if (strPar1.IsEmpty())
		{
			//CBLModule::RaiseExtRuntimeError("В метод необходимо передавать имя класса",0);
			return true; // TODO возможно, нужно исключение
		}
	}
	else // if (bIsString)
	{
		if (100 != iTypeCodeParam1)
			RuntimeError("Первым параметром необходимо передавать агрегатный объект или строку-наименование типа");

		CBLContext* pCont = valParam1.GetContext();
		if (!pCont)
			return true;

		strPar1 = pCont->GetTypeString();
	}

	if (!strPar1.CompareNoCase(strPar2) || CSetOfHierarchy::GetHierarchy()->TheClassIsDerive(strPar1, strPar2))
		RetVal = CNumeric(1);
	
	return true;
}

BOOL CInformer::funcIsFunction(CValue &RetVal, CValue **ppValue)
{
	RetVal = CNumeric(0); // невозможно определить

	CBLContext* pContext = ppValue[0]->GetContext();
	
	if (!pContext)
	{
		return TRUE;
	}
	CValue& valParam2 = *ppValue[1];

	if (2 != valParam2.GetTypeCode())
	{
		RuntimeError("Вторым параметром необходимо передавать строку-наименование метода");
	}

	CString strPar2 = valParam2.GetString();
	if (strPar2.IsEmpty())
	{
		RuntimeError("Вторым параметром необходимо передавать строку-наименование метода");
	}

	int iNumMethod = pContext->FindMethod(strPar2);
	if (iNumMethod != -1)
	{
 		RetVal = CNumeric(pContext->HasRetVal(iNumMethod));
// 		int iRes = pContext->HasRetVal(iNumMethod);
// 		RetVal = CNumeric(iRes);
	}

	return TRUE;
}

// получить командную строку 1С (взято из FormEx)
BOOL  CInformer::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  CInformer::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;
}

// получить внутренний тип значения для контекста (из RuntimeClass)
// например, для объекта "Регистры" встроенный ТипЗначенияСтр выдает "НеизвестныйОбъект"
BOOL  CInformer::funcGetInternalTypeName(CValue& RetVal, CValue **params)
{
	ValidateParamContext(*params[0], "Данный метод применяется только для агрегатных типов");

	CBLContext* pCont = params[0]->GetContext();
	CString strTypeName = pCont->GetRuntimeClass()->m_lpszClassName;

	RetVal = strTypeName;

	return TRUE;
}
