#include "stdafx.h"

CDbxSQLite::CDbxSQLite(sqlite3 *database)
	: m_db(database),
	m_safetyMode(true),
	m_modules(1, strcmp)
{
}

CDbxSQLite::~CDbxSQLite()
{
	UninitEvents();
	UninitContacts();
	UninitSettings();

	if (m_db) {
		int rc = sqlite3_close(m_db);
		if (rc != SQLITE_OK)
		{
			//TODO:
		}
		m_db = nullptr;
	}
}

int CDbxSQLite::Create(const wchar_t *profile)
{
	sqlite3 *database = nullptr;
	ptrA path(mir_utf8encodeW(profile));
	int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
	if (rc != SQLITE_OK)
		return 1;

	rc = sqlite3_exec(database, "create table contacts (id integer not null primary key autoincrement);", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "create table events (id integer not null primary key autoincrement, contact_id integer not null, module text not null,"
		"timestamp integer not null, type integer not null, flags integer not null, data blob, server_id text);", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "create index idx_events_contactid_timestamp on events(contact_id, timestamp);", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "create index idx_events_module_serverid on events(module, server_id);", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
    rc = sqlite3_exec(database, "create table events_srt (id integer not null, contact_id integer not null, timestamp integer, primary key (contact_id, timestamp, id));",
		nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "create table settings (contact_id integer not null, module text not null, setting text not null, type integer not null, value any,"
		"primary key(contact_id, module, setting)) without rowid;", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "create index idx_settings_module on settings(module);", nullptr, nullptr, nullptr);
	if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}

	sqlite3_close(database);

	return 0;
}

int CDbxSQLite::Check(const wchar_t *profile)
{
	FILE *hFile = _wfopen(profile, L"rb");

	if (hFile == INVALID_HANDLE_VALUE)
		return EGROKPRF_CANTREAD;

	char header[16] = {};
	size_t size = sizeof(header);

	if (fread(header, sizeof(char), size, hFile) != size) {
		fclose(hFile);
		return EGROKPRF_CANTREAD;
	}

	fclose(hFile);

	if (memcmp(header, SQLITE_HEADER_STR, mir_strlen(SQLITE_HEADER_STR)) != 0)
		return EGROKPRF_UNKHEADER;

	sqlite3 *database = nullptr;
	ptrA path(mir_utf8encodeW(profile));
	int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_READONLY, nullptr);
	if (rc != SQLITE_OK)
		return EGROKPRF_DAMAGED;

	sqlite3_close(database);

	return EGROKPRF_NOERROR;
}

MDatabaseCommon* CDbxSQLite::Load(const wchar_t *profile, int readonly)
{
	sqlite3 *database = nullptr;
	ptrA path(mir_utf8encodeW(profile));
	int flags = SQLITE_OPEN_READWRITE;
	if (readonly)
		flags |= SQLITE_OPEN_READONLY;
	int rc = sqlite3_open_v2(path, &database, flags, nullptr);
	if (rc != SQLITE_OK)
		return nullptr;

	rc = sqlite3_exec(database, "begin transaction;", nullptr, nullptr, nullptr);
    if (rc != SQLITE_OK)
	{
		//TODO: handle error
	}
	rc = sqlite3_exec(database, "pragma locking_mode = EXCLUSIVE;", nullptr, nullptr, nullptr);
    if (rc != SQLITE_OK) {
      // TODO: handle error
    }
    rc = sqlite3_exec(database, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr);
    if (rc != SQLITE_OK) {
      // TODO: handle error
    }
    sqlite3_exec(database, "pragma foreign_keys = OFF;", nullptr, nullptr, nullptr);
	rc = sqlite3_exec(database, "pragma journal_mode = OFF;", nullptr, nullptr, nullptr);
    if (rc != SQLITE_OK) {
      // TODO: handle error
    }
    rc = sqlite3_exec(database, "commit;", nullptr, nullptr, nullptr);
    if (rc != SQLITE_OK) {
      // TODO: handle error
    }


	CDbxSQLite *db = new CDbxSQLite(database);
	db->InitSettings();
	db->InitContacts();
	db->InitEvents();
	return db;
}

BOOL CDbxSQLite::Compact()
{
	sqlite3_exec(m_db, "pragma optimize;", nullptr, nullptr, nullptr);
	sqlite3_exec(m_db, "vacuum;", nullptr, nullptr, nullptr);
	return 0;
}

BOOL CDbxSQLite::Backup(LPCWSTR profile)
{
	sqlite3 *database = nullptr;
	ptrA path(mir_utf8encodeW(profile));
	int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
	if (rc != SQLITE_OK)
		return FALSE;

	mir_cslock lock(m_csDbAccess);

	sqlite3_backup *backup = sqlite3_backup_init(database, "main", m_db, "main");
	if (backup) {
		sqlite3_backup_step(backup, -1);
		sqlite3_backup_finish(backup);
	}
	sqlite3_close(database);

	return 0;
}

BOOL CDbxSQLite::IsRelational()
{
	return TRUE;
}

void CDbxSQLite::SetCacheSafetyMode(BOOL value)
{
	// hack to increase import speed
	if (!value)
		sqlite3_exec(m_db, "pragma synchronous = OFF;", nullptr, nullptr, nullptr);
	else
		sqlite3_exec(m_db, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr);
	m_safetyMode = value != FALSE;
}