#pragma once
#include <drogon/exports.h>
#include <string_view>
#include <drogon/orm/ArrayParser.h>
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <trantor/utils/Logger.h>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#ifdef __linux__
#include <arpa/inet.h>
#endif
namespace drogon
{
namespace orm
{
class DROGON_EXPORT Field
{
  public:
    using SizeType = unsigned long;
    const char *name() const;
    bool isNull() const;
    const char *c_str() const;
    size_t length() const
    {
        return result_.getLength(row_, column_);
    }
    template <typename T>
    T as() const
    {
        if (isNull())
            return T();
        auto data_ = result_.getValue(row_, column_);
        T value = T();
        if (data_)
        {
            try
            {
                std::stringstream ss(data_);
                ss >> value;
            }
            catch (...)
            {
                LOG_DEBUG << "Type error";
            }
        }
        return value;
    }
    ArrayParser getArrayParser() const
    {
        return ArrayParser(result_.getValue(row_, column_));
    }
    template <typename T>
    std::vector<std::shared_ptr<T>> asArray() const
    {
        std::vector<std::shared_ptr<T>> ret;
        auto arrParser = getArrayParser();
        while (1)
        {
            auto arrVal = arrParser.getNext();
            if (arrVal.first == ArrayParser::juncture::done)
            {
                break;
            }
            if (arrVal.first == ArrayParser::juncture::string_value)
            {
                T val;
                std::stringstream ss(std::move(arrVal.second));
                ss >> val;
                ret.push_back(std::shared_ptr<T>(new T(val)));
            }
            else if (arrVal.first == ArrayParser::juncture::null_value)
            {
                ret.push_back(std::shared_ptr<T>());
            }
        }
        return ret;
    }
  protected:
    Result::SizeType row_;
    long column_;
    friend class Row;
    Field(const Row &row, Row::SizeType columnNum) noexcept;
  private:
    const Result result_;
};
template <>
DROGON_EXPORT std::string Field::as<std::string>() const;
template <>
DROGON_EXPORT const char *Field::as<const char *>() const;
template <>
DROGON_EXPORT char *Field::as<char *>() const;
template <>
DROGON_EXPORT std::vector<char> Field::as<std::vector<char>>() const;
template <>
inline std::string_view Field::as<std::string_view>() const
{
    auto first = result_.getValue(row_, column_);
    auto length = result_.getLength(row_, column_);
    return {first, length};
}
template <>
inline float Field::as<float>() const
{
    if (isNull())
        return 0.0;
    return std::stof(result_.getValue(row_, column_));
}
template <>
inline double Field::as<double>() const
{
    if (isNull())
        return 0.0;
    return std::stod(result_.getValue(row_, column_));
}
template <>
inline bool Field::as<bool>() const
{
    if (result_.getLength(row_, column_) != 1)
    {
        return false;
    }
    auto value = result_.getValue(row_, column_);
    if (*value == 't' || *value == '1')
        return true;
    return false;
}
template <>
inline int Field::as<int>() const
{
    if (isNull())
        return 0;
    return std::stoi(result_.getValue(row_, column_));
}
template <>
inline long Field::as<long>() const
{
    if (isNull())
        return 0;
    return std::stol(result_.getValue(row_, column_));
}
template <>
inline int8_t Field::as<int8_t>() const
{
    if (isNull())
        return 0;
    return static_cast<int8_t>(atoi(result_.getValue(row_, column_)));
}
template <>
inline long long Field::as<long long>() const
{
    if (isNull())
        return 0;
    return atoll(result_.getValue(row_, column_));
}
template <>
inline unsigned int Field::as<unsigned int>() const
{
    if (isNull())
        return 0;
    return static_cast<unsigned int>(
        std::stoul(result_.getValue(row_, column_)));
}
template <>
inline unsigned long Field::as<unsigned long>() const
{
    if (isNull())
        return 0;
    return std::stoul(result_.getValue(row_, column_));
}
template <>
inline uint8_t Field::as<uint8_t>() const
{
    if (isNull())
        return 0;
    return static_cast<uint8_t>(atoi(result_.getValue(row_, column_)));
}
template <>
inline unsigned long long Field::as<unsigned long long>() const
{
    if (isNull())
        return 0;
    return std::stoull(result_.getValue(row_, column_));
}
}  
}  