#include "catch.hpp"

#include <sstream>
#include <type_traits>

#include <osmium/osm/location.hpp>

TEST_CASE("Location") {

// fails on MSVC and doesn't really matter
// static_assert(std::is_literal_type<osmium::Location>::value, "osmium::Location not literal type");

SECTION("instantiation_with_default_parameters") {
    osmium::Location loc;
    REQUIRE(!loc);
    REQUIRE_THROWS_AS(loc.lon(), osmium::invalid_location);
    REQUIRE_THROWS_AS(loc.lat(), osmium::invalid_location);
}

SECTION("instantiation_with_double_parameters") {
    osmium::Location loc1(1.2, 4.5);
    REQUIRE(!!loc1);
    REQUIRE(12000000 == loc1.x());
    REQUIRE(45000000 == loc1.y());
    REQUIRE(1.2 == loc1.lon());
    REQUIRE(4.5 == loc1.lat());

    osmium::Location loc2(loc1);
    REQUIRE(4.5 == loc2.lat());

    osmium::Location loc3 = loc1;
    REQUIRE(4.5 == loc3.lat());
}

SECTION("instantiation_with_double_parameters_constructor_with_universal_initializer") {
    osmium::Location loc { 2.2, 3.3 };
    REQUIRE(2.2 == loc.lon());
    REQUIRE(3.3 == loc.lat());
}

SECTION("instantiation_with_double_parameters_constructor_with_initializer_list") {
    osmium::Location loc({ 4.4, 5.5 });
    REQUIRE(4.4 == loc.lon());
    REQUIRE(5.5 == loc.lat());
}

SECTION("instantiation_with_double_parameters_operator_equal") {
    osmium::Location loc = { 5.5, 6.6 };
    REQUIRE(5.5 == loc.lon());
    REQUIRE(6.6 == loc.lat());
}

SECTION("equality") {
    osmium::Location loc1(1.2, 4.5);
    osmium::Location loc2(1.2, 4.5);
    osmium::Location loc3(1.5, 1.5);
    REQUIRE(loc1 == loc2);
    REQUIRE(loc1 != loc3);
}

SECTION("order") {
    REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0));
    REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0));

    REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2));
    REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0));
    REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0));
}

SECTION("validity") {
    REQUIRE(osmium::Location(0.0, 0.0).valid());
    REQUIRE(osmium::Location(1.2, 4.5).valid());
    REQUIRE(osmium::Location(-1.2, 4.5).valid());
    REQUIRE(osmium::Location(-180.0, -90.0).valid());
    REQUIRE(osmium::Location(180.0, -90.0).valid());
    REQUIRE(osmium::Location(-180.0, 90.0).valid());
    REQUIRE(osmium::Location(180.0, 90.0).valid());

    REQUIRE(!osmium::Location(200.0, 4.5).valid());
    REQUIRE(!osmium::Location(-1.2, -100.0).valid());
    REQUIRE(!osmium::Location(-180.0, 90.005).valid());
}


SECTION("output_to_iterator_comma_separator") {
    char buffer[100];
    osmium::Location loc(-3.2, 47.3);
    *loc.as_string(buffer, ',') = 0;
    REQUIRE(std::string("-3.2,47.3") == buffer);
}

SECTION("output_to_iterator_space_separator") {
    char buffer[100];
    osmium::Location loc(0.0, 7.0);
    *loc.as_string(buffer, ' ') = 0;
    REQUIRE(std::string("0 7") == buffer);
}

SECTION("output_to_iterator_check_precision") {
    char buffer[100];
    osmium::Location loc(-179.9999999, -90.0);
    *loc.as_string(buffer, ' ') = 0;
    REQUIRE(std::string("-179.9999999 -90") == buffer);
}

SECTION("output_to_iterator_undefined_location") {
    char buffer[100];
    osmium::Location loc;
    REQUIRE_THROWS_AS(loc.as_string(buffer, ','), osmium::invalid_location);
}

SECTION("output_to_string_comman_separator") {
    std::string s;
    osmium::Location loc(-3.2, 47.3);
    loc.as_string(std::back_inserter(s), ',');
    REQUIRE(s == "-3.2,47.3");
}

SECTION("output_to_string_space_separator") {
    std::string s;
    osmium::Location loc(0.0, 7.0);
    loc.as_string(std::back_inserter(s), ' ');
    REQUIRE(s == "0 7");
}

SECTION("output_to_string_check_precision") {
    std::string s;
    osmium::Location loc(-179.9999999, -90.0);
    loc.as_string(std::back_inserter(s), ' ');
    REQUIRE(s == "-179.9999999 -90");
}

SECTION("output_to_string_undefined_location") {
    std::string s;
    osmium::Location loc;
    REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), osmium::invalid_location);
}

SECTION("output_defined") {
    osmium::Location p(-3.2, 47.3);
    std::stringstream out;
    out << p;
    REQUIRE(out.str() == "(-3.2,47.3)");
}

SECTION("output_undefined") {
    osmium::Location p;
    std::stringstream out;
    out << p;
    REQUIRE(out.str() == "(undefined,undefined)");
}

}