/*
 * libopensync - A synchronization framework
 * Copyright (C) 2004-2005  Armin Bauer <armin.bauer@opensync.org>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 * 
 */
 
#include "opensync.h"
#include "opensync_internals.h"

void osync_db_trace(void *data, const char *query)
{
	osync_trace(TRACE_INTERNAL, "query executed: %s", query);
}

OSyncDB *osync_db_open(char *filename, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%s, %p)", __func__, filename, error);
	
	OSyncDB *db = osync_try_malloc0(sizeof(OSyncDB), error);
	if (!db)
		goto error;
		
	int rc = sqlite3_open(filename, &(db->db));
	if (rc) {
		osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot open database: %s", sqlite3_errmsg(db->db));
		goto error_close;
	}
	sqlite3_trace(db->db, osync_db_trace, NULL);
	
	osync_trace(TRACE_EXIT, "%s: %p", __func__, db);
	return db;

error_close:
	osync_db_close(db);
//error_free:
	g_free(db);
error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return NULL;
}

void osync_db_close(OSyncDB *db)
{
	osync_trace(TRACE_ENTRY, "%s(%p)", __func__, db);
	
	int ret = sqlite3_close(db->db);
	if (ret != SQLITE_OK)
		osync_trace(TRACE_INTERNAL, "Can't close database: %s", sqlite3_errmsg(db->db));
	
	osync_trace(TRACE_EXIT, "%s", __func__);
}

int osync_db_count(OSyncDB *db, char *query)
{
	int ret = 0;

	sqlite3_stmt *ppStmt = NULL;
	if (sqlite3_prepare(db->db, query, -1, &ppStmt, NULL) != SQLITE_OK)
		osync_debug("OSDB", 3, "Unable prepare count! %s", sqlite3_errmsg(db->db));
	ret = sqlite3_step(ppStmt);
	if (ret != SQLITE_DONE && ret != SQLITE_ROW)
		osync_debug("OSDB", 3, "Unable step count! %s", sqlite3_errmsg(db->db));
	if (ret == SQLITE_DONE)
		osync_debug("OSDB", 3, "No row found!");
	ret = sqlite3_column_int64(ppStmt, 0);
	sqlite3_finalize(ppStmt);
	return ret;
}

OSyncDB *_open_changelog(OSyncGroup *group, OSyncError **error)
{
	g_assert(group);
	OSyncDB *log_db;

	char *filename = g_strdup_printf("%s/changelog.db", group->configdir);
	if (!(log_db = osync_db_open(filename, error))) {
		osync_error_update(error, "Unable to load changelog: %s", osync_error_print(error));
		g_free(filename);
		osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
		return NULL;
	}
	osync_debug("OSDB", 3, "Preparing to changelog from file %s", filename);
	g_free(filename);
	
	sqlite3 *sdb = log_db->db;
	
	if (sqlite3_exec(sdb, "CREATE TABLE tbl_log (uid VARCHAR, objtype VARCHAR, memberid INTEGER, changetype INTEGER)", NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 2, "Unable create log table! %s", sqlite3_errmsg(sdb));
	return log_db;
}

osync_bool osync_db_open_changelog(OSyncGroup *group, char ***uids, char ***objtype, long long int **memberids, int **changetypes, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, group, uids, changetypes, error);
	
	OSyncDB *log_db = _open_changelog(group, error);
	if (!log_db) {
		goto error;
	}
	sqlite3 *sdb = log_db->db;
	
	int count = osync_db_count(log_db, "SELECT count(*) FROM tbl_log");

	*uids = g_malloc0(sizeof(char *) * (count + 1));
	*objtype = g_malloc0(sizeof(char *) * (count + 1));
	*memberids = g_malloc0(sizeof(long long int) * (count + 1));
	*changetypes = g_malloc0(sizeof(int) * (count + 1));

	sqlite3_stmt *ppStmt = NULL;
	sqlite3_prepare(sdb, "SELECT uid, objtype, memberid, changetype FROM tbl_log", -1, &ppStmt, NULL);
	int i = 0;
	while (sqlite3_step(ppStmt) == SQLITE_ROW) {
		(*uids)[i] = g_strdup((gchar*)sqlite3_column_text(ppStmt, 0));
		(*objtype)[i] = g_strdup((gchar*)sqlite3_column_text(ppStmt, 1));
		(*memberids)[i] = sqlite3_column_int64(ppStmt, 2);
		(*changetypes)[i] = sqlite3_column_int(ppStmt, 3);
		i++;
	}
	(*uids)[i] = NULL;
	(*objtype)[i] = NULL;
	(*memberids)[i] = 0;
	(*changetypes)[i] = 0;
	sqlite3_finalize(ppStmt);

	char *query = g_strdup_printf("DELETE FROM tbl_log");
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to remove all logs! %s", sqlite3_errmsg(sdb));
		g_free(query);
		goto error_db_close;
	}
	g_free(query);
	
	osync_db_close(log_db);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_db_close:
	osync_db_close(log_db);

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}

osync_bool osync_db_save_changelog(OSyncGroup *group, OSyncChange *change, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, group, change, error);
	
	OSyncDB *log_db = _open_changelog(group, error);
	if (!log_db) {
		goto error;
	}
	sqlite3 *sdb = log_db->db;
	
	char *escaped_uid = osync_db_sql_escape(change->uid);
	char *query = g_strdup_printf("INSERT INTO tbl_log (uid, objtype, memberid, changetype) VALUES('%s', '%s', '%lli', '%i')", escaped_uid, osync_change_get_objtype(change)->name, change->member->id, change->changetype);
	g_free(escaped_uid);
	
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to insert log! %s", sqlite3_errmsg(sdb));
		g_free(query);
		goto error_db_close;
	}
	g_free(query);
	
	osync_db_close(log_db);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_db_close:
	osync_db_close(log_db);

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;

}

osync_bool osync_db_remove_changelog(OSyncGroup *group, OSyncChange *change, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, group, change, error);
	
	OSyncDB *log_db = _open_changelog(group, error);
	if (!log_db) {
		goto error;
	}
	sqlite3 *sdb = log_db->db;
	
	char *escaped_uid = osync_db_sql_escape(change->uid);
	char *query = g_strdup_printf("DELETE FROM tbl_log WHERE uid='%s' AND memberid='%lli' AND objtype='%s'", 
	escaped_uid, change->member->id, osync_change_get_objtype(change)->name);
	g_free(escaped_uid);
	
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to remove log! %s", sqlite3_errmsg(sdb));
		g_free(query);
		goto error_db_close;
	}
	g_free(query);
	
	osync_db_close(log_db);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_db_close:
	osync_db_close(log_db);	

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}

osync_bool osync_db_open_changes(OSyncGroup *group, OSyncChange ***changes, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, group, changes, error);
	g_assert(group);

	group->changes_path = g_strdup(group->configdir);
	char *filename = g_strdup_printf("%s/change.db", group->changes_path);
	if (!(group->changes_db = osync_db_open(filename, error))) {
		osync_error_update(error, "Unable to load changes: %s", osync_error_print(error));
		goto error;
	}
	osync_debug("OSDB", 3, "Preparing to load changes from file %s", filename);
	g_free(filename);
	
	sqlite3 *sdb = group->changes_db->db;
	
	if (sqlite3_exec(sdb, "CREATE TABLE tbl_changes (id INTEGER PRIMARY KEY, uid VARCHAR, objtype VARCHAR, format VARCHAR, memberid INTEGER, mappingid INTEGER)", NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 2, "Unable create changes table! %s", sqlite3_errmsg(sdb));
	
	int count = osync_db_count(group->changes_db, "SELECT count(*) FROM tbl_changes");
	*changes = g_malloc0(sizeof(OSyncChange *) * (count + 1));
	
	sqlite3_stmt *ppStmt = NULL;
	sqlite3_prepare(sdb, "SELECT id, uid, objtype, format, memberid, mappingid FROM tbl_changes ORDER BY mappingid", -1, &ppStmt, NULL);
	int i = 0;
	while (sqlite3_step(ppStmt) == SQLITE_ROW) {
		OSyncChange *change = osync_change_new();
		change->id = sqlite3_column_int64(ppStmt, 0);
		change->uid = g_strdup((gchar*)sqlite3_column_text(ppStmt, 1));
		change->objtype_name = g_strdup((gchar*)sqlite3_column_text(ppStmt, 2));
		change->format_name = g_strdup((gchar*)sqlite3_column_text(ppStmt, 3));
		change->initial_format_name = g_strdup(change->format_name);
		change->mappingid = sqlite3_column_int64(ppStmt, 5);
		long long int memberid = sqlite3_column_int64(ppStmt, 4);
		change->changes_db = group->changes_db;
    	osync_change_set_member(change, osync_member_from_id(group, memberid));
    		osync_trace(TRACE_INTERNAL, "Loaded change with uid %s, changetype %i, data %p, size %i, objtype %s and format %s from member %lli", osync_change_get_uid(change), osync_change_get_changetype(change), osync_change_get_data(change), osync_change_get_datasize(change), osync_change_get_objtype(change) ? osync_objtype_get_name(osync_change_get_objtype(change)) : "None", osync_change_get_objformat(change) ? osync_objformat_get_name(osync_change_get_objformat(change)) : "None", memberid);
		(*changes)[i] = change;
		i++;
	}
	(*changes)[i] = NULL;
	sqlite3_finalize(ppStmt);
	
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}

osync_bool osync_db_save_change(OSyncChange *change, osync_bool save_format, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %i, %p) (Table: %p)", __func__, change, save_format, error, change->changes_db);
	osync_assert_msg(change, "Need to set change");
	osync_assert_msg(osync_change_get_objtype(change), "change->objtype was NULL while saving");
	osync_assert_msg(osync_change_get_objformat(change), "change->objformat was NULL while saving");
	
	if (!change || !change->changes_db) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "osync_db_save_change was called with wrong parameters");
		goto error;
	}
	sqlite3 *sdb = change->changes_db->db;
	
	char *query = NULL;
	if (!change->id) {
		char *escaped_uid = osync_db_sql_escape(change->uid);
		query = g_strdup_printf("INSERT INTO tbl_changes (uid, objtype, format, memberid, mappingid) VALUES('%s', '%s', '%s', '%lli', '%lli')", escaped_uid, osync_change_get_objtype(change)->name, osync_change_get_objformat(change)->name, change->member->id, change->mappingid);
		g_free(escaped_uid);
		
		if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
			osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to insert change! %s", sqlite3_errmsg(sdb));
			g_free(query);
			goto error;
		}
		change->id = sqlite3_last_insert_rowid(sdb);
	} else {
		char *escaped_uid = osync_db_sql_escape(change->uid);
		if (save_format)
			query = g_strdup_printf("UPDATE tbl_changes SET uid='%s', objtype='%s', format='%s', memberid='%lli', mappingid='%lli' WHERE id=%lli", escaped_uid, osync_change_get_objtype(change)->name, osync_change_get_objformat(change)->name, change->member->id, change->mappingid, change->id);
		else
			query = g_strdup_printf("UPDATE tbl_changes SET uid='%s', memberid='%lli', mappingid='%lli' WHERE id=%lli", escaped_uid, change->member->id, change->mappingid, change->id);
		g_free(escaped_uid);
		
		if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
			osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to update change! %s", sqlite3_errmsg(sdb));
			g_free(query);
			goto error;
		}
	}
	g_free(query);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;

}

osync_bool osync_db_delete_change(OSyncChange *change, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, change, error);
	
	if (!change || !change->changes_db) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "osync_db_delete_change was called with wrong parameters");
		goto error;
	}
	sqlite3 *sdb = change->changes_db->db;
	
	char *query = g_strdup_printf("DELETE FROM tbl_changes WHERE id=%lli", change->id);
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to delete change! %s", sqlite3_errmsg(sdb));
		g_free(query);
		goto error;
	}
	g_free(query);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:	
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}

osync_bool osync_db_reset_changes(OSyncGroup *group, const char *objtype, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, group, objtype, error);
	
	if (!group || !objtype) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "osync_db_reset_changes was called with wrong parameters");
		goto error;
	}
	sqlite3 *sdb = group->changes_db->db;

	char *query = NULL;
	if (osync_conv_objtype_is_any(objtype)) {
		query = g_strdup_printf("DELETE FROM tbl_changes");
	} else {
		query = g_strdup_printf("DELETE FROM tbl_changes WHERE objtype='%s'", objtype);
	}

	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to reset changes! %s", sqlite3_errmsg(sdb));
		g_free(query);
		goto error;
	}

	g_free(query);
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
	
}

osync_bool osync_db_reset_group(OSyncGroup *group, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, group, error);
	OSyncDB *db = NULL;
	
	if (!group) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "osync_db_reset_group was called with wrong parameters");
		goto error;
	}
	
	char *changesdb = g_strdup_printf("%s/change.db", group->configdir);
	if (!(db = osync_db_open(changesdb, error))) {
		g_free(changesdb);
		goto error;
	}

	if (sqlite3_exec(db->db, "DELETE FROM tbl_changes", NULL, NULL, NULL) != SQLITE_OK) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to reset changes! %s", sqlite3_errmsg(db->db));
		g_free(changesdb);
		goto error_db_close;
	}
	
	osync_db_close(db);
	g_free(changesdb);
	
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_db_close:	
	osync_db_close(db);

error:	
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}

osync_bool osync_db_reset_member(OSyncMember *member, OSyncError **error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, member, error);
	OSyncDB *db = NULL;
	
	if (!member) {
		osync_error_set(error, OSYNC_ERROR_PARAMETER, "osync_db_reset_member was called with wrong parameters");
		goto error;
	}
	
	char *hashtable = g_strdup_printf("%s/hash.db", member->configdir);
	if (g_file_test(hashtable, G_FILE_TEST_EXISTS)) {
		if (!(db = osync_db_open(hashtable, error))) {
			g_free(hashtable);
			goto error;
		}
	
		if (sqlite3_exec(db->db, "DELETE FROM tbl_hash", NULL, NULL, NULL) != SQLITE_OK) {
			osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to reset hashtable! %s", sqlite3_errmsg(db->db));
			g_free(hashtable);
			goto error_db_close;
		}
		
		osync_db_close(db);
	}
	g_free(hashtable);
	
	char *anchordb = g_strdup_printf("%s/anchor.db", member->configdir);
	if (g_file_test(anchordb, G_FILE_TEST_EXISTS)) {
		if (!(db = osync_db_open(anchordb, error))) {
			g_free(anchordb);
			goto error;
		}
	
		if (sqlite3_exec(db->db, "DELETE FROM tbl_anchor", NULL, NULL, NULL) != SQLITE_OK) {
			osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to reset anchors! %s", sqlite3_errmsg(db->db));
			g_free(anchordb);
			goto error_db_close;
		}
		
		osync_db_close(db);
	}
	g_free(anchordb);
	
	osync_trace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_db_close:	
	osync_db_close(db);

error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;

}

void osync_db_close_changes(OSyncGroup *group)
{
	if (group->changes_db)
		osync_db_close(group->changes_db);
}

OSyncDB *osync_db_open_anchor(OSyncMember *member, OSyncError **error)
{
	g_assert(member);
	OSyncDB *sdb = NULL;
	char *filename = g_strdup_printf ("%s/anchor.db", member->configdir);
	if (!(sdb = osync_db_open(filename, error))) {
		g_free(filename);
		osync_error_update(error, "Unable to open anchor table: %s", (*error)->message);
		return NULL;
	}
	g_free(filename);
	
	if (sqlite3_exec(sdb->db, "CREATE TABLE tbl_anchor (id INTEGER PRIMARY KEY, anchor VARCHAR, objtype VARCHAR UNIQUE)", NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 3, "Unable create anchor table! %s", sqlite3_errmsg(sdb->db));
	
	return sdb;
}

void osync_db_close_anchor(OSyncDB *db)
{
	osync_db_close(db);
}

void osync_db_get_anchor(OSyncDB *sdb, const char *objtype, char **retanchor)
{
	sqlite3_stmt *ppStmt = NULL;
	char *query = g_strdup_printf("SELECT anchor FROM tbl_anchor WHERE objtype='%s'", objtype);
	if (sqlite3_prepare(sdb->db, query, -1, &ppStmt, NULL) != SQLITE_OK)
		osync_debug("OSDB", 3, "Unable prepare anchor! %s", sqlite3_errmsg(sdb->db));
	int ret = sqlite3_step(ppStmt);
	if (ret != SQLITE_DONE && ret != SQLITE_ROW)
		osync_debug("OSDB", 3, "Unable step count! %s", sqlite3_errmsg(sdb->db));
	if (ret == SQLITE_DONE)
		osync_debug("OSDB", 3, "No row found!");
	*retanchor = g_strdup((gchar*)sqlite3_column_text(ppStmt, 0));
	sqlite3_finalize(ppStmt);
	g_free(query);
}

void osync_db_put_anchor(OSyncDB *sdb, const char *objtype, const char *anchor)
{
	char *escaped_anchor = osync_db_sql_escape(anchor);
	char *query = g_strdup_printf("REPLACE INTO tbl_anchor (objtype, anchor) VALUES('%s', '%s')", objtype, escaped_anchor);
	g_free(escaped_anchor);
	
	if (sqlite3_exec(sdb->db, query, NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 1, "Unable put anchor! %s", sqlite3_errmsg(sdb->db));

	g_free(query);
}


osync_bool osync_db_open_hashtable(OSyncHashTable *table, OSyncMember *member, OSyncError **error)
{
	g_assert(member);
	char *filename = g_strdup_printf ("%s/hash.db", member->configdir);
	if (!(table->dbhandle = osync_db_open(filename, error))) {
		g_free(filename);
		osync_error_update(error, "Unable to open hashtable: %s", (*error)->message);
		return FALSE;
	}
	g_free(filename);
	
	sqlite3 *db = table->dbhandle->db;
	
	if (sqlite3_exec(db, "CREATE TABLE tbl_hash (id INTEGER PRIMARY KEY, uid VARCHAR, hash VARCHAR, objtype VARCHAR)", NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 3, "Unable create hash table! %s", sqlite3_errmsg(db));
	
	return TRUE; //FIXME
}

void osync_db_close_hashtable(OSyncHashTable *table)
{
	osync_db_close(table->dbhandle);
}

int exists_hashtable_id(OSyncHashTable *table, const char *uid, const char *objtype)
{
	g_assert(table->dbhandle);
	sqlite3 *sdb = table->dbhandle->db;
	
	int id = -1;

	char *exist = g_strdup_printf("SELECT id FROM tbl_hash WHERE uid='%s' AND objtype='%s'",uid, objtype);

	sqlite3_stmt *ppStmt = NULL;
	if (sqlite3_prepare(sdb, exist, -1, &ppStmt, NULL) != SQLITE_OK)
	{
		osync_debug("OSDB", 3, "Unable prepare get id! %s", sqlite3_errmsg(sdb));
		return (id);
	}
	int ret = sqlite3_step(ppStmt);
	if (ret != SQLITE_DONE && ret != SQLITE_ROW)
		return (id);
	if (ret == SQLITE_DONE)
		return (id);
	
	id = sqlite3_column_int64(ppStmt, 0);
	sqlite3_finalize(ppStmt);
	g_free(exist);

	return(id);
}

void osync_db_save_hash(OSyncHashTable *table, const char *uid, const char *hash, const char *objtype)
{
	g_assert(table->dbhandle);
	sqlite3 *sdb = table->dbhandle->db;
	
	char *escaped_uid = osync_db_sql_escape(uid);
	char *escaped_hash = osync_db_sql_escape(hash);
	char *escaped_objtype = osync_db_sql_escape(objtype);
	int id = exists_hashtable_id(table, escaped_uid, escaped_objtype);
	char *query = NULL;
	if(id < 0){
		query = g_strdup_printf(
			"REPLACE INTO tbl_hash ('uid', 'hash', 'objtype') VALUES('%s', '%s', '%s')", 
			escaped_uid, escaped_hash, escaped_objtype);
	}else{
		query = g_strdup_printf(
			"REPLACE INTO tbl_hash ('id', 'uid', 'hash', 'objtype') VALUES('%i', '%s', '%s', '%s')", 
			id, escaped_uid, escaped_hash, escaped_objtype);
	}
	
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 1, "Unable to insert hash! uid = %s, hash = %s, error = %s", escaped_uid, escaped_hash, sqlite3_errmsg(sdb));
		
	g_free(escaped_uid);
	g_free(escaped_hash);
	g_free(escaped_objtype);
	g_free(query);
}


void osync_db_delete_hash(OSyncHashTable *table, const char *uid)
{
	g_assert(table->dbhandle);
	sqlite3 *sdb = table->dbhandle->db;
	
	char *escaped_uid = osync_db_sql_escape(uid);
	char *query = g_strdup_printf("DELETE FROM tbl_hash WHERE uid='%s'", escaped_uid);
	g_free(escaped_uid);

	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 1, "Unable to delete hash! %s", sqlite3_errmsg(sdb));
	g_free(query);
}

char **osync_db_get_deleted_hash(OSyncHashTable *table, const char *objtype)
{
	g_assert(table->dbhandle);
	sqlite3 *sdb = table->dbhandle->db;
	
	char **azResult = NULL;
	int numrows = 0;
	char *query = NULL;
	
	if (osync_conv_objtype_is_any(objtype)) {
		query = g_strdup_printf("SELECT uid, hash FROM tbl_hash");
	} else {
		query = g_strdup_printf("SELECT uid, hash FROM tbl_hash WHERE objtype='%s'", objtype);
	}
	sqlite3_get_table(sdb, query, &azResult, &numrows, NULL, NULL);
	g_free(query);
	
	char **ret = g_malloc0((numrows + 1) * sizeof(char *));
	
	int i;
	int ccell = 2;
	int num = 0;
	for (i = 0; i < numrows; i++) {
		char *uid = azResult[ccell];
		ccell += 2;
		
		if (!g_hash_table_lookup(table->used_entries, uid)) {
			ret[num] = g_strdup(uid);
			num++;
		}
	}
	sqlite3_free_table(azResult);
	return ret;
}	

void osync_db_get_hash(OSyncHashTable *table, const char *uid, const char *objtype, char **rethash)
{
	sqlite3 *sdb = table->dbhandle->db;
	sqlite3_stmt *ppStmt = NULL;
	char *escaped_uid = osync_db_sql_escape(uid);
	char *escaped_objtype = osync_db_sql_escape(objtype);
	char *query = g_strdup_printf("SELECT hash FROM tbl_hash WHERE uid='%s' AND objtype='%s'", escaped_uid, escaped_objtype);
	g_free(escaped_uid);
	g_free(escaped_objtype);

	if (sqlite3_prepare(sdb, query, -1, &ppStmt, NULL) != SQLITE_OK)
		osync_debug("OSDB", 3, "Unable prepare get hash! %s", sqlite3_errmsg(sdb));
	int ret = sqlite3_step(ppStmt);
	if (ret != SQLITE_DONE && ret != SQLITE_ROW)
		osync_debug("OSDB", 3, "Unable step get hash! %s", sqlite3_errmsg(sdb));
	if (ret == SQLITE_DONE)
		osync_debug("OSDB", 3, "No row found!");
	*rethash = g_strdup((gchar*)sqlite3_column_text(ppStmt, 0));
	sqlite3_finalize(ppStmt);
	g_free(query);
}

void osync_db_reset_hash(OSyncHashTable *table, const char *objtype)
{
	sqlite3 *sdb = table->dbhandle->db;
	char *query = NULL;
	if (osync_conv_objtype_is_any(objtype)) {
		query = g_strdup_printf("DELETE FROM tbl_hash");
	} else {
		query = g_strdup_printf("DELETE FROM tbl_hash WHERE objtype='%s'", objtype);
	}
	if (sqlite3_exec(sdb, query, NULL, NULL, NULL) != SQLITE_OK)
		osync_debug("OSDB", 1, "Unable to reset hash! %s", sqlite3_errmsg(sdb));
	g_free(query);
}

char *osync_db_sql_escape(const char *s)
{
	return osync_strreplace(s, "'", "''");
}



syntax highlighted by Code2HTML, v. 0.9.1