#pragma once

#include "exception.h"
#include "parser.h"
#include "settings.h"

#include <vector>

namespace usql {

    struct ColValue {
	virtual bool isNull() { return false; };
	virtual ColumnType getColType() = 0;
	virtual long getIntValue() = 0;
	virtual double getDoubleValue() = 0;
	virtual std::string getStringValue() = 0;
	virtual long getDateValue() = 0;
	virtual bool getBoolValue() = 0;

	virtual int compare(ColValue * other) = 0;

	virtual ~ColValue() = default;

    };


    struct ColNullValue : ColValue {
        bool isNull() override { return true; };
        ColumnType getColType() override { throw Exception("getColType not supported on ColNullValue"); }
	long getIntValue() override { throw Exception("getIntValue not supported on ColNullValue"); };
	double getDoubleValue() override { throw Exception("getDoubleValue not supported on ColNullValue"); };
	std::string getStringValue() override { return "null"; };
	long getDateValue() override { throw Exception("getDateValue not supported on ColNullValue"); };
	bool getBoolValue() override { throw Exception("getDateValue not supported on ColNullValue"); };

	int compare(ColValue * other) override;
    };


    struct ColIntegerValue : ColValue {
	ColIntegerValue(long value) : m_integer(value) {};
	ColIntegerValue(const ColIntegerValue &other) : m_integer(other.m_integer) {};

	ColumnType getColType() override { return ColumnType::integer_type; };
	long getIntValue() override { return m_integer; };
	double getDoubleValue() override { return (double) m_integer; };
	std::string getStringValue() override { return std::to_string(m_integer); };
	long getDateValue() override { return m_integer; };
	bool getBoolValue() override { throw Exception("Not supported on ColIntegerValue"); };

	int compare(ColValue * other) override;

	long m_integer;
    };


    struct ColDoubleValue : ColValue {
	ColDoubleValue(double value) : m_double(value) {};
	ColDoubleValue(const ColDoubleValue &other) : m_double(other.m_double) {}

	ColumnType getColType() override { return ColumnType::float_type; };
	long getIntValue() override { return (long) m_double; };
	double getDoubleValue() override { return m_double; };
	std::string getStringValue() override { return std::to_string(m_double); };
	long getDateValue() override { return (long) m_double; };
	bool getBoolValue() override { throw Exception("Not supported on ColDoubleValue"); };

	int compare(ColValue * other) override;

	double m_double;
    };


    struct ColStringValue : ColValue {
	ColStringValue(const std::string &value) : m_string(value) {};
	ColStringValue(const ColStringValue &other) : m_string(other.m_string) {};

	ColumnType getColType() override { return ColumnType::varchar_type; };
	long getIntValue() override { return std::stoi(m_string); };
	double getDoubleValue() override { return std::stod(m_string); };
	std::string getStringValue() override { return m_string; };
	long getDateValue() override { return std::stoi(m_string); };
	bool getBoolValue() override { throw Exception("Not supported on ColStringValue"); };

	int compare(ColValue * other) override;

	std::string m_string;
    };

    struct ColDateValue : ColValue {
        ColDateValue(long value) : m_date(value) {};
        ColDateValue(const ColDateValue &other) : m_date(other.m_date) {};

	ColumnType getColType() override { return ColumnType::date_type; };
        long getIntValue() override { return m_date; };
        double getDoubleValue() override { return (double) m_date; };
        std::string getStringValue() override { return Settings::date_to_string(m_date); };
	long getDateValue() override { return m_date; };
	bool getBoolValue() override { throw Exception("Not supported on ColDateValue"); };

        int compare(ColValue * other) override;

        long m_date;	// seconds since epoch for now
    };

    struct ColBooleanValue : ColValue {
        ColBooleanValue(bool value) : m_bool(value) {};
        ColBooleanValue(const ColBooleanValue &other) : m_bool(other.m_bool) {};

	ColumnType getColType() override { return ColumnType::bool_type; };
        long getIntValue() override { return (long) m_bool; };
        double getDoubleValue() override { return (double) m_bool; };
        std::string getStringValue() override { return m_bool ? "Y"  : "N"; };
        long getDateValue() override { throw Exception("Not supported on ColBooleanValue"); };
	bool getBoolValue() override { return m_bool; };

        int compare(ColValue * other) override;

        bool m_bool;
    };

    class Row {

    public:
	Row(int cols_count);
	Row(const Row &other);

	Row &operator=(Row other);
	bool operator==(const Row &other) const {return this->compare(other) == 0; };

	void setColumnNull(int col_index);
	void setIntColumnValue(int col_index, long value);
	void setFloatColumnValue(int col_index, double value);
	void setStringColumnValue(int col_index, const std::string &value);
	void setDateColumnValue(int col_index, long value);
	void setDateColumnValue(int col_index, const std::string &value);
	void setBoolColumnValue(int col_index, bool value);
	void setBoolColumnValue(int col_index, const std::string &value);
	void setColumnValue(ColDefNode *col_def, ColValue *col_value);
	void setColumnValue(ColDefNode *col_def, ValueNode *col_value);

	ColValue &operator[](int i) { return *m_columns[i]; }

	ColValue * ith_column(int i) const { return m_columns[i].get(); }
	int compare(const Row  &other) const;

	void print(const std::vector<int> & col_char_sizes);

    private:
	std::vector<std::unique_ptr<ColValue>> m_columns;
    };

}	// namespace