#ifndef PYOSMIUM_SIMPLE_HANDLER_HPP
#define PYOSMIUM_SIMPLE_HANDLER_HPP

#include <pybind11/pybind11.h>

#include <osmium/io/any_input.hpp>
#include <osmium/io/file.hpp>
#include <osmium/visitor.hpp>

#include "base_handler.h"

class SimpleHandler: public BaseHandler
{
public:
    virtual ~SimpleHandler() = default;

    void apply_file(const std::string &filename, bool locations = false,
                    const std::string &idx = "flex_mem")
    {
        apply_object(osmium::io::File(filename), locations, idx);
    }

    void apply_buffer(pybind11::buffer const &buf, std::string const &format,
                      bool locations = false,
                      const std::string &idx = "flex_mem")
    {
        Py_buffer pybuf;
        PyObject_GetBuffer(buf.ptr(), &pybuf, PyBUF_C_CONTIGUOUS);

        apply_object(osmium::io::File(reinterpret_cast<const char *>(pybuf.buf),
                                      (size_t) pybuf.len, format.c_str()),
                     locations, idx);
    }

private:
    void apply_object(osmium::io::File file, bool locations, const std::string &idx)
    {
        osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nothing;
        BaseHandler::pre_handler handler = locations?
                                            BaseHandler::location_handler
                                            :BaseHandler::no_handler;

        auto callbacks = enabled_callbacks();

        if (callbacks & osmium::osm_entity_bits::area)
        {
            entities = osmium::osm_entity_bits::object;
            handler = BaseHandler::area_handler;
        } else {
            if (locations || callbacks & osmium::osm_entity_bits::node)
                entities |= osmium::osm_entity_bits::node;
            if (callbacks & osmium::osm_entity_bits::way)
                entities |= osmium::osm_entity_bits::way;
            if (callbacks & osmium::osm_entity_bits::relation)
                entities |= osmium::osm_entity_bits::relation;
        }

        if (callbacks & osmium::osm_entity_bits::changeset)
            entities |= osmium::osm_entity_bits::changeset;

        apply(file, entities, handler, idx);
    }
};

class PySimpleHandler : public SimpleHandler
{
public:
    using SimpleHandler::SimpleHandler;

    osmium::osm_entity_bits::type enabled_callbacks() override
    {
        auto callbacks = osmium::osm_entity_bits::nothing;
        if (hasfunc("node"))
            callbacks |= osmium::osm_entity_bits::node;
        if (hasfunc("way"))
            callbacks |= osmium::osm_entity_bits::way;
        if (hasfunc("relation"))
            callbacks |= osmium::osm_entity_bits::relation;
        if (hasfunc("area"))
            callbacks |= osmium::osm_entity_bits::area;
        if (hasfunc("changeset"))
            callbacks |= osmium::osm_entity_bits::changeset;

        return callbacks;
    }

    // handler functions
    void node(osmium::Node const *n) override
    { PYBIND11_OVERLOAD(void, SimpleHandler, node, n); }

    void way(osmium::Way const *w) override
    { PYBIND11_OVERLOAD(void, SimpleHandler, way, w); }

    void relation(osmium::Relation const *r) override
    { PYBIND11_OVERLOAD(void, SimpleHandler, relation, r); }

    void changeset(osmium::Changeset const *c) override
    { PYBIND11_OVERLOAD(void, SimpleHandler, changeset, c); }

    void area(osmium::Area const *a) override
    { PYBIND11_OVERLOAD(void, SimpleHandler, area, a); }

private:
    bool hasfunc(char const *name)
    { return (bool)pybind11::get_overload(static_cast<SimpleHandler const *>(this), name); }
};

#endif // PYOSMIUM_SIMPLE_HANDLER_HPP
