// Copyright 2013 Daniel Parker
// Distributed under Boost license

#if defined(_MSC_VER)
#include "windows.h" // test no inadvertant macro expansions
#endif
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/json_query.hpp>
#include <catch/catch.hpp>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <utility>
#include <ctime>
#include <new>

using namespace jsoncons;

TEST_CASE("jsonpath function tests")
{
    const json store = json::parse(R"(
    {
        "store": {
            "book": [
                {
                    "category": "reference",
                    "author": "Nigel Rees",
                    "title": "Sayings of the Century",
                    "price": 8.95
                },
                {
                    "category": "fiction",
                    "author": "Evelyn Waugh",
                    "title": "Sword of Honour",
                    "price": 12.99
                },
                {
                    "category": "fiction",
                    "author": "Herman Melville",
                    "title": "Moby Dick",
                    "isbn": "0-553-21311-3",
                    "price": 8.99
                },
                {
                    "category": "fiction",
                    "author": "J. R. R. Tolkien",
                    "title": "The Lord of the Rings",
                    "isbn": "0-395-19395-8",
                    "price": 22.99
                }
            ],
            "bicycle": {
                "color": "red",
                "price": 19.95
            }
        }
    }
    )");

    SECTION("count")
    {
        json result = jsonpath::json_query(store,"count($.store.book[*])");

        std::size_t expected = 4;

        REQUIRE(result.size() == 1);
        CHECK(result[0].as<std::size_t>() == expected);
    }

    SECTION("keys")
    {
        json result = jsonpath::json_query(store,"keys($.store.book[0])[*]");

        json expected = json::parse("[\"author\",\"category\",\"price\",\"title\"]");

        REQUIRE(result.size() == 4);
        CHECK(result == expected);
    }
    SECTION("sum")
    {
        json result = jsonpath::json_query(store,"sum($.store.book[*].price)");
        double expected = 53.92;
        REQUIRE(result.size() == 1);
        CHECK(result[0].as<double>() == Approx(expected).epsilon(0.000001));
    }
    SECTION("sum in filter")
    {
        json result = jsonpath::json_query(store,"$.store.book[?(@.price > sum($.store.book[*].price) / count($.store.book[*]))].title");
        std::string expected = "The Lord of the Rings";
        REQUIRE(result.size() == 1);
        CHECK(result[0].as<std::string>() == expected);
    }
    SECTION("avg")
    {
        json result = jsonpath::json_query(store,"avg($.store.book[*].price)");

        double expected = 13.48;

        REQUIRE(result.size() == 1);
        CHECK(result[0].as<double>() == Approx(expected).epsilon(0.000001));
    }
    SECTION("avg in filter")
    {
        json result = jsonpath::json_query(store,"$.store.book[?(@.price > avg($.store.book[*].price))].title");
        std::string expected = "The Lord of the Rings";
        REQUIRE(result.size() == 1);
        CHECK(result[0].as<std::string>() == expected);
    }

    SECTION("prod")
    {
        json result = jsonpath::json_query(store,"prod($.store.book[*].price)");

        double expected = 24028.731766049998;

        REQUIRE(result.size() == 1);
        CHECK(result[0].as<double>() == Approx(expected).epsilon(0.000001));
    }

    SECTION("min")
    {
        json result = jsonpath::json_query(store,"min($.store.book[*].price)");

        double expected = 8.95;

        REQUIRE(result.size() == 1);
        CHECK(result[0].as<double>() == Approx(expected).epsilon(0.000001));
    }

    SECTION("max")
    {
        json result = jsonpath::json_query(store,"max($.store.book[*].price)");

        double expected = 22.99;

        REQUIRE(result.size() == 1);
        CHECK(result[0].as<double>() == Approx(expected).epsilon(0.000001));
    }

    SECTION("max in filter")
    {
        std::string path = "$.store.book[?(@.price < max($.store.book[*].price))].title";

        json expected = json::parse(R"(
    ["Sayings of the Century","Sword of Honour","Moby Dick"]
        )");

        json result = jsonpath::json_query(store,path);

        CHECK(result == expected);
    }

#if defined(JSONCONS_HAS_STD_REGEX)
    SECTION("tokenize")
    {
        json j("The cat sat on the mat");
        json result = jsonpath::json_query(j,"tokenize($,'\\\\s+')[*]");

        json expected = json::parse("[\"The\",\"cat\",\"sat\",\"on\",\"the\",\"mat\"]");

        CHECK(result == expected);
    }
    SECTION("tokenize in filter")
    {
        json j = json::parse("[\"The cat sat on the mat\",\"The cat on the mat\"]");
        json result = jsonpath::json_query(j,"$[?(tokenize(@,'\\\\s+')[2]=='sat')]");

        CHECK(result[0] == j[0]);
    }
    SECTION("tokenize in filter 2")
    {
        json result = jsonpath::json_query(store,"$.store.book[?(tokenize(@.author,'\\\\s+')[1] == 'Waugh')].title");
        std::string expected = "Sword of Honour";
        REQUIRE(result.size() == 1);
        CHECK(result[0].as<std::string>() == expected);
    }
#endif
}


