#pragma once

#include "sqlite_common.h"

#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/variant.hpp>

#include <cstdint>
#include <string>
#include <type_traits>
#include <vector>

namespace maps {
namespace mrc {
namespace common {

/*
 * As of boost 1.60, boost::none_t is not default-constructible
 * Using simple empty structure as replacement
 */
struct None
{
};

/// @see http://www.sqlite.org/datatype3.html#section_2
using SQLiteVariant
    = boost::variant<None, int64_t, double, std::string, SQLiteBlob>;

std::string toString(const SQLiteVariant&);

namespace detail {

template <typename To>
struct NumericVisitor : boost::static_visitor<To> {
    To operator()(const None&) const
    {
        throw SQLiteError{} << "NULL to number conversion is not supported";
    }

    template <typename From>
    typename std::enable_if<std::is_arithmetic<From>::value, To>::type
    operator()(const From& from) const
    {
        return boost::numeric_cast<To>(from);
    }

    To operator()(const std::string& from) const
    {
        return boost::lexical_cast<To>(from);
    }

    To operator()(const SQLiteBlob&) const
    {
        throw SQLiteError{} << "BLOB to number conversion is not supported";
    }
};

} // detail

template <typename To>
typename std::enable_if<std::is_arithmetic<To>::value, To>::type
numericCast(const SQLiteVariant& from)
{
    return boost::apply_visitor(detail::NumericVisitor<To>(), from);
}

} // common
} // mrc
} // maps
