// mysql.cpp : 定义控制台应用程序的入口点。
//

#include "acl_cpp/lib_acl.hpp"

const char* CREATE_TBL =
	"create table group_tbl\r\n"
	"(\r\n"
	"group_name varchar(128) not null,\r\n"
	"uvip_tbl varchar(32) not null default 'uvip_tbl',\r\n"
	"access_tbl varchar(32) not null default 'access_tbl',\r\n"
	"access_week_tbl varchar(32) not null default 'access_week_tbl',\r\n"
	"access_month_tbl varchar(32) not null default 'access_month_tbl',\r\n"
	"update_date date not null default '1970-1-1',\r\n"
	"disable integer not null default 0,\r\n"
	"add_by_hand integer not null default 0,\r\n"
	"class_level integer not null default 0,\r\n"
	"primary key(group_name, class_level)\r\n"
	")";

static bool db_create(const char* dbaddr, const char* dbname,
	const char* dbuser, const char* dbpass)
{
	acl::db_mysql db(dbaddr, "mysql", dbuser, dbpass);
	if (db.open() == false)
	{
		printf("open %s@mysql error, dbuser: %s, dbpass: %s\r\n",
			dbaddr, dbuser, dbpass);
		return false;
	}

	printf("mysqlclient lib's version: %ld, info: %s\r\n",
		db.mysql_libversion(), db.mysql_client_info());

	acl::string sql;
	sql.format("use mysql");
	if (db.sql_update(sql.c_str()) == false)
	{
		printf("'%s' error: %s\r\n", sql.c_str(), db.get_error());
		return false;
	}
	db.free_result();

	sql.format("create database %s character set utf8;\r\n", dbname);
	if (db.sql_update(sql.c_str()) == false)
	{
		printf("'%s' error: %s\r\n", sql.c_str(), db.get_error());
		return false;
	}
	db.free_result();
	
	sql.format("grant CREATE,DROP,INSERT,DELETE,UPDATE,SELECT on %s.* to %s",
		dbname, dbuser);
	if (db.sql_update(sql.c_str()) == false)
	{
		printf("'%s' error: %s\r\n", sql.c_str(), db.get_error());
		return false;
	}
	db.free_result();
		
	sql = "flush privileges";
	if (db.sql_update(sql.c_str()) == false)
	{
		printf("'%s' error: %s\r\n", sql.c_str(), db.get_error());
		return false;
	}
	db.free_result();

	printf("create db(%s) ok\r\n", dbname);
	return true;
}

static bool tbl_create(const char* dbaddr, const char* dbname,
	const char* dbuser, const char* dbpass)
{
	acl::db_mysql db(dbaddr, dbname, dbuser, dbpass);
	if (db.open() == false)
	{
		printf("open %s@mysql error, dbuser: %s, dbpass: %s\r\n",
			dbaddr, dbuser, dbpass);
		return false;
	}

	printf("mysqlclient lib's version: %ld, info: %s\r\n",
		db.mysql_libversion(), db.mysql_client_info());

	if (db.tbl_exists("group_tbl"))
	{
		printf("table exist\r\n");
		return (true);
	}
	else if (db.sql_update(CREATE_TBL) == false)
	{
		printf("sql error\r\n");
		return (false);
	}
	else
	{
		printf("create table ok\r\n");
		return (true);
	}
}

// 添加表数据
static bool tbl_insert(acl::db_handle& db, int n)
{
	acl::query query;
	query.create_sql("insert into group_tbl(group_name, uvip_tbl,"
		" update_date) values(:group, :test, :date)")
		.set_format("group", "group:%d", n)
		.set_parameter("test", "test")
		.set_date("date", time(NULL), "%Y-%m-%d");

	if (db.exec_update(query) == false)
		return (false);

	const acl::db_rows* result = db.get_result();
	if (result)
	{
		const std::vector<acl::db_row*>& rows = result->get_rows();
		for (size_t i = 0; i < rows.size(); i++)
		{
			const acl::db_row* row = rows[i];
			for (size_t j = 0; j < row->length(); j++)
				printf("%s, ", (*row)[j]);
			printf("\r\n");
		}
	}

	db.free_result();
	return (true);
}

// 查询表数据
static int tbl_select(acl::db_handle& db, int n)
{
	acl::query query;
	query.create_sql("select * from group_tbl where group_name=:group"
		" and uvip_tbl=:test")
		.set_format("group", "group:%d", n)
		.set_format("test", "test");

	if (db.exec_select(query) == false)
	{
		printf("select sql error\r\n");
		return (-1);
	}

	printf("\r\n---------------------------------------------------\r\n");

	// 列出查询结果方法一
	const acl::db_rows* result = db.get_result();
	if (result)
	{
		const std::vector<acl::db_row*>& rows = result->get_rows();
		for (size_t i = 0; i < rows.size(); i++)
		{
			if (n > 100)
				continue;
			const acl::db_row* row = rows[i];
			for (size_t j = 0; j < row->length(); j++)
				printf("%s, ", (*row)[j]);
			printf("\r\n");
		}
	}

	// 列出查询结果方法二
	for (size_t i = 0; i < db.length(); i++)
	{
		if (n > 100)
			continue;
		const acl::db_row* row = db[i];

		// 取出该行记录中某个字段的值
		const char* ptr = (*row)["group_name"];
		if (ptr == NULL)
		{
			printf("error, no group name\r\n");
			continue;
		}
		printf("group_name=%s: ", ptr);
		for (size_t j = 0; j < row->length(); j++)
			printf("%s, ", (*row)[j]);
		printf("\r\n");
	}

	// 列出查询结果方法三
	const std::vector<acl::db_row*>* rows = db.get_rows();
	if (rows)
	{
		std::vector<acl::db_row*>::const_iterator cit = rows->begin();
		for (; cit != rows->end(); cit++)
		{
			if (n > 100)
				continue;
			const acl::db_row* row = *cit;
			for (size_t j = 0; j < row->length(); j++)
				printf("%s, ", (*row)[j]);
			printf("\r\n");
		}

	}
	int  ret = (int) db.length();

	// 释放查询结果
	db.free_result();
	return (ret);
}

// 删除表数据
static bool tbl_delete(acl::db_handle& db, int n)
{
	acl::query query;
	query.create_sql("delete from group_tbl where group_name=:group")
		.set_format("group", "group-%d", n);

	if (db.exec_update(query) == false)
	{
		printf("delete sql error\r\n");
		return (false);
	}

	for (size_t i = 0; i < db.length(); i++)
	{
		const acl::db_row* row = db[i];
		for (size_t j = 0; j < row->length(); j++)
			printf("%s, ", (*row)[j]);
		printf("\r\n");
	}
	// 释放查询结果
	db.free_result();

	return (true);
}

//////////////////////////////////////////////////////////////////////////////

class db_thread : public acl::thread
{
public:
	db_thread(acl::db_pool& dp, int max) : pool_(dp), max_(max) {}
	~db_thread() {}

protected:
	void* run()
	{
		int n = 0;

		for (int i = 0; i < max_; i++)
		{
			acl::db_handle* db = pool_.peek_open();
			if (db == NULL)
			{
				printf("peek db connection error\r\n");
				break;
			}

			bool ret = tbl_insert(*db, i);
			if (ret)
			{
				printf(">>insert ok: i=%d, affected: %d\r",
					i, db->affect_count());
				n++;
			}
			else
				printf(">>insert error: i = %d\r\n", i);

			pool_.put(db);
		}
		printf("\r\n");
		printf(">>insert total: %d\r\n", n);

		n = 0;
		// 批量查询数据
		for (int i = 0; i < max_; i++)
		{
			acl::db_handle* db = pool_.peek_open();
			if (db == NULL)
			{
				printf("peek db connection error\r\n");
				break;
			}

			int  ret = tbl_select(*db, i);
			if (ret >= 0)
			{
				n += ret;
				printf(">>select ok: i=%d, ret=%d\r", i, ret);
			}
			else
				printf(">>select error: i = %d\r\n", i);

			pool_.put(db);
		}
		printf("\r\n");
		printf(">>select total: %d\r\n", n);

		// 批量删除数据
		for (int i = 0; i < max_; i++)
		{
			acl::db_handle* db = pool_.peek_open();
			if (db == NULL)
			{
				printf("peek db connection error\r\n");
				break;
			}

			bool ret = tbl_delete(*db, i);
			if (ret)
				printf(">>delete ok: %d, affected: %d\r",
					i, (int) db->affect_count());
			else
				printf(">>delete error: i = %d\r\n", i);

			pool_.put(db);
		}
		printf("\r\n");

		return NULL;
	}

private:
	acl::db_pool& pool_;
	int   max_;
};

//////////////////////////////////////////////////////////////////////////////

int main(void)
{
	// WIN32 下需要调用此函数进行有关 SOCKET 的初始化
	acl::acl_cpp_init();

	// 允许将错误日志输出至屏幕
	acl::log::stdout_open(true);

	acl::string line;
	acl::stdin_stream in;
	acl::stdout_stream out;

#if	defined(_WIN32) || defined(_WIN64)
	const char* libname = "libmysql.dll";
#else
	const char* libname = "libmysqlclient_r.so";
#endif

	acl::string path;

	// 因为采用动态加载的方式，所以需要应用给出 mysql 客户端库所在的路径
	out.format("Enter %s load path: ", libname);
	if (in.gets(line) && !line.empty())
#if	defined(_WIN32) || defined(_WIN64)
		path.format("%s\\%s", line.c_str(), libname);
#else
		path.format("%s/%s", line.c_str(), libname);
#endif
	else
		path = libname;

	out.format("%s path: %s\r\n", libname, path.c_str());
	// 设置动态库加载的全路径
	acl::db_handle::set_loadpath(path);

	acl::string dbaddr("127.0.0.1:3306");
	acl::string dbname("acl_db"), dbuser("root"), dbpass;

	out.format("Enter dbaddr [default: %s]: ", dbaddr.c_str());
	if (in.gets(line) && !line.empty())
		dbaddr = line;

	out.format("Enter dbname [default: %s]: ", dbname.c_str());
	if (in.gets(line) && !line.empty())
		dbname = line;

	out.format("Enter dbuser [default: %s]: ", dbuser.c_str());
	if (in.gets(line) && !line.empty())
		dbuser = line;

	out.format("Enter dbpass [default: %s]: ", dbpass.c_str());
	if (in.gets(line) && !line.empty())
		dbpass = line;

	out.format("dbname: %s, dbuser: %s, dbpass: %s\r\n",
		dbname.c_str(), dbuser.c_str(), dbpass.c_str());

	// 如果需要创建数据库，则需要以 root 身份进行创建
	out.format("Do you want to create %s? yes|no: ", dbname.c_str());
	if (in.gets(line) && (line == "yes" || line == "y"))
	{
		if (dbuser != "root")
		{
			printf("dbuser must be root for create db\r\n");
			dbuser = "root";
		}

		// 创建数据库
		if (db_create(dbaddr, dbname, dbuser, dbpass) == false)
		{
			printf("create db failed, enter any key to exit.\r\n");
			getchar();
			return 1;
		}
		printf("create db %s ok, enter any key to continue\r\n",
			dbname.c_str());
		(void) in.gets(line);
	}

	// 当数据表不存在时创建表
	if (tbl_create(dbaddr, dbname, dbuser, dbpass) == false)
	{
		printf("create table error\r\n");
		getchar();
		return 1;
	}

	//////////////////////////////////////////////////////////////////////

	out.puts("Enter any key to continue ...");
	(void) in.gets(line);

	// 连接池中最大连接数量限制，该值应和线程池中的最大线程数相等，
	// 以保证每个线程都可以获得一个连接
	int  dblimit = 10;

	// 创建数据库连接池对象
	acl::mysql_conf dbconf(dbaddr, dbname);
	dbconf.set_dbuser(dbuser).set_dbpass(dbpass).set_dblimit(dblimit);
	acl::db_pool* dp = new acl::mysql_pool(dbconf);

	// 设置连接池中每个连接的空闲时间(秒)
	dp->set_idle(120);

	// 每个处理线程内部针对每个操作执行的次数
	int  max = 10;

	// 创建多个线程
	std::vector<db_thread*> threads;
	for (int i = 0; i < dblimit; i++)
	{
		db_thread* thread = new db_thread(*dp, max);
		threads.push_back(thread);
		thread->set_detachable(false);
		thread->start();
	}

	// 等待所有工作线程退出
	std::vector<db_thread*>::iterator it;
	for (it = threads.begin(); it != threads.end(); ++it)
	{
		(*it)->wait(NULL);
		delete *it;
	}

//#ifndef WIN32
//	mysql_server_end();
//	mysql_thread_end();
//#endif

	delete dp;
	printf("Enter any key to exit.\r\n");
	getchar();
	return 0;
}
