#!/bin/sh
set -e

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

cat > "$TMPDIR/test_geometry.cpp" << 'EOF'
#include <vtzero/vector_tile.hpp>
#include <vtzero/builder.hpp>
#include <cassert>
#include <iostream>
#include <string>
#include <vector>

// decode_geometry is templated and requires all 9 methods on every handler

struct point_handler {
    std::vector<vtzero::point> pts;
    void points_begin(uint32_t) {}
    void points_point(vtzero::point p) { pts.push_back(p); }
    void points_end() {}
    void linestring_begin(uint32_t) {}
    void linestring_point(vtzero::point) {}
    void linestring_end() {}
    void ring_begin(uint32_t) {}
    void ring_point(vtzero::point) {}
    void ring_end(vtzero::ring_type) {}
};

struct linestring_handler {
    std::vector<std::vector<vtzero::point>> segs;
    std::vector<vtzero::point> cur;
    void points_begin(uint32_t) {}
    void points_point(vtzero::point) {}
    void points_end() {}
    void linestring_begin(uint32_t) { cur.clear(); }
    void linestring_point(vtzero::point p) { cur.push_back(p); }
    void linestring_end() { segs.push_back(cur); }
    void ring_begin(uint32_t) {}
    void ring_point(vtzero::point) {}
    void ring_end(vtzero::ring_type) {}
};

struct polygon_handler {
    struct ring_data {
        vtzero::ring_type type;
        std::vector<vtzero::point> pts;
    };
    std::vector<ring_data> rings;
    std::vector<vtzero::point> cur;
    void points_begin(uint32_t) {}
    void points_point(vtzero::point) {}
    void points_end() {}
    void linestring_begin(uint32_t) {}
    void linestring_point(vtzero::point) {}
    void linestring_end() {}
    void ring_begin(uint32_t) { cur.clear(); }
    void ring_point(vtzero::point p) { cur.push_back(p); }
    void ring_end(vtzero::ring_type rt) { rings.push_back({rt, cur}); }
};

int main() {
    std::cout << "Testing geometry types\n";

    vtzero::tile_builder tile;
    vtzero::layer_builder lyr_pt{tile, "points"};
    vtzero::layer_builder lyr_ls{tile, "lines"};
    vtzero::layer_builder lyr_pg{tile, "polygons"};

    std::cout << "  Building point feature id=1 at (100,200)\n";
    {
        vtzero::point_feature_builder f{lyr_pt};
        f.set_id(1);
        f.add_point(100, 200);
        f.commit();
    }

    std::cout << "  Building linestring feature id=2: (0,0)-(50,0)-(50,50)\n";
    {
        vtzero::linestring_feature_builder f{lyr_ls};
        f.set_id(2);
        f.add_linestring(3);
        f.set_point(0, 0);
        f.set_point(50, 0);
        f.set_point(50, 50);
        f.commit();
    }

    std::cout << "  Building polygon feature id=3: outer ring (5 pts) + inner ring (4 pts)\n";
    {
        vtzero::polygon_feature_builder f{lyr_pg};
        f.set_id(3);
        f.add_ring(5);
        f.set_point(0, 0);
        f.set_point(100, 0);
        f.set_point(100, 100);
        f.set_point(0, 100);
        f.set_point(0, 0);
        f.add_ring(4);
        f.set_point(20, 20);
        f.set_point(20, 40);
        f.set_point(40, 40);
        f.close_ring();
        f.commit();
    }

    const std::string data = tile.serialize();
    std::cout << "  Tile serialized: " << data.size() << " bytes\n";

    vtzero::vector_tile vt{data};
    assert(vt.count_layers() == 3);
    std::cout << "  Layer count: 3 OK\n";

    {
        auto layer = vt.next_layer();
        assert(layer);
        assert(std::string(layer.name()) == "points");
        assert(layer.num_features() == 1);
        auto feat = layer.next_feature();
        assert(feat);
        assert(feat.has_id() && feat.id() == 1);
        assert(feat.geometry_type() == vtzero::GeomType::POINT);
        point_handler h;
        vtzero::decode_geometry(feat.geometry(), h);
        assert(h.pts.size() == 1);
        assert(h.pts[0].x == 100 && h.pts[0].y == 200);
        std::cout << "  Point layer: feature id=1 type="
                  << vtzero::geom_type_name(feat.geometry_type())
                  << " at (" << h.pts[0].x << "," << h.pts[0].y << ") OK\n";
    }

    {
        auto layer = vt.next_layer();
        assert(layer);
        assert(std::string(layer.name()) == "lines");
        auto feat = layer.next_feature();
        assert(feat);
        assert(feat.has_id() && feat.id() == 2);
        assert(feat.geometry_type() == vtzero::GeomType::LINESTRING);
        linestring_handler h;
        vtzero::decode_geometry(feat.geometry(), h);
        assert(h.segs.size() == 1);
        assert(h.segs[0].size() == 3);
        assert(h.segs[0][0].x == 0  && h.segs[0][0].y == 0);
        assert(h.segs[0][1].x == 50 && h.segs[0][1].y == 0);
        assert(h.segs[0][2].x == 50 && h.segs[0][2].y == 50);
        std::cout << "  Linestring layer: feature id=2 type="
                  << vtzero::geom_type_name(feat.geometry_type())
                  << " 1 segment 3 points verified OK\n";
    }

    {
        auto layer = vt.next_layer();
        assert(layer);
        assert(std::string(layer.name()) == "polygons");
        auto feat = layer.next_feature();
        assert(feat);
        assert(feat.has_id() && feat.id() == 3);
        assert(feat.geometry_type() == vtzero::GeomType::POLYGON);
        polygon_handler h;
        vtzero::decode_geometry(feat.geometry(), h);
        assert(h.rings.size() == 2);
        assert(h.rings[0].type == vtzero::ring_type::outer);
        assert(h.rings[0].pts.size() == 5);
        assert(h.rings[1].type == vtzero::ring_type::inner);
        assert(h.rings[1].pts.size() == 4);
        std::cout << "  Polygon layer: feature id=3 type="
                  << vtzero::geom_type_name(feat.geometry_type())
                  << " outer(" << h.rings[0].pts.size()
                  << " pts) inner(" << h.rings[1].pts.size() << " pts) OK\n";
    }

    std::cout << "Geometry test PASSED\n";
    return 0;
}
EOF

echo "Compiling geometry test..."
g++ -std=c++14 -o "$TMPDIR/test_geometry" "$TMPDIR/test_geometry.cpp" -I/usr/include
echo "Running geometry test..."
"$TMPDIR/test_geometry"
