// Copyright (C) FIT VUT
// Petr Lampa <lampa@fit.vutbr.cz>
// $Id$
// vi:set ts=8 sts=8 sw=8:
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>
#include <map>

class Mysql_DB 
{
private:
	string _host;
	string _user;
	string _pwd;
	string _db;
	MYSQL *_conn;

	Mysql_DB(const Mysql_DB&);
	Mysql_DB& operator=(const Mysql_DB&);
public:
	Mysql_DB(const string &host, const string &db, const string &user, const string &pwd): _host(host), _user(user), _pwd(pwd), _db(db), _conn(NULL) { }
	MYSQL *conn() const { return _conn; }
	bool connect();
	bool commit() { return mysql_commit(_conn); }
	bool rollback() { return mysql_rollback(_conn); }
	const string error() const { return mysql_error(_conn); }
	string escape(const string &str) {
		char to[str.length()*2+1];
		mysql_real_escape_string(_conn, to, str.c_str(), str.length());
		return string(to);
	}
	~Mysql_DB() { if (_conn) mysql_close(_conn); }
};

bool Mysql_DB::connect()
{
	_conn = mysql_init(NULL);
	// mysql_options(_conn, ...);
	if (!mysql_real_connect(_conn, _host.c_str(), _user.c_str(), _pwd.c_str(), _db.c_str(), 0, NULL, 0)) {
		// cerr << "Failed to connect to database: " << mysql_error(_conn) << endl;
		return false;
	}
	return true;
}

class Query
{
private:
	Mysql_DB& _db;
	string _last;
	MYSQL_RES *_res;
	unsigned _ncols;
	typedef map<const string, unsigned> Nmap;
	Nmap _nmap;
	MYSQL_ROW _row;

	Query(const Query&);
	Query& operator=(const Query&);
public:
	Query(Mysql_DB &db): _db(db),_last(),_res(NULL),_ncols(0),_nmap(), _row() { }

	int affected_rows() {
		return mysql_affected_rows(_db.conn());
	}
	bool execute(const string &query) 
	{ 
		_last = query; 
		_nmap.clear();
		_res = NULL;
		_ncols = 0;
		if (_res) mysql_free_result(_res);
		if (mysql_query(_db.conn(), query.c_str())) {
			// cerr << "sql error: " << _db.error() << endl;
			return false;
		}
		if (mysql_field_count(_db.conn()) == 0) {	// no data
			return true;
		}
		_res = mysql_store_result(_db.conn());
		if (_res == NULL) {
			return false;
		}
		MYSQL_FIELD *f = mysql_fetch_field(_res);
		int i = 1;
		while (f) {
			if (f->name) _nmap[f->name] = i;
			f = mysql_fetch_field(_res);
			i++;
		}
		_ncols = i-1;
		return true;
	}
	bool fetch_row()
	{
		return (_row = mysql_fetch_row(_res));
	}
	const char *name(unsigned i)
	{
		if (i < mysql_num_fields(_res)) {
			MYSQL_FIELD *f = mysql_fetch_field_direct(_res, i);
			return f->name;
		}
		return NULL;
	}
	const char *operator[](unsigned i)
	{
		if (i < mysql_num_fields(_res)) return _row[i];
		return NULL;
	}
	const char *operator[](const char *name)
	{
		int i = _nmap[name]-1;
		if (i >= 0) return _row[i];
		return NULL;
	}
	long get_int(const char *name)
	{
		int i = _nmap[name]-1;
		if (i >= 0) {
			if (_row[i] == NULL) return 0;
			return atol(_row[i]);
		}
		return 0;
	}
	bool is_null(const char *name)
	{
		int i = _nmap[name]-1;
		if (i >= 0) return (_row[i] == NULL);
		return false;
	}
	void free_result()
	{
		if (_res) mysql_free_result(_res);
		_res = NULL;
	}
	~Query() { free_result(); _nmap.clear(); }
};
