
#include "table.h"
#include "csvreader.h"


namespace usql {

Table::Table(const std::string name, const std::vector<ColDefNode> columns) {
	m_name = name;
	m_col_defs = columns;
	m_rows.clear();
}

ColDefNode Table::get_column_def(const std::string &col_name) {
	auto name_cmp = [col_name](ColDefNode cd) { return cd.name == col_name; };
	auto col_def = std::find_if(begin(m_col_defs), end(m_col_defs), name_cmp);
	if (col_def != std::end(m_col_defs)) {
		return *col_def;
	} else {
		throw Exception("column not exists (" + col_name + ")");
	}
}


Row Table::create_empty_row() {
	return Row(columns_count());
}

std::string Table::csv_string() {
	// header
	std::string out_string;
	for(int i = 0; i < m_col_defs.size(); i++) {
		if (i > 0) out_string += ",";
		out_string += m_col_defs[i].name;
	}

	// rows
	for (auto it = m_rows.begin(); it != m_rows.end(); ++it) {
		std::string csv_line{"\n"};
		for(int i = 0; i < m_col_defs.size(); i++) {
			if (i > 0) csv_line += ",";

			auto col = it->ith_column(i);
			if (!col->isNull()) {
				csv_line += col->getStringValue();		// TODO handle enclosing commas etc
			}
		}
		out_string += csv_line;
	}

	return out_string;
}

int Table::load_csv_string(const std::string &content) {
	int row_cnt = 0;

	CsvReader csvparser{};
	auto csv = csvparser.parseCSV(content);

	std::vector<ColDefNode> &colDefs = m_col_defs;

	for (auto it = csv.begin() + 1; it != csv.end(); ++it) {
		std::vector<std::string> csv_line = *it;

		// prepare empty new_row
		Row new_row = create_empty_row();

		// copy values
		for (size_t i = 0; i < columns_count(); i++) {
			ColDefNode col_def = get_column_def(colDefs[i].name);

			// TODO validate value
			if (col_def.type == ColumnType::integer_type) {
				new_row.setColumnValue(col_def.order, std::stol(csv_line[i]));
			} else if (col_def.type == ColumnType::float_type) {
				new_row.setColumnValue(col_def.order, std::stof(csv_line[i]));
			} else {
				new_row.setColumnValue(col_def.order, csv_line[i]);
			}
		}

		// append new_row
		add_row(new_row);

		row_cnt++;
	}

	return row_cnt;
}

void Table::print() {
	std::cout << "** " << m_name << " **" << std::endl;
	for (auto row : m_rows) {
		row.print();
	}
}

Table::Table(const Table &other) {
	m_name = other.m_name;
	m_col_defs = other.m_col_defs;
	for(const Row& orig_row : other.m_rows) {
		add_copy_of_row(orig_row);
	}
}

void Table::add_row(const Row &row) {
	validate_row(row);
	m_rows.push_back(row);
}

void Table::add_copy_of_row(const Row &row) {
	Row new_row = create_empty_row();

	for(int i = 0; i < m_col_defs.size(); i++) {
		ColValue *ct = row.ith_column(i);

		if (ct->isNull()) {
			new_row.setColumnNull(i);
		} else {
			if (m_col_defs[i].type == ColumnType::integer_type) {
				new_row.setColumnValue(i, row.ith_column(i)->getIntValue());
			} else if (m_col_defs[i].type == ColumnType::float_type) {
				new_row.setColumnValue(i, row.ith_column(i)->getDoubleValue());
			} else if (m_col_defs[i].type == ColumnType::varchar_type) {
				new_row.setColumnValue(i, row.ith_column(i)->getStringValue());
			}
		}
	}

	validate_row(new_row);
	m_rows.push_back(new_row);
}

void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
	if (col_def->null == false && col_val->isNull()) {
		throw Exception("Column " + col_def->name + " cannot be null");
	}
	if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
		throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
	}
}

void Table::validate_column(const ColDefNode *col_def, ColValue *col_val) {
	if (col_def->null == false && col_val->isNull()) {
		throw Exception("Column " + col_def->name + " cannot be null");
	}
	if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
		throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
	}
}

void Table::validate_row(const Row &row) {
	for(int i = 0; i < m_col_defs.size(); i++) {
		ColDefNode col_def = m_col_defs[i];
		ColValue *col_val = row.ith_column(i);

		validate_column(&col_def, col_val);
	}
}

}	// namespace