/*
 **	Database.cpp
 **
 **	Published / author: 2005-08-12 / grymse@alhem.net
 **/

/*
Copyright (C) 2001-2006  Anders Hedstrom

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 <stdio.h>
#ifdef _WIN32
#pragma warning(disable:4786)
#endif

#include <string>
//#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include <stdarg.h>

#include "IError.h"
#include "Database.h"


#ifdef SQLITEW_NAMESPACE
namespace SQLITEW_NAMESPACE {
#endif


sqlDatabase::sqlDatabase(const char* d,IError *e)
:database(d)
,m_errhandler(e)
,m_embedded(true)
,m_mutex(m_mutex)
,m_b_use_mutex(false)
{

	int i;
	for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){
		m_opendbs[i] = NULL;
	}

}


sqlDatabase::sqlDatabase(Mutex& m,const char* d,IError *e)
:database(d)
,m_errhandler(e)
,m_embedded(true)
,m_mutex(m)
,m_b_use_mutex(true)
{
	int i;
	for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){
		m_opendbs[i] = NULL;
	}

}


sqlDatabase::~sqlDatabase()
{
	int i;
	for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){
		if (m_opendbs[i] != NULL){
			sqlite3_close(m_opendbs[i]-> db);
		}
	}

	for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){
		if (m_opendbs[i] != NULL){
			if (m_opendbs[i] -> busy){
				error("destroying Database object before Query object");
			}
			delete m_opendbs[i];
			m_opendbs[i] = NULL;
		}
	}

}


void sqlDatabase::RegErrHandler(IError *p)
{
	m_errhandler = p;
}


sqlDatabase::OPENDB *sqlDatabase::grabdb()
{
	Lock lck(m_mutex, m_b_use_mutex);
	OPENDB *odb = NULL;
	int i;

	for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){ // check whether there is un-busy odb
		if (m_opendbs[i] != NULL){
			if (! m_opendbs[i]->busy){
				odb = m_opendbs[i];
				break;
			}
		}
	}
	if (odb) {
		odb -> busy = true;
	}
	else { // check empty *odb slot
		for (i=0; i< MAXIMUM_DB_CONNECTIONS; i++){ // check whether there is free odb
			if (m_opendbs[i] == NULL){
				odb = new OPENDB;
				if (!odb)
				{
					error("grabdb: OPENDB struct couldn't be created");
					return NULL;
				}
				int rc = sqlite3_open(database, &odb -> db);
				if (rc)
				{
					error("Can't open database: %s\n", sqlite3_errmsg(odb -> db));
					sqlite3_close(odb -> db);
					delete odb;
					return NULL;
				}
				odb -> busy = true;
				m_opendbs[i] = odb;
				break;
			}
		}
	}
	return odb;
}


void sqlDatabase::freedb(sqlDatabase::OPENDB *odb)
{
	Lock lck(m_mutex, m_b_use_mutex);
	if (odb)
	{
		odb -> busy = false;
	}
}


void sqlDatabase::error(const char *format, ...)
{
	if (m_errhandler)
	{
		va_list ap;
		char errstr[5000];
		va_start(ap, format);
#ifdef WIN32
		vsprintf(errstr, format, ap);
#else
		vsnprintf(errstr, 5000, format, ap);
#endif
		va_end(ap);
		m_errhandler -> error(*this, errstr);
	}
}


void sqlDatabase::error(Query& q,const char *format, ...)
{
	if (m_errhandler)
	{
		va_list ap;
		char errstr[5000];
		va_start(ap, format);
#ifdef WIN32
		vsprintf(errstr, format, ap);
#else
		vsnprintf(errstr, 5000, format, ap);
#endif
		va_end(ap);
		m_errhandler -> error(*this, q, errstr);
	}
}


void sqlDatabase::error1(Query& q,const char* msg)
{
	if (m_errhandler)
	{
		m_errhandler -> error(*this, q, msg);
	}
}


bool sqlDatabase::Connected()
{
	OPENDB *odb = grabdb();
	if (!odb)
	{
		return false;
	}
	freedb(odb);
	return true;
}


sqlDatabase::Lock::Lock(Mutex& mutex,bool use) : m_mutex(mutex),m_b_use(use) 
{
	if (m_b_use) 
	{
		m_mutex.Lock();
	}
}


sqlDatabase::Lock::~Lock() 
{
	if (m_b_use) 
	{
		m_mutex.Unlock();
	}
}


sqlDatabase::Mutex::Mutex()
{
#ifdef _WIN32
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
	pthread_mutex_init(&m_mutex, NULL);
#endif
}


sqlDatabase::Mutex::~Mutex()
{
#ifdef _WIN32
	::CloseHandle(m_mutex);
#else
	pthread_mutex_destroy(&m_mutex);
#endif
}


void sqlDatabase::Mutex::Lock()
{
#ifdef _WIN32
	DWORD d = WaitForSingleObject(m_mutex, INFINITE);
	// %! check 'd' for result
#else
	pthread_mutex_lock(&m_mutex);
#endif
}


void sqlDatabase::Mutex::Unlock()
{
#ifdef _WIN32
	::ReleaseMutex(m_mutex);
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}

/*
char* sqlDatabase::xmlsafestr(const char* str)
{
	std::string str2;
	for (size_t i = 0; i < str.size(); i++)
	{
		switch (str[i])
		{
		case '&':
			str2 += "&amp;";
			break;
		case '<':
			str2 += "&lt;";
			break;
		case '>':
			str2 += "&gt;";
			break;
		case '"':
			str2 += "&quot;";
			break;
		case '\'':
			str2 += "&apos;";
			break;
		default:
			str2 += str[i];
		}
	}
	return str2;
}

*/
/*
int64_t sqlDatabase::a2bigint(const char* str)
{
	int64_t val = 0;
	bool sign = false;
	size_t i = 0;
	if (str[i] == '-')
	{
		sign = true;
		i++;
	}
	for (; i < strlen(str); i++)
	{
		val = val * 10 + (str[i] - 48);
	}
	return sign ? -val : val;
}


uint64_t sqlDatabase::a2ubigint(const char* str)
{
	uint64_t val = 0;
	for (size_t i = 0; i < strlen(str); i++)
	{
		val = val * 10 + (str[i] - 48);
	}
	return val;
}

*/
#ifdef SQLITEW_NAMESPACE
} // namespace SQLITEW_NAMESPACE {
#endif

