// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"
#include "resource.h"

#ifdef _ATL_STATIC_REGISTRY
#include <statreg.h>
#include <statreg.cpp>
#endif

#include <atlimpl.cpp>
#include <atlctl.cpp>
#include <atlwin.cpp>

extern CBkEndUI * pBkEndUI;
extern BOOL m_bShowKL;
extern CString strTimerName;

//Функции для выдачи сообщений в окно сообщений 1С
void ShowMsg(const char* format,MessageMarker marker,...)
{
	va_list arg;
	va_start(arg,marker);
	CString txt;
	txt.FormatV(format,arg);
	va_end(arg);
	if(!pBkEndUI)
		AfxMessageBox(txt);
	else
		pBkEndUI->DoMessageLine(txt,marker);
}

void Msg(char *msg, ...)
{
	CString str;
	va_list ap;
	va_start(ap, msg);
	str.FormatV(msg, ap);
	va_end(ap);

	pBkEndUI->DoMessageLine((LPCSTR)str, mmBlueTriangle);
}

void LogErr(const char* format,...)
{
	va_list arg;
	va_start(arg,format);
	CString txt;
	txt.FormatV(format,arg);
	pBkEndUI->DoMessageLine(txt,mmBlackErr);
	va_end(arg);
}

// функция выдает окно с информацией
void MessageBox(const char* format, ...)
{
	va_list arg;
	va_start(arg,format);
	CString txt;
	txt.FormatV(format,arg);
	va_end(arg);

	AfxMessageBox(txt);
}

//Функция сообщает об ошибке, и информирует 1С что произошло исключение
void RuntimeError(const char *msg, ...)
{
	CString str;
	va_list ap;
	va_start(ap, msg);
	str.FormatV(msg, ap);
	va_end(ap);

	CBLModule::RaiseExtRuntimeError((LPCSTR)str, mmRedErr);
}

void RuntimeErrorRes(UINT nID)
{
	CString err;
	err.LoadString(nID);
	CBLModule::RaiseExtRuntimeError(err, mmRedErr);
}


void CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
	if(nIDEvent == 500)
	{
		SECStatusBar* pStatus = (SECStatusBar*)SECStatusBar::FromHandlePermanent(hWnd);
		if((pStatus != NULL) & m_bShowKL)
		{
			int count = pStatus->GetCount();
			char KLName[KL_NAMELENGTH]; 
			if(GetLocaleInfo((LCID)((WORD)GetKeyboardLayout(AfxGetApp()->m_nThreadID)),LOCALE_SABBREVLANGNAME,KLName,KL_NAMELENGTH))
				pStatus->SetPaneText(count-2,KLName,KL_NAMELENGTH);
			char buf[8];
			SYSTEMTIME SystemTime;
			GetLocalTime(&SystemTime);
			//sprintf(buf,"%.2d:%.2d:%.2d",SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond);
			sprintf(buf,"%.2d:%.2d",SystemTime.wHour,SystemTime.wMinute);
			pStatus->SetPaneText(count-1,buf,8);
		}
	}
	else if(nIDEvent == 501)
	{
		if(!strTimerName.IsEmpty())
			_CallFromGlobalModules(NULL,NULL,(LPCSTR)strTimerName,NULL,0,NULL,true);
	}
}

void GetRTCFull(CRuntimeClass* rtc, CString& Str)
{
    if (rtc)
    {
        Str += "<-";
        Str += rtc->m_lpszClassName;
        GetRTCFull(rtc->m_pfnGetBaseClass(), Str);
    }
}

void Trace0(const char* str)
{
    GetBkEndUI()->DoMessageLine(str, mmNone);
}

void MessageFullRTC(CObject* obj)
{
    CString Str;
    GetRTCFull(obj->GetRuntimeClass(), Str);
    Trace0(Str);
}


CVTExtended* CValue2VTExt(CValue const& Value)
{
	if( Value.GetTypeCode() != AGREGATE_TYPE_1C ) return NULL;

    CBLContext* pVTContext = Value.GetContext();
	if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CVTExtended") == 0 )
	{
		return (CVTExtended*)pVTContext;
	}
	else if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CComponentClass") == 0 )
	{
		CValue vBaseClass;
		if( static_cast<CComponentClass*>(pVTContext)->GetBaseClass("ИндексированнаяТаблица", vBaseClass) )
		{
			return (CVTExtended*)(vBaseClass.GetContext());
		}
	}

	return NULL;
}

CValueTable* CValue2VT(CValue const& Value)
{
	if( Value.GetTypeCode() != AGREGATE_TYPE_1C ) return NULL;

    CBLContext* pVTContext = Value.GetContext();
	if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CValueTableContext") == 0 )
	{
		return ((CValueTableContextData*)pVTContext->GetInternalData())->GetValueTable();
	}
	else if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CComponentClass") == 0 )
	{
		CValue vBaseClass;
		if( static_cast<CComponentClass*>(pVTContext)->GetBaseClass("ТаблицаЗначений", vBaseClass) )
		{
			pVTContext = vBaseClass.GetContext();
			return ((CValueTableContextData*)pVTContext->GetInternalData())->GetValueTable();
		}
	}

	return NULL;
}

CPtrArray* CValue2VL(CValue const& Value)
{
	if( Value.GetTypeCode() != AGREGATE_TYPE_1C ) return NULL;

    CBLContext* pVTContext = Value.GetContext();
	if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CValueListContext") == 0 )
	{
		return (CPtrArray*)(((CValueListContext*)(pVTContext))->m_pValueItemList);
	}
	else if( strcmp(pVTContext->GetRuntimeClass()->m_lpszClassName, "CComponentClass") == 0 )
	{
		CValue vBaseClass;
		if( static_cast<CComponentClass*>(pVTContext)->GetBaseClass("СписокЗначений", vBaseClass) )
		{
			pVTContext = vBaseClass.GetContext();
			return (CPtrArray*)(((CValueListContext*)(pVTContext))->m_pValueItemList);
		}
	}

	return NULL;
}

CString strLastError(DWORD dwMessageID /* = 0*/)
{
	void* cstr = NULL;
	::FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		dwMessageID==0 ? GetLastError() : dwMessageID,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &cstr,
		0,
		NULL
		);
	CString res = (char*)cstr;
	::LocalFree(cstr);
	return res;
}

// проверка на существование файла или папки
bool FileExists(CString fileName)
{
	CString sAbsoluteFilePath(::AbsoluteFilePath(fileName));
	DWORD tRes = GetFileAttributes((LPCSTR)sAbsoluteFilePath);
/*	if (tRes != 0xFFFFFFFF)
		if (!(tRes & FILE_ATTRIBUTE_DIRECTORY))
			bFileExists = true;*/
	return tRes != 0xFFFFFFFF && !(tRes & FILE_ATTRIBUTE_DIRECTORY);
}

bool FolderExists(CString folderName)
{
	DWORD tRes = GetFileAttributes((LPCSTR)folderName);
	return tRes != 0xFFFFFFFF && tRes & FILE_ATTRIBUTE_DIRECTORY;
}

bool DirectoryExists(CString directoryName)
{
	return FolderExists(directoryName);
}

// получить имя папки из полного пути к файлу
// существование файла не проверяется
// если не удалось получить имя папки, возвращается ""
//
// ВАЖНО: папка всегда возвращается со слешем в конце!!
//
//	из "T:\Папка Файла\Имя Файла.txt" получу T:\Папка Файла\
//
CString ExtractFileDir(CString FileName)
{
	int i = 0;
	const char PathDelim = '\\';
	const char DriveDelim = ':';
	LPCSTR pName = (LPCSTR)FileName;
	LPCSTR s = strrchr(pName, PathDelim);
	if (!s) return "";

	int count = (s-pName)+1+1; // 1 байт под '\0'
	char* _ret = new char[count];
	lstrcpyn(_ret, pName, count);
	CString ret = _ret;
	delete [] _ret;
	return ret;
}

// получить абсолютный путь к файлу\папке
// учитывается наличие "." или ".."
//
CString AbsoluteFilePath(const CString sRelativePath)
{
	LPSTR fullpath = new char[_MAX_PATH];
	if (_fullpath( fullpath, (LPCTSTR)sRelativePath, _MAX_PATH ) == NULL )
		return ""; // TODO или выбросить исключение
	CString ret(fullpath); 
	
	delete [] fullpath;
	return ret;
}

// получить версию произвольного файла
CString GetVersionField(LPCTSTR filePath, LPCTSTR szField)
{
	if (!FileExists(filePath))
		AfxThrowFileException(CFileException::fileNotFound);

    CString sRet;
	LPTSTR BufFileName = const_cast<LPTSTR>(filePath);
    
    DWORD z,size;
    size = GetFileVersionInfoSize(BufFileName, &z);
	//if (!size) throw CException();
    if(size)
    {
        char* pVersion = new char[size];
        GetFileVersionInfo(BufFileName, 0, size, pVersion);
        DWORD* translate;
        UINT len;
        VerQueryValue(pVersion, "\\VarFileInfo\\Translation", (void**)&translate, &len);
        CString blockName;
        blockName.Format("\\StringFileInfo\\%04x%04x\\", LOWORD(translate[0]), HIWORD(translate[0]));
        LPCTSTR valuebuf;
        CString fdName = blockName + szField;
        if (VerQueryValue(pVersion, (char*)(LPCTSTR)fdName, (void**)&valuebuf, &len))
			sRet = valuebuf;
        delete pVersion;
    }
    return sRet;
}

// получить версию
CString GetVersionField(LPCSTR szField)
{
    LPTSTR BufFileName = new char[256];
    GetModuleFileName(_Module.m_hInst, BufFileName, 256);
	CString ret = ::GetVersionField(BufFileName, szField);
	delete [] BufFileName;
	return ret;
}

// Возвращает строку, полную версию 1С++, как она показана  на
// закладке "О программе..."
// Например, "2.0.2.0 Nightly build 2006-05-15" или "2.0.3.0"
//
CString GetFullVersion()
{
	CString sVersion = ::GetVersionField("FileVersion");
	sVersion.Replace(" ", "");
	sVersion.Replace(',', '.');
	CString sPrivateBuild = ::GetVersionField("PrivateBuild");
	if (!sPrivateBuild.IsEmpty())
	{
		sVersion += ' ';
		sVersion += sPrivateBuild;
	}
	CString sSpecialBuild = ::GetVersionField("SpecialBuild");
	if (!sSpecialBuild.IsEmpty())
	{
		sVersion += ' ';
		sVersion += sSpecialBuild;
	}
	return sVersion;
}

// вызвать метод одного из глобальных контекстов
// например, "IBDir"
// в случае ошибки выдает исключение throw
LPCTSTR CallMethodOfGlobalContexts0(CString MethodName)
{
	CBLContext* pCont = NULL;
	int nMethNum = ::FindMethodOfGlobalContexts(MethodName, pCont);
    if (nMethNum != -1)
    {
	  CValue retval;
      CValue* MasPar[1] = {0};
      CValue Par;
      MasPar[0] = &Par;
      
      if (pCont->CallAsFunc(nMethNum, retval, &MasPar[0])!=0)
      {
        const char *pC = retval.GetString().operator LPCSTR();            
        //pathBase1C = pC;
        //bFindMethod = true;            
		return pC;
      }
    }        
	throw LPCTSTR(NULL);
}

// найти метод одного из глобальных контекстов
// например, "IBDir"
// возвращает номер найденного метода 
//	или -1, если метод не найден
//
// в параметр pBLContext возвращется указатель на найденный глобальный контекст
// 
int FindMethodOfGlobalContexts(CString MethodName, CBLContext*& pBLContext)
{
    ULONG first = CBLContext::GetFirstLoadedContextID();
    for (ULONG ind = first; ind != 0; ind = CBLContext::GetNextLoadedContextID(ind))
    {
		CBLContext* pContLoaded = CBLContext::GetLoadedContext(ind);
      
		int nMethNum = pContLoaded->FindMethod((LPCTSTR)MethodName);
		if (nMethNum != -1)
		{
			pBLContext = pContLoaded;
			return nMethNum;
		}        
	}
	return -1;
}

// проверяет, существует ли данный контекст
// в основном удобно использовать для групповых контекстов
// чтобы, например, перед удалением группового контекста pBLContext->DecrRef 1C не падала
//
// TODO возможно проще это решить перехватом метода CBLContext::DecrRef или CGroupContext::DecrRef
//
bool IsContextLive(CBLContext* pBLContext)
{
	try
	{
		// убедимся в том, что контекст жив artbear
		// если нет, будет исключение и значит, контекста уже нет!!
		long i = pBLContext->GetTypeID();
		return true;	
	}
	catch(...)
	{
		return false;
	}
}