import os
from python_helpers import open_utf8

GENERATED_HEADER = 'include/tpce_generated.hpp'
GENERATED_SOURCE = 'tpce_generated.cpp'
TPCE_DIR = os.path.join('third_party', 'tpce-tool')

GENERATED_HEADER = os.path.join(TPCE_DIR, GENERATED_HEADER)
GENERATED_SOURCE = os.path.join(TPCE_DIR, GENERATED_SOURCE)

current_table = None

tables = {}

print(GENERATED_HEADER)
print(GENERATED_SOURCE)

header = open_utf8(GENERATED_HEADER, 'w+')
source = open_utf8(GENERATED_SOURCE, 'w+')

for fp in [header, source]:
    fp.write(
        """
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// THIS FILE IS GENERATED BY gentpcecode.py, DO NOT EDIT MANUALLY //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

"""
    )

header.write(
    """
#include "duckdb/catalog/catalog.hpp"
#include "duckdb/main/appender.hpp"
#include "duckdb/main/connection.hpp"
#include "duckdb/main/database.hpp"

#include "main/BaseLoader.h"
#include "main/BaseLoaderFactory.h"
#include "main/NullLoader.h"
#include "main/TableRows.h"

namespace TPCE {
	class DuckDBLoaderFactory : public CBaseLoaderFactory {
		duckdb::Connection &con;
		std::string schema;
		std::string suffix;

	  public:
		DuckDBLoaderFactory(duckdb::Connection &con, std::string schema,
		                    std::string suffix)
		    : con(con), schema(schema), suffix(suffix) {
		}

		// Functions to create loader classes for individual tables.
		virtual CBaseLoader<ACCOUNT_PERMISSION_ROW> *
		CreateAccountPermissionLoader();
		virtual CBaseLoader<ADDRESS_ROW> *CreateAddressLoader();
		virtual CBaseLoader<BROKER_ROW> *CreateBrokerLoader();
		virtual CBaseLoader<CASH_TRANSACTION_ROW> *
		CreateCashTransactionLoader();
		virtual CBaseLoader<CHARGE_ROW> *CreateChargeLoader();
		virtual CBaseLoader<COMMISSION_RATE_ROW> *CreateCommissionRateLoader();
		virtual CBaseLoader<COMPANY_COMPETITOR_ROW> *
		CreateCompanyCompetitorLoader();
		virtual CBaseLoader<COMPANY_ROW> *CreateCompanyLoader();
		virtual CBaseLoader<CUSTOMER_ACCOUNT_ROW> *
		CreateCustomerAccountLoader();
		virtual CBaseLoader<CUSTOMER_ROW> *CreateCustomerLoader();
		virtual CBaseLoader<CUSTOMER_TAXRATE_ROW> *
		CreateCustomerTaxrateLoader();
		virtual CBaseLoader<DAILY_MARKET_ROW> *CreateDailyMarketLoader();
		virtual CBaseLoader<EXCHANGE_ROW> *CreateExchangeLoader();
		virtual CBaseLoader<FINANCIAL_ROW> *CreateFinancialLoader();
		virtual CBaseLoader<HOLDING_ROW> *CreateHoldingLoader();
		virtual CBaseLoader<HOLDING_HISTORY_ROW> *CreateHoldingHistoryLoader();
		virtual CBaseLoader<HOLDING_SUMMARY_ROW> *CreateHoldingSummaryLoader();
		virtual CBaseLoader<INDUSTRY_ROW> *CreateIndustryLoader();
		virtual CBaseLoader<LAST_TRADE_ROW> *CreateLastTradeLoader();
		virtual CBaseLoader<NEWS_ITEM_ROW> *CreateNewsItemLoader();
		virtual CBaseLoader<NEWS_XREF_ROW> *CreateNewsXRefLoader();
		virtual CBaseLoader<SECTOR_ROW> *CreateSectorLoader();
		virtual CBaseLoader<SECURITY_ROW> *CreateSecurityLoader();
		virtual CBaseLoader<SETTLEMENT_ROW> *CreateSettlementLoader();
		virtual CBaseLoader<STATUS_TYPE_ROW> *CreateStatusTypeLoader();
		virtual CBaseLoader<TAX_RATE_ROW> *CreateTaxRateLoader();
		virtual CBaseLoader<TRADE_HISTORY_ROW> *CreateTradeHistoryLoader();
		virtual CBaseLoader<TRADE_ROW> *CreateTradeLoader();
		virtual CBaseLoader<TRADE_REQUEST_ROW> *CreateTradeRequestLoader();
		virtual CBaseLoader<TRADE_TYPE_ROW> *CreateTradeTypeLoader();
		virtual CBaseLoader<WATCH_ITEM_ROW> *CreateWatchItemLoader();
		virtual CBaseLoader<WATCH_LIST_ROW> *CreateWatchListLoader();
		virtual CBaseLoader<ZIP_CODE_ROW> *CreateZipCodeLoader();
	};

"""
)

source.write(
    """
#include "tpce_generated.hpp"

using namespace duckdb;
using namespace std;

namespace TPCE {
struct tpce_append_information {
	tpce_append_information(Connection &con, string schema, string table) :
		appender(con, schema, table) {}

	Appender appender;
};

static void append_value(tpce_append_information &info, int32_t value) {
	info.appender.Append<int32_t>(value);
}

static void append_bigint(tpce_append_information &info, int64_t value) {
	info.appender.Append<int64_t>(value);
}

static void append_string(tpce_append_information &info, const char *value) {
	info.appender.Append<Value>(Value(value));
}

static void append_double(tpce_append_information &info, double value) {
	info.appender.Append<double>(value);
}

static void append_bool(tpce_append_information &info, bool value) {
	info.appender.Append<bool>(value);
}

static void append_timestamp(tpce_append_information &info, CDateTime time) {
	int32_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, msec = 0;
	time.GetYMDHMS(&year, &month, &day, &hour, &minute, &second, &msec);
	info.appender.Append<Value>(Value::TIMESTAMP(year, month, day, hour, minute, second, msec * 1000));
}

void append_char(tpce_append_information &info, char value) {
	char val[2];
	val[0] = value;
	val[1] = '\\0';
	append_string(info, val);
}

template <typename T> class DuckDBBaseLoader : public CBaseLoader<T> {
  protected:
	tpce_append_information info;

  public:
	DuckDBBaseLoader(Connection &con, string schema, string table) :
		info(con, schema, table) {
	}

	void FinishLoad() {

	}
};

"""
)

with open(os.path.join(TPCE_DIR, 'include/main/TableRows.h'), 'r') as f:
    for line in f:
        line = line.strip()
        if line.startswith('typedef struct '):
            line = line.replace('typedef struct ', '')
            current_table = line.split(' ')[0].replace('_ROW', ' ').replace('_', ' ').lower().strip()
            tables[current_table] = []
        elif line.startswith('}'):
            current_table = None
        elif current_table != None:
            # row
            # get type
            splits = line.strip().split(' ')
            if len(splits) < 2:
                continue
            line = splits[0]
            name = splits[1].split(';')[0].split('[')[0].lower()
            is_single_char = False
            if 'TIdent' in line or 'INT64' in line or 'TTrade' in line:
                tpe = "TypeId::BIGINT"
                sqltpe = "BIGINT"
            elif 'double' in line or 'float' in line:
                tpe = "TypeId::DECIMAL"
                sqltpe = "DECIMAL"
            elif 'int' in line:
                tpe = "TypeId::INTEGER"
                sqltpe = "INTEGER"
            elif 'CDateTime' in line:
                tpe = "TypeId::TIMESTAMP"
                sqltpe = "TIMESTAMP"
            elif 'bool' in line:
                tpe = 'TypeId::BOOLEAN'
                sqltpe = "BOOLEAN"
            elif 'char' in line:
                if '[' not in splits[1]:
                    is_single_char = True
                tpe = "TypeId::VARCHAR"
                sqltpe = "VARCHAR"
            else:
                continue
            tables[current_table].append([name, tpe, is_single_char, sqltpe])


def get_tablename(name):
    name = name.title().replace(' ', '')
    if name == 'NewsXref':
        return 'NewsXRef'
    return name


for table in tables.keys():
    source.write(
        """
class DuckDB${TABLENAME}Load : public DuckDBBaseLoader<${ROW_TYPE}> {
public:
	DuckDB${TABLENAME}Load(Connection &con, string schema, string table) :
		DuckDBBaseLoader(con, schema, table) {

	}

	void WriteNextRecord(const ${ROW_TYPE} &next_record) {
		info.appender.BeginRow();""".replace(
            "${TABLENAME}", get_tablename(table)
        ).replace(
            "${ROW_TYPE}", table.upper().replace(' ', '_') + '_ROW'
        )
    )
    source.write("\n")
    collist = tables[table]
    for i in range(len(collist)):
        entry = collist[i]
        name = entry[0].upper()
        tpe = entry[1]
        if tpe == "TypeId::BIGINT":
            funcname = "bigint"
        elif tpe == "TypeId::DECIMAL":
            funcname = "double"
        elif tpe == "TypeId::INTEGER":
            funcname = "value"
        elif tpe == "TypeId::TIMESTAMP":
            funcname = "timestamp"
        elif tpe == 'TypeId::BOOLEAN':
            funcname = "bool"
        elif tpe == "TypeId::VARCHAR":
            if entry[2]:
                funcname = "char"
            else:
                funcname = "string"
        else:
            print("Unknown type " + tpe)
            exit(1)
        source.write("\t\tappend_%s(info, next_record.%s);" % (funcname, name))
        if i != len(collist) - 1:
            source.write("\n")
    source.write(
        """
		info.appender.EndRow();
	}

};"""
    )


for table in tables.keys():
    source.write(
        """
CBaseLoader<${ROW_TYPE}> *
DuckDBLoaderFactory::Create${TABLENAME}Loader() {
	return new DuckDB${TABLENAME}Load(con, schema, "${TABLEINDB}" + suffix);
}
""".replace(
            "${TABLENAME}", get_tablename(table)
        )
        .replace("${ROW_TYPE}", table.upper().replace(' ', '_') + '_ROW')
        .replace("${TABLEINDB}", table.replace(' ', '_'))
    )

source.write("\n")

# static string RegionSchema(string schema, string suffix) {
# 	return "CREATE TABLE " + schema + ".region" + suffix + " ("
# 		"r_regionkey INT NOT NULL,"
# 		"r_name VARCHAR(25) NOT NULL,"
# 		"r_comment VARCHAR(152) NOT NULL);";
# }


for table in tables.keys():
    tname = table.replace(' ', '_')
    str = 'static string ' + table.title().replace(' ', '') + 'Schema(string schema, string suffix) {\n'
    str += '\treturn "CREATE TABLE " + schema + ".%s" + suffix + " ("\n' % (tname,)
    columns = tables[table]
    for i in range(len(columns)):
        column = columns[i]
        str += '\t    "' + column[0] + " " + column[3]
        if i == len(columns) - 1:
            str += ')";'
        else:
            str += ',"'
        str += "\n"
    str += "}\n\n"
    source.write(str)


func = 'void CreateTPCESchema(duckdb::DuckDB &db, duckdb::Connection &con, std::string &schema, std::string &suffix)'
header.write(func + ';\n\n')
source.write(func + ' {\n')


# con.Query(RegionSchema(schema, suffix));

for table in tables.keys():
    tname = table.replace(' ', '_')
    source.write('\tcon.Query(%sSchema(schema, suffix));\n' % (table.title().replace(' ', '')))


source.write('}\n\n')


for fp in [header, source]:
    fp.write("} /* namespace TPCE */\n")
    fp.close()
