#include "standard.h"
#include "global.h"

#include "template.h"
#include "debug.h"
#include "utils.h"


#include <mapi.h>
#include "winmain.h"

bool UT_ExecAndWait(char* sProgramName,DWORD dwTimeout)
	{
	STARTUPINFO si;
	ZeroMemory(&si,sizeof(si));
	si.cb=sizeof(si);
	si.dwFlags=STARTF_USESHOWWINDOW;
	si.wShowWindow=SW_SHOW;
	PROCESS_INFORMATION pi;
	bool tOK=false;
	if (CreateProcess(NULL,sProgramName,NULL,NULL,false,0,NULL,NULL,&si,&pi))
		{
		if (WaitForInputIdle(pi.hProcess,dwTimeout)==0)
			tOK=true;
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		}
	return tOK;
	}

void* UT_GetResource(char* szResource,char* szType)
	{
	HRSRC hR=FindResource(App.hInstance,szResource,szType);
	if (!hR)
		{
		Log(LT_Error,"UT_GetResource: Can't find resource %s (type %s)",
						(char*)ERR_Describe_RC(szResource),
						(char*)ERR_Describe_RC(szType));
		return 0;
		}
	HGLOBAL hG=LoadResource(App.hInstance,hR);
	if (!hG)
		{
		Log(LT_Error,"UT_GetResource: Can't load resource %s (type %s)",
						(char*)ERR_Describe_RC(szResource),
						(char*)ERR_Describe_RC(szType));
		return 0;
		}
	return LockResource(hG);
	}

__declspec(naked) double UT_RDTSC()	// Pentium only!!!!
	{
	__asm
		{
		push	eax
		push	edx
		push	eax
		push	eax
		__emit	0x0f
		__emit	0x31
		mov		[esp],eax
		mov		[esp+4],edx
		fild	qword ptr [esp]
		pop		eax
		pop		eax
		pop		edx
		pop		eax
		ret
		}
	}

////////
local double LdPerformanceDivisor;
local double LdInitialTimer;
local DWORD LdwInitialTimer;

void UT_InitAccurateTimer()
	{
	LARGE_INTEGER pf;
	if (QueryPerformanceFrequency(&pf))
		{
		LdPerformanceDivisor=1.0/double(pf.QuadPart);
		Log(LT_Info,"UT_InitPerformanceCounter: divisor=%f",LdPerformanceDivisor);
		LdInitialTimer=0;
		LdInitialTimer=UT_GetAccurateTimer();		
		}
	else
		{
		LdPerformanceDivisor=0.0;
		Log(LT_Info,"UT_InitPerformanceCounter: using timeGetTime");
		LdwInitialTimer=timeGetTime();
		}
	}

double UT_GetAccurateTimer()
	{
	LARGE_INTEGER pr;
	if (QueryPerformanceCounter(&pr))
		return LdPerformanceDivisor*(double(pr.QuadPart)-LdInitialTimer);
	else
		return double(timeGetTime()-LdwInitialTimer)/1000.0;
	}

////////
time_t UT_ConvertDateToTimeT(char* szTime,char* szDate)
	{
	static char* aszMonthName[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
	struct tm a_tm;
	char acMonth[16];

	Zero(a_tm);
	sscanf(szTime,"%d:%d:%d",&a_tm.tm_hour,&a_tm.tm_min,&a_tm.tm_sec);
	sscanf(szDate,"%s %d %d",acMonth,&a_tm.tm_mday,&a_tm.tm_year);
	a_tm.tm_year-=1900;
	int nMonth;
	for (nMonth=0;nMonth<12;++nMonth)
		if (strcmp(acMonth,aszMonthName[nMonth])==0)
			{
			a_tm.tm_mon=nMonth;
			break;
			}
	a_tm.tm_isdst=-1;
	return mktime(&a_tm);
	}

void UT_CenterWindow(HWND hWnd)
	{
	RECT rcWnd,rcArea;
	SystemParametersInfo(SPI_GETWORKAREA,NULL,&rcArea,NULL);
	GetWindowRect(hWnd,&rcWnd);
	int x,y;
	x=(rcArea.right+rcArea.left)>>1;
	y=(rcArea.bottom+rcArea.top)>>1;
	x-=(rcWnd.right-rcWnd.left)>>1;
	y-=(rcWnd.bottom-rcWnd.top)>>1;
	SetWindowPos(hWnd,NULL,x,y,-1,-1,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
	}

/*** Command line argument stuff ***/

char* UT_FindArg(char* szArg)
	{
	char* pArg=strstr(GpCmdLine,szArg);
	if (!pArg)
		return 0;
	return pArg+strlen(szArg);
	}

bool UT_GetArg(char* szArg,int& rnValue)
	{
	char* pArg=UT_FindArg(szArg);
	if (!pArg)
		return false;
	return sscanf(pArg,"=%d",&rnValue)==1;
	}

/*** Fatal error box ***/

void UT_ErrorBox(char* szString,HWND hWndOwner)
	{
	MessageBox(hWndOwner,szString,"Tomb Raider II",MB_ICONERROR);
	}

void UT_ErrorBox(int nID,HWND hWndOwner)
	{
	char buffer[256];
	if (LoadString(App.hInstance,nID,buffer,sizeof buffer))
		UT_ErrorBox(buffer,hWndOwner);
	}

local BOOL CALLBACK UT_OKCB_DlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
	{
	switch (msg)
		{
		case WM_INITDIALOG:
			UT_CenterWindow(hWnd);
			return TRUE;

		case WM_COMMAND:
			switch (LOWORD(wParam))
				{
				case IDOK:
					EndDialog(hWnd,true);
					return true;

				case IDCANCEL:
					EndDialog(hWnd,false);
					return true;

				default:
					break;
				}
			break;

		default:
			break;
		}
	return false;
	}

bool UT_OKCancelBox(char* szDlgResource,HWND hWndOwner)
	{
	return DialogBox(App.hInstance,szDlgResource,hWndOwner,UT_OKCB_DlgProc);
	}

/*** GUID stuff ***/
//local const char LacGuidMap[]={'{',3,2,1,0,'-',5,4,'-',7,6,'-',8,'-',9,'-',10,'-',11,'-',12,'-',13,'-',14,'-',15,'}'};
local const char LacGuidMap[]={'{',3,2,1,0,'-',5,4,'-',7,6,'-',8,9,10,11,12,13,14,15,'}'};
local const char LacHex[] = "0123456789ABCDEF";

char* UT_StringFromGUID(GUID& rGUID)
	{
	static char acBuffer[GUID_STRING_LENGTH+1];
    const BYTE *pBytes=(const BYTE *)&rGUID;
	char* pBuffer=acBuffer;
	char c;
    for (int i=0;i<ArraySize(LacGuidMap);++i)
		{
		c=LacGuidMap[i];
        if (c<16)
			{
            *pBuffer++=LacHex[(pBytes[c]&0xF0)>>4];
            *pBuffer++=LacHex[(pBytes[c]&0x0F)];
			}
        else
            *pBuffer++=c;
		}
    *pBuffer=0;
	return acBuffer;
	}

local inline char GetDigit(char c)
	{
	if (c>='0' && c<='9')
		return c-'0';
	if (c>='A' && c<='F')
		return c-'A'+10;
	if (c>='a' && c<='f')
		return c-'A'+10;
	return -1;
	}

bool UT_GUIDFromString(char* pString,GUID& rGUID)
	{
	char* pCur=pString;
	BYTE* pBytes=(BYTE*)&rGUID;
	char c,h,l;
	for (int i=0;i<ArraySize(LacGuidMap);++i)
		{
		c=LacGuidMap[i];
		if (c<16)
			{
			h=GetDigit(*pCur++);
			if (h<0)
				return false;
			l=GetDigit(*pCur++);
			if (l<0)
				return false;
			pBytes[c]=(h<<4)+l;
			}
		else if (*pCur++!=c)
			return false;
		}
	return *pCur==0;
	}

/*** Mail ***/
#ifdef MAIL_OK
bool UT_SendMail(char* szTo,char* szSubject,char* szBody,bool tAllowLogonDialog)
	{
	char acAddress[256];

	strcpy(acAddress,"SMTP:");
	strcat(acAddress,szTo);

	HMODULE hlibMAPI=LoadLibrary("MAPI32.DLL");
	LPMAPISENDMAIL lpfnMAPISendMail=(LPMAPISENDMAIL)GetProcAddress(hlibMAPI,"MAPISendMail");
	MapiRecipDesc recipient={0,MAPI_TO,szTo,acAddress,0,0};
	MapiMessage message={0,
		0,	// subject
		0,	// body
		NULL, NULL, NULL, 0, NULL,
		1,&recipient,
		0,0};
	message.lpszSubject=szSubject;
	message.lpszNoteText=szBody;
	bool tItsAResult=(lpfnMAPISendMail(0,0,&message,tAllowLogonDialog?MAPI_LOGON_UI:0,0)==SUCCESS_SUCCESS);
	FreeLibrary(hlibMAPI);
	return tItsAResult;
	}
#endif

/*** Registry functions ***/
/*
local HKEY LhRegKey;
local DWORD LdwDisposition;

bool REG_OpenKey(char* szLocation)
	{
	return RegCreateKeyEx(HKEY_CURRENT_USER,
		szLocation,
		0,
		"",
		REG_OPTION_NON_VOLATILE,
		KEY_ALL_ACCESS,
		NULL,
		&LhRegKey,
		&LdwDisposition)==ERROR_SUCCESS;
	}

bool REG_KeyWasCreated()
	{
	return LdwDisposition==REG_CREATED_NEW_KEY;
	}

void REG_CloseKey()
	{
	RegCloseKey(LhRegKey);
	}

void REG_WriteValue(char* szName,DWORD dwValue)
	{
	RegSetValueEx(LhRegKey,szName,0,REG_DWORD,(CONST BYTE*)&dwValue,sizeof dwValue);
	}

void REG_WriteValue(char* szName,bool tValue)
	{
	DWORD dwValue=(DWORD)tValue;
	RegSetValueEx(LhRegKey,szName,0,REG_DWORD,(CONST BYTE*)&dwValue,sizeof dwValue);
	}

void REG_WriteValue(char* szName,double dValue)
	{
	char buffer[64];
	int nLen=sprintf(buffer,"%.5f",dValue);
	REG_WriteValue(szName,buffer,nLen);
	}

void REG_WriteValue(char* szName,void* pData,int nSize)
	{
	if (pData)
		RegSetValueEx(LhRegKey,szName,0,REG_BINARY,(CONST BYTE*)pData,nSize);
	else
		RegDeleteValue(LhRegKey,szName);
	}

void REG_WriteValue(char* szName,char* szString,int nLength)
	{
	if (szString)
		RegSetValueEx(LhRegKey,szName,0,REG_SZ,(CONST BYTE*)szString,(nLength>=0?nLength:strlen(szString))+1);
	else
		RegDeleteValue(LhRegKey,szName);
	}

void REG_DeleteValue(char* szName)
	{
	RegDeleteValue(LhRegKey,szName);
	}

bool REG_ReadValue(char* szName,DWORD& rdwValue,DWORD dwDefault)
	{
	DWORD dwType;
	DWORD dwBufSize=sizeof rdwValue;
	if ((RegQueryValueEx(LhRegKey,szName,0,&dwType,(LPBYTE)&rdwValue,&dwBufSize)==ERROR_SUCCESS)
		&&(dwType==REG_DWORD)
		&&(dwBufSize==sizeof rdwValue))
		return true;
	REG_WriteValue(szName,dwDefault);
	rdwValue=dwDefault;
	return false;
	}

bool REG_ReadValue(char* szName,bool& rtBool,bool tDefault)
	{
	DWORD dwType;
	DWORD dwVal;
	DWORD dwBufSize=sizeof dwVal;
	if ((RegQueryValueEx(LhRegKey,szName,0,&dwType,(LPBYTE)&dwVal,&dwBufSize)==ERROR_SUCCESS)
		&&(dwType==REG_DWORD)
		&&(dwBufSize==sizeof dwVal))
		{
		rtBool=bool(dwVal);
		return true;
		}
	REG_WriteValue(szName,tDefault);
	rtBool=tDefault;
	return false;
	}

bool REG_ReadValue(char* szName,double& rdDouble,double dDefault)
	{
	char buffer[64];
	if (REG_ReadValue(szName,buffer,sizeof buffer,0))
		{
		rdDouble=atof(buffer);
		return true;
		}
	REG_WriteValue(szName,dDefault);
	rdDouble=dDefault;
	return false;
	}

bool REG_ReadValue(char* szName,void* pData,int nSize,void* pDefaultData)
	{
	DWORD dwType;
	DWORD dwBufSize=nSize;
	if ((RegQueryValueEx(LhRegKey,szName,0,&dwType,(LPBYTE)pData,&dwBufSize)==ERROR_SUCCESS) &&
		dwType==REG_BINARY &&
		nSize==int(dwBufSize))
		return true;
	if (pDefaultData)
		REG_WriteValue(szName,pData,nSize);
	else
		RegDeleteValue(LhRegKey,szName);
	return false;
	}

bool REG_ReadValue(char* szName,char* szString,int nMax,char* szDefault)
	{
	DWORD dwType;
	DWORD dwBufSize=nMax;
	if ((RegQueryValueEx(LhRegKey,szName,0,&dwType,(LPBYTE)szString,&dwBufSize)==ERROR_SUCCESS) &&
		dwType==REG_SZ)
		return true;
	if (szDefault)
		{
		REG_WriteValue(szName,szDefault);
		int nLen=strlen(szDefault)+1;
		if (nLen>nMax)
			{
			nLen=nMax-1;
			szString[nLen]=0;
			}
		memcpy(szString,szDefault,nLen);
		}
	else
		RegDeleteValue(LhRegKey,szName);
	return false;
	}

bool REG_ReadValue(char* szName,GUID& rGUID,GUID* pDefaultGUID)
	{
	char buf[GUID_STRING_LENGTH+1];
	if (REG_ReadValue(szName,buf,sizeof buf) &&
		UT_GUIDFromString(buf,rGUID))
		return true;
	if (pDefaultGUID)
		{
		REG_WriteValue(szName,*pDefaultGUID);
		rGUID=*pDefaultGUID;
		}
	else
		RegDeleteValue(LhRegKey,szName);
	return false;
	}


*/

/*** DIB stuff ***/
/*
DIB::~DIB()
	{
	if (m_hPalette)
		{
		DeleteObject(m_hPalette);
		m_hPalette=0;
		}
	if ((m_dwFlags&DIB_FLAG_DELETEIMAGE) && m_pImage)
		{
		free(m_pImage);
		m_pImage=0;
		}
	}

void DIB::LoadFromResource(char* szResource)
	{
	if (m_hPalette)
		{
		DeleteObject(m_hPalette);
		m_hPalette=0;
		}
	m_dwFlags=0;
	m_pBMI=(BITMAPINFO*)UT_GetResource(szResource,RT_BITMAP);
	if (!m_pBMI)
		return;
	RGBQUAD* pColorTable=m_pBMI->bmiColors;
	m_pImage=pColorTable+256;
	DWORD adwLogPalette[1+256];
	adwLogPalette[0]=0x01000300;
	PALETTEENTRY* pPE=(PALETTEENTRY*)(adwLogPalette+1);
	for (int i=0;i<256;++i)
		{
		pPE[i].peRed=pColorTable[i].rgbRed;
		pPE[i].peGreen=pColorTable[i].rgbGreen;
		pPE[i].peBlue=pColorTable[i].rgbBlue;
		if (i>=10 && i<246)
			pPE[i].peFlags=PC_NOCOLLAPSE;
		else
			pPE[i].peFlags=0;
		}
	m_hPalette=CreatePalette((LOGPALETTE*)adwLogPalette);
	}

bool DIB::Make(int nWidth,int nHeight,uint8* pPalette)
	{
	if (m_hPalette)
		{
		DeleteObject(m_hPalette);
		m_hPalette=0;
		}
	m_dwFlags=0;
	m_pBMI=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER));
	if (!m_pBMI)
		return false;
	int nActualWidth=(nWidth+3)&(~3);
	m_pImage=malloc(nActualWidth*nHeight);
	if (!m_pImage)
		return false;
	m_dwFlags=DIB_FLAG_DELETEIMAGE;
	DWORD adwLogPalette[1+256];
	adwLogPalette[0]=0x01000300;
	PALETTEENTRY* pPE=(PALETTEENTRY*)(adwLogPalette+1);
	for (int i=0;i<256;++i)
		{
		pPE[i].peRed=*pPalette++;
		pPE[i].peGreen=*pPalette++;
		pPE[i].peBlue=*pPalette++;
		if (i>=10 && i<246)
			pPE[i].peFlags=PC_NOCOLLAPSE;
		else
			pPE[i].peFlags=0;
		}
	m_hPalette=CreatePalette((LOGPALETTE*)adwLogPalette);
	BITMAPINFOHEADER* pBMIH=&(m_pBMI->bmiHeader);
	Zero(*pBMIH);
	pBMIH->biSize=sizeof(BITMAPINFOHEADER);
	pBMIH->biWidth=nWidth;
	pBMIH->biHeight=-nHeight;
	pBMIH->biPlanes=1;
	pBMIH->biBitCount=8;
	pBMIH->biCompression=BI_RGB;
	return true;
	}

void DIB::Draw(HDC hDC,int nX,int nY)
	{
	if (m_hPalette)
		{
		BITMAPINFOHEADER& rBMIH=m_pBMI->bmiHeader;
		HPALETTE hOldPalette=SelectPalette(hDC,m_hPalette,false);
		RealizePalette(hDC);
		SetDIBitsToDevice(hDC,nX,nY,rBMIH.biWidth,rBMIH.biHeight,0,0,0,rBMIH.biHeight,m_pImage,m_pBMI,DIB_RGB_COLORS);
		SelectPalette(hDC,hOldPalette,false);
		}
	}

void DIB::WM_PaletteChanged(HWND hWnd,HWND hWndChangedBy)
	{
	if (!m_hPalette || hWndChangedBy==hWnd)
		return;
	WM_QueryNewPalette(hWnd);
	}

void DIB::WM_QueryNewPalette(HWND hWnd)
	{
	if (m_hPalette)
		{
		HDC hDC=GetDC(hWnd);
		HPALETTE hOldPalette=SelectPalette(hDC,m_hPalette,false);
		UnrealizeObject(m_hPalette);
		RealizePalette(hDC);
		SelectPalette(hDC,hOldPalette,false);
		ReleaseDC(hWnd,hDC);
		}
	}


*/
/*** DIB Window ***/

/*
DefLocalString(DWWndClassName,"Dude:TombRaiderII:DWWndClass");
#define WM_DW_LOADFROMRESOURCE (WM_USER+1000)

local LONG CALLBACK DW_WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
	{
	DIB* pDIB;
	RECT rcTemp;
	POINT pt;
	int nWidth,nHeight;
	HDC hDC;
	PAINTSTRUCT ps;
	char acBuffer[64];

	pDIB=(DIB*)GetWindowLong(hWnd,8);
	switch (msg)
		{
		case WM_CREATE:
			GetClientRect(hWnd,&rcTemp);
			pt.x=rcTemp.right>>1;
			pt.y=rcTemp.bottom>>1;
			MapWindowPoints(hWnd,GetParent(hWnd),&pt,1);
			SetWindowLong(hWnd,0,pt.x);
			SetWindowLong(hWnd,4,pt.y);
			pDIB=new DIB;
			SetWindowLong(hWnd,8,(DWORD)pDIB);
			GetWindowText(hWnd,acBuffer,sizeof acBuffer);
			if (*acBuffer==0)
				return 0;
			wParam=(WPARAM)acBuffer;
			// fall through
		case WM_DW_LOADFROMRESOURCE:
			pDIB->LoadFromResource((char*)wParam);
			nWidth=pDIB->GetWidth();
			nHeight=pDIB->GetHeight();
			pt.x=GetWindowLong(hWnd,0);
			pt.y=GetWindowLong(hWnd,4);
			MoveWindow(hWnd,(pt.x-(nWidth>>1))-2,(pt.y-(nHeight>>1))-2,nWidth+4,nHeight+4,true);
			return 0;

		case WM_PAINT:
			GetClientRect(hWnd,&rcTemp);
			hDC=BeginPaint(hWnd,&ps);
			DrawEdge(hDC,&rcTemp,EDGE_ETCHED,BF_RECT|BF_ADJUST);
			pDIB->Draw(hDC,rcTemp.left,rcTemp.top);
			EndPaint(hWnd,&ps);
			return 0;

		case WM_PALETTECHANGED:
			pDIB->WM_PaletteChanged(hWnd,(HWND)wParam);
			InvalidateRect(hWnd,0,false);
			UpdateWindow(hWnd);
			break;

		case WM_QUERYNEWPALETTE:
			pDIB->WM_QueryNewPalette(hWnd);
			InvalidateRect(hWnd,0,false);
			UpdateWindow(hWnd);
			return TRUE;

		case WM_DESTROY:
			delete pDIB;
			break;
		}
	return DefWindowProc(hWnd,msg,wParam,lParam);
	}

bool DW_RegisterWindowClass()
	{
	Log(LT_Enter,"DW_RegisterWindowClass");
	WNDCLASS wc;
	Zero(wc);
	wc.style=CS_VREDRAW|CS_HREDRAW;
	wc.lpfnWndProc=DW_WndProc;
	wc.hInstance=App.hInstance;
	wc.hCursor=LoadCursor(NULL,IDC_ARROW);
	wc.lpszClassName=LocalString(DWWndClassName);
	wc.cbWndExtra=12;
	if (!RegisterClass(&wc))
		{
		Log(LT_Error,"Couldn't register window class");
		return false;
		}
	return true;
	}

void DW_LoadFromResource(HWND hWnd,char* szResource)
	{
	SendMessage(hWnd,WM_DW_LOADFROMRESOURCE,(WPARAM)szResource,0);
	}

void DW_HandlePaletteChanges(HWND hWnd,UINT msg,WPARAM wParam)
	{
	if (msg==WM_PALETTECHANGED || msg==WM_QUERYNEWPALETTE)
		SendMessage(hWnd,msg,wParam,0);
	}

*/
  
void UT_MemBlt(uint8* pDest,int nDX,int nDY,int nDW,int nDH,int nDStride,uint8* pSrc,int nSX,int nSY,int nSStride)
{
	pDest+=(nDX+nDY*nDStride);
	pSrc+=(nSX+nSY*nSStride);
	while (nDH--)
	{
		memcpy(pDest,pSrc,nDW);
		pDest+=nDStride;
		pSrc+=nSStride;
	}
}
