/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2003 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "commonheaders.h"

int ProfileManager(char *szDbDest,int cbDbDest);
int ShouldAutoCreate(void);
int CreateDbHeaders(HANDLE hFile);
int InitialiseDbHeaders(void);
int InitSettings(void);
void UninitSettings(void);
int InitContacts(void);
void UninitContacts(void);
int InitEvents(void);
void UninitEvents(void);
int InitCrypt(void);
int InitModuleNames(void);
void UninitModuleNames(void);
int InitCache(void);
void UninitCache(void);
int InitIni(void);
void UninitIni(void);
int InitPreset(void);
void UninitPreset(void);
int InitDialogs(void);
void InitSecurity(void);
void UnloadSecurity(void);

HANDLE hDbFile=INVALID_HANDLE_VALUE;
CRITICAL_SECTION csDbAccess;
struct DBHeader dbHeader;
char szDbPath[MAX_PATH];

static void UnloadDatabase(void)
{
	// update profile last modified time
	DWORD bytesWritten;
	SetFilePointer(hDbFile,0,NULL,FILE_BEGIN);
	WriteFile(hDbFile,dbHeader.signature,sizeof(1),&bytesWritten,NULL);

	CloseHandle(hDbFile);
}

DWORD CreateNewSpace(int bytes)
{
	DWORD ofsNew;
	ofsNew=dbHeader.ofsFileEnd;
	dbHeader.ofsFileEnd+=bytes;
	DBWrite(0,&dbHeader,sizeof(dbHeader));
	log2("newspace %d@%08x",bytes,ofsNew);
	return ofsNew;
}

void DeleteSpace(DWORD ofs,int bytes)
{
	if (ofs+bytes == dbHeader.ofsFileEnd)	{
		log2("freespace %d@%08x",bytes,ofs);
		dbHeader.ofsFileEnd=ofs;
	} else	{
		log2("deletespace %d@%08x",bytes,ofs);
		dbHeader.slackSpace+=bytes;
	}
	DBWrite(0,&dbHeader,sizeof(dbHeader));
	DBFill(ofs,bytes);
}

DWORD ReallocSpace(DWORD ofs,int oldSize,int newSize)
{
	DWORD ofsNew;

	if (oldSize >= newSize) return ofs;

	if (ofs+oldSize == dbHeader.ofsFileEnd) {
		ofsNew = ofs;
		dbHeader.ofsFileEnd+=newSize-oldSize;
		DBWrite(0,&dbHeader,sizeof(dbHeader));
		log3("adding newspace %d@%08x+%d",newSize,ofsNew,oldSize);
	} else {
		ofsNew=CreateNewSpace(newSize);
		DBMoveChunk(ofsNew,ofs,oldSize);
		DeleteSpace(ofs,oldSize);
	}
	return ofsNew;
}

void UnloadDatabaseModule(void)
{
	//UninitIni();
	UninitPreset();
	UninitEvents();
	UninitSettings();
	UninitContacts();
	UninitModuleNames();
	UninitCache();
	UnloadDatabase();
	UnloadSecurity();
	DeleteCriticalSection(&csDbAccess);
}

INT_PTR GetProfileName(WPARAM wParam, LPARAM lParam)
{
	char * p = 0;
	p = strrchr(szDbPath, '\\');
	if ( p == 0 ) return 1;
	p++;
	strncpy((char*)lParam, p, (size_t) wParam);
	return 0;
}

int LoadDatabaseModule(void)
{
	char szDBName[255];
	InitializeCriticalSection(&csDbAccess);
	log0("DB logging running");
	{
		DWORD dummy=0;
		hDbFile=CreateFileA(szDbPath,GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL);
		if ( hDbFile == INVALID_HANDLE_VALUE ) {
			return 1;
		}
		if ( !ReadFile(hDbFile,&dbHeader,sizeof(dbHeader),&dummy,NULL) ) {
			CloseHandle(hDbFile);
			return 1;
		}
	}
	InitSecurity();
	CheckDbHeaders(&dbHeader);
	GetProfileName((WPARAM)50, (LPARAM)szDBName);
	if(bEncoding && !CheckPassword(dbHeader.checkWord, szDBName)) return 1;
	//if(ParseCommandLine()) return 1;
	if(InitCache()) return 1;
	if(InitModuleNames()) return 1;
	if(InitContacts()) return 1;
	if(InitSettings()) return 1;
	if(InitEvents()) return 1;
	if(InitCrypt()) return 1;
	if(InitPreset()) return 1;
	if(InitDialogs()) return 1;
	//EncryptDB();
	return 0;
}

static DWORD DatabaseCorrupted=0;
static TCHAR *msg = NULL;
static DWORD dwErr = 0;

void __cdecl dbpanic(void *arg)
{
	if (msg)
	{
		TCHAR err[256];

		if (dwErr==ERROR_DISK_FULL)
			msg = TranslateT("Disk is full. Miranda will now shutdown.");

		mir_sntprintf(err, SIZEOF(err), msg, TranslateT("Database failure. Miranda will now shutdown."), dwErr);

		MessageBox(0,err,TranslateT("Database Error"),MB_SETFOREGROUND|MB_TOPMOST|MB_APPLMODAL|MB_ICONWARNING|MB_OK);
	}
	else
		MessageBox(0,TranslateT("Miranda has detected corruption in your database. This corruption maybe fixed by DBTool.  Please download it from http://www.miranda-im.org. Miranda will now shutdown."),
					TranslateT("Database Panic"),MB_SETFOREGROUND|MB_TOPMOST|MB_APPLMODAL|MB_ICONWARNING|MB_OK);

	TerminateProcess(GetCurrentProcess(),255);
}

void DatabaseCorruption(TCHAR *text)
{
	int kill=0;

	EnterCriticalSection(&csDbAccess);
	if (DatabaseCorrupted==0) {
		DatabaseCorrupted++;
		kill++;
		msg = text;
		dwErr = GetLastError();
	} else {
		/* db is already corrupted, someone else is dealing with it, wait here
		so that we don't do any more damage */
		LeaveCriticalSection(&csDbAccess);
		Sleep(INFINITE);
		return;
	}
	LeaveCriticalSection(&csDbAccess);
	if (kill) {
		_beginthread(dbpanic,0,NULL);
		Sleep(INFINITE);
	}
}

#ifdef DBLOGGING
void DBLog(const char *file,int line,const char *fmt,...)
{
	FILE *fp;
	va_list vararg;
	char str[1024];

	va_start(vararg,fmt);
	mir_vsnprintf(str,sizeof(str),fmt,vararg);
	va_end(vararg);
	fp=fopen("c:\\mirandadatabase.log.txt","at");
	fprintf(fp,"%u: %s %d: %s\n",GetTickCount(),file,line,str);
	fclose(fp);
}
#endif