#include "Pgsql_Value.h"
#include <cstdlib>
#include <cstring>

using namespace Pgsql;

Value::Value(const char *val, Oid typ)
	: m_val(val)
	, m_typ(typ)
{}

QString Value::asQString() const
{
	return QString::fromUtf8(m_val);
}

Value::operator QString() const
{
//	if (isString())
		return QString::fromUtf8(m_val);
//	else
//		throw std::logic_error("Column type doesn't match requested type");
}

Value::operator QDateTime() const
{
	return QDateTime::fromString(asQString(), Qt::ISODateWithMs);
//	return QDateTime::fromString(asQString(),
//		"yyyy-MM-dd hh:mm:ss");
}

Value::operator QDate() const
{
	return QDate::fromString(asQString(), Qt::ISODate);
}

Value::operator QTime() const
{
    // as values are expected to be coming straight out of postgresql
    // we will not validate everything
    char* pos;
    int hours = std::strtol(m_val, &pos, 10);
    if (*pos == ':')
        ++pos;
    else
        throw std::runtime_error("Unpexted character after hour in input");
    int minutes = std::strtol(pos, &pos, 10);
    if (*pos == ':')
        ++pos;
    else
        throw std::runtime_error("Unpexted character after minutes in input");
    int seconds = std::strtol(pos, &pos, 10);
    int millis = 0;
    if (*pos == '.')
    {
        const char *start = ++pos;
        millis = std::strtol(pos, &pos, 10);
        int length = pos - start;
        switch (length) {
            case 0: break; // should be zero anyway so no reason to multiply by 1000
            case 1: millis *= 100; break;
            case 2: millis *= 10; break;
            case 3: break;
            case 4: millis = (millis + 5) / 10; break;
            case 5: millis = (millis + 50) / 100; break;
            case 6: millis = (millis + 500) / 1000; break;
            default:
                throw std::runtime_error("Unpexted number of digits in microsecond field");
        }
    }
    int tzhours = 0, tzminutes = 0;
    if (*pos == '+' || *pos == '-')
    {
        ++pos;
        tzhours = std::strtol(pos, &pos, 10);
        if (*pos == ':')
        {
            tzminutes = std::strtol(pos, &pos, 10);
        }
        // TODO QTime does not support timezones (and rightly so as they are meaningless without a date)
        // the postgresql documentation actually agrees with this but supports timezones to not break
        // compatability with older verions
    }
    return QTime(hours, minutes, seconds, millis);
}


Value::operator std::string() const
{
	return m_val;
}

Value::operator int16_t() const
{
    if (m_val == nullptr)
        return 0;

    int32_t r = operator int32_t();
	if (r <= std::numeric_limits<int16_t>::max()
			&& r >= std::numeric_limits<int16_t>::min())
		return int16_t(r);

	throw std::runtime_error("Value conversion to int16_t failed (out of range)");
}

Value::operator int32_t() const
{
    if (m_val == nullptr)
        return 0;

    const size_t len = std::strlen(m_val);
	if (len > 0) {
		char *endptr = nullptr;
		long result = std::strtol(m_val, &endptr, 10);
		if (endptr == m_val + len)
			return result;
	}
	throw std::runtime_error("Value conversion to int32_t failed");
}

Value::operator Oid() const
{
	return operator int32_t();
}

Value::operator int64_t() const
{
    if (m_val == nullptr)
        return 0LL;

    const size_t len = std::strlen(m_val);
	if (len > 0) {
		char *endptr = nullptr;
		int64_t result = std::strtoll(m_val, &endptr, 10);
		if (endptr == m_val + len)
			return result;
	}
	throw std::runtime_error("Value conversion to int64_t failed");
}

Value::operator bool() const
{
	return std::strcmp(m_val, "t") == 0;
}

Value::operator float() const
{
	return std::stof(m_val);
}

Value::operator double() const
{
	return std::stod(m_val);
}

Value::operator char() const
{
	return m_val[0];
}


bool Value::isString() const
{
	return m_typ == char_oid
		|| m_typ == varchar_oid
		|| m_typ == text_oid
		;
}



//void operator<<(QString &s, const Value &v)
//{
//	s = v.asQString();
//}

//void operator<<(QDateTime &l, const Value &v)
//{
//	l = QDateTime::fromString(asQString(),
//		"yyyy-MM-dd hh:mm:ss");
//}

//void operator<<(std::string &l, const Value &v)
//{
//	l = v;
//}

//void operator<<(short &l, const Value &v)
//{
//	l = (short)std::atoi(v.c_str());
//}

//void operator<<(int &l, const Value &v)
//{
//	l = std::atoi(v.c_str());
//}

//void Pgsql::operator<<(Oid &l, const Value &v)
//{
//	l = std::atoi(v.c_str());
//}

//void operator<<(__int64 &l, const Value &v)
//{
//	l = std::strtoull(v.c_str(), nullptr, 10);
//}

//void operator<<(bool &l, const Value &v)
//{
//	l = std::strcmp(v.c_str(), "t") == 0;
//}
