
#include <ml_string.h>
#include "row.h"

namespace usql {

int ColNullValue::compare(ColValue &other) {
	return other.isNull() ? 0 : -1;        // null goes to end
}

int ColIntegerValue::compare(ColValue &other) {
	long r = m_integer - other.getIntValue();
	return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
}

int ColDoubleValue::compare(ColValue &other) {
	if (other.isNull()) return 1;        // null goes to end

	double c = m_double - other.getDoubleValue();
	return c < 0 ? -1 : c == 0.0 ? 0 : 1;
}

ColStringValue & ColStringValue::operator=(ColStringValue other) {
	std::swap(m_string, other.m_string);
	return *this;
}

int ColStringValue::compare(ColValue &other) {
	return other.isNull() ? 1 : m_string->compare(other.getStringValue());        // null goes to end
}

int ColDateValue::compare(ColValue &other) {
	long r = m_date - other.getIntValue();
	return other.isNull() ? 1 : r > 0 ? 1 : r == 0 ? 0 : -1;
}

int ColBooleanValue::compare(ColValue &other) {
	if (other.isNull()) return 1;        // null goes to end

	return m_bool == other.getBoolValue() ? 0 : m_bool && !other.getBoolValue() ? -1 : 1;        // true first
}

Row::Row(const Row &other) : m_columns(other.m_columns.size()) {
	for (int i = 0; i < other.m_columns.size(); i++) {
		if (other[i].isNull())
			continue;        // for null NOP

		ColumnType col_type = other[i].getColType();
		switch (col_type) {
			case ColumnType::integer_type :
				setIntColumnValue(i, other[i].getIntValue());
				break;
			case ColumnType::float_type :
				setFloatColumnValue(i, other[i].getDoubleValue());
				break;
			case ColumnType::varchar_type :
				setStringColumnValue(i, other[i].getStringValue());
				break;
			case ColumnType::date_type :
				setDateColumnValue(i, other[i].getDateValue());
				break;
			case ColumnType::bool_type :
				setBoolColumnValue(i, other[i].getBoolValue());
				break;
			default:
				throw Exception("unsupported column type");
		}
	}
}


Row &Row::operator=(Row other) {
	std::swap(m_columns, other.m_columns);
	return *this;
}

void Row::setColumnNull(int col_index) {
	m_columns[col_index] = ColNullValue();
}

void Row::setIntColumnValue(int col_index, long value) {
	m_columns[col_index] = ColIntegerValue(value);
}

void Row::setFloatColumnValue(int col_index, double value) {
	m_columns[col_index] = ColDoubleValue(value);
}

void Row::setStringColumnValue(int col_index, const std::string &value) {
	m_columns[col_index] = ColStringValue(value);
}

void Row::setDateColumnValue(int col_index, long value) {
	m_columns[col_index] = ColDateValue(value);
}

void Row::setDateColumnValue(int col_index, const std::string &value) {
	m_columns[col_index] = ColDateValue(Settings::string_to_date(value));
}

void Row::setBoolColumnValue(int col_index, bool value) {
	m_columns[col_index] = ColBooleanValue(value);
}

void Row::setBoolColumnValue(int col_index, const std::string &value) {
	bool v = (value == "Y" || value == "1");
	m_columns[col_index] = ColBooleanValue(v);
}

void Row::setColumnValue(ColDefNode *col_def, ColValue &col_value) {
	if (!col_value.isNull()) {
		if (col_def->type == ColumnType::integer_type)
			setIntColumnValue(col_def->order, col_value.getIntValue());
		else if (col_def->type == ColumnType::float_type)
			setFloatColumnValue(col_def->order, col_value.getDoubleValue());
		else if (col_def->type == ColumnType::varchar_type)
			setStringColumnValue(col_def->order, col_value.getStringValue());
		else if (col_def->type == ColumnType::date_type)
			setDateColumnValue(col_def->order, col_value.getDateValue());
		else if (col_def->type == ColumnType::bool_type)
			setBoolColumnValue(col_def->order, col_value.getBoolValue());
	} else {
		setColumnNull(col_def->order);
	}
}

void Row::setColumnValue(ColDefNode *col_def, ValueNode *col_value) {
	if (!col_value->isNull()) {
		if (col_def->type == ColumnType::integer_type)
			setIntColumnValue(col_def->order, col_value->getIntegerValue());
		else if (col_def->type == ColumnType::float_type)
			setFloatColumnValue(col_def->order, col_value->getDoubleValue());
		else if (col_def->type == ColumnType::varchar_type)
			setStringColumnValue(col_def->order, col_value->getStringValue());
		else if (col_def->type == ColumnType::date_type)
			setIntColumnValue(col_def->order, col_value->getDateValue());
		else if (col_def->type == ColumnType::bool_type)
			setBoolColumnValue(col_def->order, col_value->getBooleanValue());
		else
			throw Exception("unsupported data type");
	} else {
		setColumnNull(col_def->order);
	}
}

int Row::compare(const Row &other) const {
	for (int ci = 0; ci < m_columns.size(); ci++) {
		int cmp = this->operator[](ci).compare(other[ci]);
		if (cmp != 0) return cmp;
	}
	return 0;
}

void Row::print(const std::vector<ColDefNode> &col_defs) {
	std::string out{"| "};

	for (int ci = 0; ci < m_columns.size(); ci++) {
		auto & col_def = col_defs[ci];
		int col_size = print_get_column_size(col_def);

		if (col_def.type==ColumnType::integer_type || col_def.type==ColumnType::float_type || col_def.type==ColumnType::bool_type)
			out.append(string_padd(this->operator[](ci).getStringValue(), col_size, ' ', false) + " | ");
		else
			out.append(string_padd(this->operator[](ci).getStringValue(), col_size, ' ', true) + " | ");
	}

	std::cout << out << std::endl;
}


int Row::print_get_column_size(const ColDefNode &col_def) {
	int col_size = col_def.type == ColumnType::varchar_type ? col_def.length :
		    col_def.type == ColumnType::float_type ? 16 : 10;
	return col_size;
}

}	// namespace
