/*
 **	Main.cpp
 **
 **	Published / author: 2006-03-29 / grymse@alhem.net
 **/
/*
Copyright (C) 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>
// MFC
#include <afxdb.h>
#include <sql.h>
// odbcw
#include <libodbcwrapped.h>
// db
#include "libodbc.h"
// Defines
#define C cmd[strlen(cmd) - 1]
#define C1 tmp[strlen(tmp) - 1]

#pragma warning(push, 3)
#include <map>
#pragma warning(pop)
#pragma warning(disable:4786)

// Forwards
void TimeStart();
void TimeStop();
void TimeShow( long q );
void cmd_select(Database& db, Query& q, const std::string& cmd);
void cmd_tables(Database& db, Query& q, const std::string& cmd);
void odbcdump(const std::string& dsn, const std::string& user, const std::string& auth, const std::string& catalog, const std::string& table_name);
void tabledump(Database& db,const std::string& cat,const std::string& sch,const std::string& tbl,sql::Odbctable& );
void fixstring(std::string& str);

// Fulingar
static DWORD t0;
static DWORD ts;


int main(int argc,char *argv[])
{
	if (argc < 2)
	{
		fprintf(stderr,"Usage: %s <data source> [user] [auth] [catalog]\n",*argv);
		fprintf(stderr," or: %s -dump <datasource> [-u user] [-a auth] [-d catalog] [<table>]\n",*argv);
		exit(-1);
	}
	std::string dsn;
	std::string user;
	std::string auth;
	std::string catalog;

	if (!strcmp(argv[1],"-dump"))
	{
		std::string table_name;
		for (int i = 1; i < argc; i++)
		{
			if (!strcmp(argv[i],"-dump") && i < argc - 1)
			{
				i++;
				dsn = argv[i];
			}
			else
			if (!strcmp(argv[i],"-u") && i < argc - 1)
			{
				i++;
				user = argv[i];
			}
			else
			if (!strcmp(argv[i],"-a") && i < argc - 1)
			{
				i++;
				auth = argv[i];
			}
			else
			if (!strcmp(argv[i],"-d") && i < argc - 1)
			{
				i++;
				catalog = argv[i];
			}
			else
			if (*argv[i] == '-')
			{
				fprintf(stderr,"%s: Unknown argument '%s'\n",*argv,argv[i]);
				exit(-1);
			}
			else
			{
				table_name = argv[i];
			}
		}
		printf("--\n");
		printf("-- Dump from data source '%s'\n",dsn.c_str());
		printf("--\n");
		printf("\n");

		odbcdump(dsn, user, auth, catalog, table_name);
		return 0;
	}

	dsn = argv[1];
	user = (argc > 2) ? argv[2] : "";
	auth = (argc > 3) ? argv[3] : "";
	catalog = (argc > 4) ? argv[4] : "";

	try
	{
		Database db(dsn.c_str(), user.c_str(), auth.c_str() );
		Query q(db);
		bool quit = false;
		char cmd[200];
		std::string query;

		printf("SQL> ");
		fflush(stdout);

		query = "";
		while (!quit)
		{
			fgets(cmd,200,stdin);

			while (strlen(cmd) && (C == 13 || C == 10 || C == ' ' || C == 9))
			{
				C = 0;
			}
			if (query == "" && !strnicmp(cmd,"quit",4))
			{
				quit = true;
			}
			else
			if (query == "" && !strnicmp(cmd,"tables",6) && (cmd[6] == 0 || cmd[6] == ' '))
			{
				cmd_tables(db, q, cmd);
				// prompt
				printf("SQL> ");
				fflush(stdout);
			}
			else
			if (strlen(cmd) && C != ';')
			{
				if (query.size())
					query += " ";
				query += cmd;
				// prompt
				printf("	 . ");
				fflush(stdout);
			}
			else
			{
				if (strlen(cmd))
				{
					C = 0;
					if (query.size())
						query += " ";
					query += cmd;
				}

				if (!strnicmp(query.c_str(),"select ",7))
				{
					cmd_select( db, q, query );
				}
				else
				if (!strnicmp(query.c_str(),"update ",7) ||
						!strnicmp(query.c_str(),"insert ",7) ||
						!strnicmp(query.c_str(),"delete ",7) )
				{
					TimeStart();
					q.execute( query.c_str() );
					TimeStop();
					TimeShow( q.num_rows() );
				}
				else
				{
					TimeStart();
					q.execute( query.c_str() );
					TimeStop();
					TimeShow( -1 );
				}
				if (0)
				{
					printf("Unknown/Unsupported command '%s'\n",query.c_str());
				}
				// reset query
				query = "";
				// prompt
				printf("SQL> ");
				fflush(stdout);
			}
		}
	}
	catch( const std::string& e )
	{
		printf("Exception: %s\n", e.c_str());
	}

	return 0;
}


void TimeStart()
{
	t0 = GetTickCount();
}


void TimeStop()
{
	ts = GetTickCount();
}


void TimeShow( long count )
{
	DWORD ticks = ts - t0;
	DWORD secs = ticks / 1000;
	DWORD milli = ticks % 1000;

	if (count < 0)
	{
		printf("No rows in set (%ld.%03ld sec)\n",secs,milli);
	}
	else
	{
		printf("%ld row%s in set (%ld.%03ld sec)\n",count,(count == 1) ? "" : "s",secs,milli);
	}
	printf("\n");
}


void cmd_select(Database& db, Query& q, const std::string& cmd)
{
	std::string name;
	std::string typ;
	long count = 0;
	int wid;
	int dec;
	int nul;
	int i;
	int qcols;
	size_t wids[100];
	char format[200];
	char slask[500];

	// execute statement

	TimeStart();
	q.get_result(cmd.c_str() );
	TimeStop();

	// get maximum widths

	for (i = 0; i < 100; i++)
		wids[i] = 0;

	qcols = q.num_cols();
	i = 0;
	while (q.getcol(name,typ,wid,dec,nul))
	{
		if (name.size() > wids[i])
			wids[i] = name.size();
		i++;
	}
	while (q.fetch_row())
	{
		std::string value;
		for (i = 0; i < qcols; i++)
		{
			value = q.getstr();
			fixstring( value );
			if (value.size() > wids[i])
				wids[i] = value.size();
		}
		count++;
	}
	q.free_result();

	// display results

	// title bar
	for (i = 0; i < qcols; i++)
	{
		*slask = 0;
		while ( (int)strlen(slask) < wids[i])
			strcat(slask,"-");
		printf("+-%s-",slask);
	}
	printf("+\n");

	// column names
	q.get_result( cmd.c_str() );
	qcols = q.num_cols();
	i = 0;
	while (q.getcol(name,typ,wid,dec,nul))
	{
		sprintf(format,"| %%%ds ",wids[i]);
		printf(format,name.c_str());
		i++;
	}
	printf("|\n");

	// middle line
	for (i = 0; i < qcols; i++)
	{
		*slask = 0;
		while ( (int)strlen(slask) < wids[i])
			strcat(slask,"-");
		printf("+-%s-",slask);
	}
	printf("+\n");

	// results
	while (q.fetch_row())
	{
		std::string value;
		for (i = 0; i < qcols; i++)
		{
			sprintf(format,"| %%%ds ",wids[i]);
			value = q.getstr();
			fixstring( value );
			printf(format,value.c_str());
		}
		printf("|\n");
	}
	q.free_result();

	// end line
	for (i = 0; i < qcols; i++)
	{
		*slask = 0;
		while ( (int)strlen(slask) < wids[i])
			strcat(slask,"-");
		printf("+-%s-",slask);
	}
	printf("+\n");

	TimeShow( count );
}


void cmd_tables(Database& db, Query& q, const std::string& cmd)
{
	char slask[10];

	q.get_result(SQLGET_TABLES);
	while (q.fetch_row())
	{
		sql::Odbctable tbl(&db,&q);

		// 0 cat
		// 1 sch
		// 2 table name
		strcpy(slask," ");
		if (*tbl.GetTable_cat()) //cat.size())
		{
			printf("%s%s",slask,tbl.GetTable_cat());
			strcpy(slask,".");
		}
		if (*tbl.GetTable_schem()) //sch.size())
		{
			printf("%s%s",slask,tbl.GetTable_schem());
			strcpy(slask,".");
		}
		printf("%s%s",slask,tbl.GetTable_name());
	}
	q.free_result();

	printf("\n");
}


void odbcdump(const std::string& dsn, const std::string& user, const std::string& auth, const std::string& catalog, const std::string& table_name)
{
	try
	{
		Database db(dsn.c_str(), user.c_str(), auth.c_str() );
		Query q(db);
		std::string cat;
		std::string sch;
		std::string tbl;

		q.get_result(SQLGET_TABLES);
		while (q.fetch_row())
		{
			sql::Odbctable table(&db,&q);

			cat = table.GetTable_cat(); //q.getstr();
			sch = table.GetTable_schem(); //q.getstr();
			tbl = table.GetTable_name(); //q.getstr();
			if ( (!stricmp(cat.c_str(),catalog.c_str()) || catalog == "") &&
					 (!stricmp(tbl.c_str(),table_name.c_str()) || table_name == "")
					 )
			{
				tabledump(db,cat,sch,tbl,table);
			}
		}
		q.free_result();

		if (table_name.size())
		{
		}
		else
		{
			// dump all tables
		}
	}
	catch( const std::string& e )
	{
		printf("Exception: %s\n", e.c_str());
	}
}


void tabledump(Database& db,const std::string& cat,const std::string& sch,const std::string& table_name,sql::Odbctable& table)
{
	Query q(db);
	std::string primary;
	char sql[200];
	std::map<std::string,std::string> mmap;

	q.get_result(SQLGET_PRIMARYKEYS, cat.c_str(), sch.c_str(), table_name.c_str());
	while (q.fetch_row())
	{
		sql::Odbcprimarykey pr(&db,&q);
		// 0 table_cat
		// 1 table_schem
		// 2 table_name
		// 3 column_name
		// 4 key_seq (field# in key (1..x))
		// 5 pk_name (primary key name)
		if (primary.size())
		{
			printf("Warning - no support for multiple primary columns\n");
		}
		else
		{
//			primary = q.getstr(3);
		}
	}
	q.free_result();

	q.get_result(SQLGET_COLUMNS, cat.c_str(), sch.c_str(), table_name.c_str());
	while (q.fetch_row())
	{
		sql::Odbccolumn cc(&db,&q);
/*
		std::string xml;
		printf("%s / %s\n",cc.GetColumn_name(),cc.GetRemarks());
		cc.ReturnAsXML(xml);
		printf("%s\n",xml.c_str());
*/
		if (*cc.GetColumn_def())
		{
			mmap[cc.GetColumn_name()] = cc.GetColumn_def();
		}
		else
		{
			mmap[cc.GetColumn_name()] = cc.GetRemarks();
		}
	}
	q.free_result();

	std::string name;
	std::string typ;
	std::string query;
	std::string slask;
	std::string slask1;
	int wid;
	int dec;
	int nul;
	int x;
	int primary_num = -1;
	int strtype[100];
	int float_type[100];

	query = "";
	slask = "select * from ";
	if (cat.size())
	{
		query = query + slask + cat;
		slask = ".";
	}
	if (sch.size())
	{
		query = query + slask + sch;
		slask = ".";
	}
	query = query + slask + table_name;
	strcpy(sql,query.c_str());

	query = "";
	slask = "INSERT INTO ";
	if (cat.size())
	{
		query = query + slask + cat;
		slask = ".";
	}
	if (sch.size())
	{
		query = query + slask + sch;
		slask = ".";
	}
	slask = query + slask + table_name + " (";
	query = "";

	printf("--\n");
	printf("-- Dumping data for\n");
	if (cat.size())
	{
		printf("--	Catalog '%s'	(database, as in USE <database>)\n",cat.c_str());
	}
	if (sch.size())
	{
		printf("--	Schema	'%s'\n",sch.c_str());
	}
	printf("--	Table	 '%s'\n",table_name.c_str());
	printf("--\n");
	printf("\n");

	q.get_result(sql);
	x = 0;
	printf("CREATE TABLE %s (\n",table_name.c_str());
	while (q.getcol(name,typ,wid,dec,nul))
	{
		printf("	%s %s",name.c_str(),typ.c_str() + 4);
		if (typ == "SQL_DECIMAL" ||
				typ == "SQL_NUMERIC" ||
				typ == "SQL_FLOAT" ||
				typ == "SQL_REAL")
		{
			float_type[x] = 1;
		}
		else
		{
			float_type[x] = 0;
		}
		if (typ == "SQL_CHAR" || typ == "SQL_WCHAR" ||
				typ == "SQL_VARCHAR" || typ == "SQL_WVARCHAR" ||
				typ == "SQL_LONGVARCHAR" || typ == "SQL_WLONGVARCHAR" ||
				typ == "SQL_TYPE_TIMESTAMP")
		{
			strtype[x] = 1;
		}
		else
		{
			strtype[x] = 0;
		}
		if (name != primary)
		{
			query = query + slask + name;
			slask = ",";
		}
		else
		{
			primary_num = x;
		}
		if (float_type[x])
		{
			printf("(%d,%d)",wid,dec);
		}
		else
		if (wid > 0)
		{
			printf("(%d)",wid);
		}
		if (!nul)
		{
			printf(" NOT NULL");
		}
		if (mmap[name].size())
		{
			printf(", //! <%s\n",mmap[name].c_str() );
		}
		else
		{
			printf(",\n");
		}
		x++;
	}
	if (*table.GetRemarks())
	{
		printf(") COMMENT=\"%s\";\n",table.GetRemarks());
	}
	else
	{
		printf(");\n"); // end of create table
	}
	printf("\n");

	slask1 = query + ") VALUES (";
	while (q.fetch_row())
	{
		slask = slask1;
		query = "";
		for (int i = 0; i < q.num_cols(); i++)
		{
			if (i != primary_num)
			{
				if (float_type[i])
				{
					double				d = q.getnum(i);
					char tmp[100];

					sprintf(tmp,"%f",d);
					while (C1 == '0' || C1 == '.')
					{
						char c = C1;
						C1 = 0;
						if (c == '.')
						{
							break;
						}
					}
					query = query + slask + tmp;
					slask = ",";
				}
				else
				if (strtype[i])
				{
					std::string value = q.getstr(i);

					fixstring( value );
					query = query + slask + "'" + db.safestr(value) + "'";
					slask = ",";
				}
				else
				{
					char value[200];
					int index = 0;

					strcpy(value,q.getstr(i));
					while (value[index] == ' ')
					{
						index++;
					}
					memmove(value, value + index, strlen(value + index) + 1);
					while (strlen(value) && value[strlen(value) - 1] == ' ')
					{
						value[strlen(value) - 1] = 0;
					}
					query = query + slask + value;
					slask = ",";
				}
			}
		}
		query += ");";
		printf("%s\n",query.c_str());
	}
	q.free_result();

	printf("\n");
}


void fixstring(std::string& str)
{
	char *buffer = new char[str.size() * 2 + 4];

	CharToOemBuff(str.c_str(), buffer, str.size());
	buffer[str.size()] = 0;
	str = buffer;
	delete buffer;
}


