#include "catch.hpp"

#include <mapnik/memory_datasource.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/map.hpp>
#include <mapnik/params.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/geometry/geometry_type.hpp>
#include <mapnik/agg_renderer.hpp>

class test_datasource : public mapnik::memory_datasource
{
  public:
    test_datasource(mapnik::box2d<double> const& expected_query_bbox)
        : mapnik::memory_datasource(prepare_params())
        , expected_query_bbox_(expected_query_bbox)
    {}

    virtual mapnik::featureset_ptr features(mapnik::query const& q) const
    {
        mapnik::box2d<double> const& actual_bbox = q.get_bbox();
        REQUIRE(actual_bbox.minx() == Approx(expected_query_bbox_.minx()));
        REQUIRE(actual_bbox.miny() == Approx(expected_query_bbox_.miny()));
        REQUIRE(actual_bbox.maxx() == Approx(expected_query_bbox_.maxx()));
        REQUIRE(actual_bbox.maxy() == Approx(expected_query_bbox_.maxy()));
        return mapnik::memory_datasource::features(q);
    }

  private:
    mapnik::parameters prepare_params() const
    {
        mapnik::parameters params;
        params["type"] = "memory";
        return params;
    }

    mapnik::box2d<double> expected_query_bbox_;
};

TEST_CASE("feature_style_processor: buffer-size with scale-factor")
{
    SECTION("query extent with buffer-size should not be affected by scale-factor")
    {
        const mapnik::box2d<double> expected_query_bbox(-0.5, -0.5, 1.5, 1.5);

        using datasource_ptr = std::shared_ptr<test_datasource>;
        datasource_ptr datasource = std::make_shared<test_datasource>(expected_query_bbox);
        mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
        {
            mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx, 2));
            mapnik::geometry::line_string<double> path;
            path.emplace_back(-10, -10);
            path.emplace_back(10, 10);
            feature->set_geometry(std::move(path));
            datasource->push(feature);
        }

        mapnik::Map map(256, 256);
        map.set_buffer_size(128);

        mapnik::feature_type_style lines_style;
        mapnik::rule rule;
        mapnik::line_symbolizer line_sym;
        rule.append(std::move(line_sym));
        lines_style.add_rule(std::move(rule));
        map.insert_style("lines", std::move(lines_style));

        mapnik::layer lyr("layer");
        lyr.set_datasource(datasource);
        lyr.add_style("lines");
        map.add_layer(lyr);

        const mapnik::box2d<double> map_extent(0, 0, 1, 1);
        map.zoom_to_box(map_extent);

        {
            mapnik::image_rgba8 image(map.width(), map.height());
            mapnik::agg_renderer<mapnik::image_rgba8> ren(map, image);
            ren.apply();
        }

        {
            // Rendering with scale-factor 2.0 should query data
            // with the same extent as with scale-factor 1.0.
            map.resize(map.width() * 2, map.height() * 2);
            mapnik::image_rgba8 image(map.width(), map.height());
            mapnik::agg_renderer<mapnik::image_rgba8> ren(map, image, 2.0);
            ren.apply();
        }
    }
}
