// db_mssql.cpp : Defines the entry point for the DLL application.
//

//#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "db_mysql.h"

#include "mysql\include\mysql.h"

#pragma comment(lib, "mysql/lib/libmySQL.lib")

MYSQL *g_conn, g_mysql;
BOOL	bInited;

BOOL Init()
{
#ifdef _DEBUG
	const char* version_str = mysql_get_client_info();
	char buf[200];
	sprintf(buf, "mysql client version: %s\n", version_str);
	OutputDebugString(buf);
#endif

	g_conn = mysql_init(&g_mysql);
    if (g_conn) return TRUE;		
    return FALSE;
}

BOOL ParseConnStr(char* conn_str, char* host, char* user, char* passwd, char* db)
{
	// in this example, i use the format as "host:user:passwd:db", and you can define yours as you like
	char* p;
	char* q;
	
	if (!conn_str || !host || !user || !passwd || !db) return FALSE;
	
	q = p = conn_str;
	while (*p && *p != ':') p++;
	if (!*p) return FALSE;
	strncpy(host, q, p - q);
	host[p-q] = '\0';
	
	q = ++p;
	while (*p && *p != ':') p++;
	if (!*p) return FALSE;
	strncpy(user, q, p - q);
	user[p-q] = '\0';

	q = ++p;
	while (*p && *p != ':') p++;
	if (!*p) return FALSE;
	strncpy(passwd, q, p - q);
	passwd[p-q] = '\0';

	q = ++p;
	while (*p && *p != ':') p++;
	strncpy(db, q, p - q);
	db[p-q] = '\0';
	
	return TRUE;
}

/* get the hex string presentation of a binary
    dst     :   hex string
    data    :   binary data
    size    :   size of data
    !!make sure dst's size >= size*2+1
*/
char* ToHexString(char* dst, const unsigned char* data, size_t size)
{
	char pBuf[4];
    size_t i;

    if (!dst) return NULL;

	memset( dst, 0, sizeof( char ) * (size * 2 + 1) );
	for(i=0; i<size; i++ )
	{
		memset( pBuf, 0, sizeof( char ) * 4 );
		sprintf( pBuf, "%02x", (unsigned char)data[i] );
		strcat(dst, pBuf);
	}
	return dst;
}



BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		bInited = Init();
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
    return TRUE;
}

DLL_EXPORT_API char* DLL_CALL_TYPE db_get_plugin_interface_version()
{
	return "0.3";
}

DLL_EXPORT_API BOOL DLL_CALL_TYPE db_connect(LPSTR conn_str)
{
	char host[32];
	char user[32];
	char passwd[32];
	char db[32];
	ParseConnStr(conn_str, host, user, passwd, db);
	g_conn = mysql_real_connect(&(g_mysql), host, user, passwd, db, 0, NULL, 0);
    if (g_conn) return TRUE;
    return FALSE;
}

DLL_EXPORT_API BOOL DLL_CALL_TYPE db_update(const unsigned char* info_hash, int completed_num, int peer_num, int seeder_num)
// BCTracker call this function to store record of one hashinfo into database.
{
    MYSQL_RES	* res ;
	char hash_string[64];
	char sql[512];
	char sqlf_select[] = "SELECT InfoHash FROM TrackerStatus WHERE InfoHash='%s'";
	char sqlf_insert[] = "INSERT INTO TrackerStatus (InfoHash, CompletedNum, PeerNum, SeederNum) VALUES ('%s', %d, %d, %d)";
	char sqlf_update[] = "UPDATE TrackerStatus SET CompletedNum=CompletedNum+%d, PeerNum=%d, SeederNum=%d WHERE InfoHash='%s'";

	if ((info_hash == NULL) || (completed_num < 0) || (peer_num < 0) || (seeder_num < 0) )
	{
		return FALSE;
	}

    if (!bInited) return FALSE;	
    
    ToHexString(hash_string, info_hash, 20);

 	sprintf(sql, sqlf_select, hash_string);
    if (mysql_real_query(&g_mysql, sql, strlen(sql)) != 0) {
    	return FALSE;
    }
        
    res = mysql_store_result( &g_mysql );
    if (mysql_num_rows( res ) > 0) {
    	sprintf(sql, sqlf_update, completed_num, peer_num, seeder_num, hash_string);
    }else {
    	sprintf(sql, sqlf_insert, hash_string, completed_num, peer_num, seeder_num);
    }
	mysql_free_result( res ) ;
    
    if ( mysql_real_query(&g_mysql, sql, strlen(sql)) != 0) {
		return FALSE;
    }
    
    return TRUE;
}

DLL_EXPORT_API BOOL DLL_CALL_TYPE db_disconnect()
{
	if (g_conn) {
        mysql_close(g_conn);
        g_conn = NULL;
	}
    return TRUE;
}

DLL_EXPORT_API int DLL_CALL_TYPE db_fetch(info_list_t** add_list_head, BOOL need_auth)
// BCTracker call this function once at startup to fetch all records of hashinfo from database.
// The singly linked list used to pass records is created by this funcion, and released by BCTracker.
// return value: number of records if succeed, -1 if failed.
{
	char sql[256];
	info_list_t* p;
	int n = 0;

	*add_list_head = NULL;

	if(need_auth)
		strcpy(sql, "select HashInfo from metatable where Auditing=1 and localTF=1");
	else
		strcpy(sql, "select HashInfo from metatable where localTF=1");

	if ( ! mysql_real_query( &g_mysql, sql, strlen(sql)) ) 
	{
		MYSQL_RES	* res ;
		MYSQL_ROW	row ;
		n = 0;
		*add_list_head = NULL;

		res = mysql_store_result( &g_mysql ) ;
		if( mysql_num_fields( res ) == 0)
		{
			mysql_free_result( res ) ;
			return -1;
		}

		while ( row = mysql_fetch_row( res ) ) 
		{
			int field_index = 0;
			if (row[field_index] != NULL && strlen(row[field_index]) == 40) 
			{
				p = (info_list_t*) new info_list_t;
				if(p == NULL)
				{
					mysql_free_result( res ) ;
					return -1;
				}
				memcpy(p->info, row[field_index], 40);
				p->next = *add_list_head;
				*add_list_head = p;
				n++;
			}
		}
		mysql_free_result( res ) ;
	}
	else 
	{
		return -1;
	}

	return n;

}


DLL_EXPORT_API int DLL_CALL_TYPE db_fetch2(info_list_t** add_list_head, info_list_t** delete_list_head)
// BCTracker call this function periodically after db_fetch() to fetch changed records of hashinfo from database.
// The singly linked list used to pass records is created by this funcion, and released by BCTracker.
// return value: number of records if succeed, -1 if failed.
{
	char sql[256];
	info_list_t* p;
	int n = 0;

	if(add_list_head == NULL || delete_list_head == NULL)
		return -1;

	*add_list_head = NULL;
	*delete_list_head = NULL;

	strcpy(sql, "select HashInfo,isAdd from HashInfoTemp");

	if ( ! mysql_real_query( &g_mysql, sql, strlen(sql)) ) 
	{
		MYSQL_RES	* res ;
		MYSQL_ROW	row ;
		n = 0;
		*add_list_head = NULL;
		*delete_list_head = NULL;

		res = mysql_store_result( &g_mysql ) ;
		if( mysql_num_fields( res ) != 2)
		{
			mysql_free_result( res ) ;
			return -1;
		}

		while ( row = mysql_fetch_row( res ) ) 
		{
			if (row[0] != NULL && strlen(row[0]) == 40) 
			{
				if (row[1] != NULL) 
				{
					p = (info_list_t*) new info_list_t;
					if(p == NULL)
					{
						mysql_free_result( res ) ;
						return -1;
					}

					memcpy(p->info, row[0], 40); // HashInfo

					if( *row[1]) // isAdd
					{
						p->next = *add_list_head;
						*add_list_head = p;
					}
					else
					{
						p->next = *delete_list_head;
						*delete_list_head = p;
					}
					n++;
				}
			}
		}
		mysql_free_result( res ) ;
	}
	else 
	{
		return -1;
	}

	strcpy(sql, "delete from HashInfoTemp");
	mysql_real_query( &g_mysql, sql, strlen(sql));

	return n;
}
