#pragma once

#include "magic_strings.h"

#include <yandex/maps/wiki/revision/common.h>

#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/serialization.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.h>

#include <boost/optional.hpp>

#include <chrono>
#include <exception>
#include <vector>

namespace maps::wiki::validator {

inline void logException(
        log8::Level level,
        const std::string& contextMessage)
{
    try {
        if (std::current_exception() != std::exception_ptr()) {
            throw;
        }
    } catch (const maps::Exception& ex) {
        log8::MessageHolder(level) << "maps::Exception " << contextMessage
                << ", traceback: " << ex;
    } catch (const std::exception& ex) {
        log8::MessageHolder(level) << "std::exception " << contextMessage
                << ": " << ex.what();
    } catch (...) {
        log8::MessageHolder(level) << "Unknown exception " << contextMessage;
    }
}

class Timer
{
public:
    typedef std::chrono::high_resolution_clock Clock;

    Timer()
        : startTime_(Clock::now())
    { }

    template<typename Duration =
        std::chrono::duration<double> /* double precision seconds */>
    Duration elapsed() const
    {
        return std::chrono::duration_cast<Duration>(Clock::now() - startTime_);
    }

private:
    std::chrono::time_point<Clock> startTime_;
};

template<class Geom>
std::string geomWkb(const Geom& geom)
{
    return geolib3::WKB::toString(geom);
}

inline std::string geomWkb(const boost::none_t&)
{ return std::string(); }

template<class Geom>
std::string geomWkb(const boost::optional<Geom>& geom)
{
    if (!geom) {
        return std::string();
    }

    return geolib3::WKB::toString(*geom);
}

std::string extractCategoryId(const revision::Attributes& revAttrs);

std::vector<geolib3::BoundingBox> unionIntersectedBoundingBoxes(
    std::vector<geolib3::BoundingBox> bboxes);

} // namespace maps::wiki::validator
