#include <catch.hpp>
#include <hocon/parser/config_document_factory.hpp>
#include <internal/simple_config_document.hpp>
#include "fixtures.hpp"

using namespace hocon;
using namespace std;

void replace_test(string const& original_text, string const& final_text, string const& new_value,
                  string const& replace_path, config_syntax syntax) {
    auto config_doc = config_document_factory::parse_string(original_text,
                                                            config_parse_options().set_syntax(syntax));
    REQUIRE(original_text == config_doc->render());
    auto new_doc = config_doc->with_value_text(replace_path, new_value);
    REQUIRE(final_text == new_doc->render());
}

TEST_CASE("replacement of elements in a document", "[config-doc]") {
    SECTION("replacements in a simple map") {
        replace_test("{\"a\":1}", "{\"a\":2}", "2", "a", config_syntax::JSON);
        replace_test("{\"a\":1}", "{\"a\":2}", "2", "a", config_syntax::CONF);
    }

    SECTION("replacement in a map without braces") {
        replace_test("a: b\nc = d", "a: b\nc = 12", "12", "c", config_syntax::CONF);
    }

    string original_text = "{\n\"a\":123,\n\"b\": 123.456,\n\"c\": true,\n\"d\": false,\n\"e\": null,\n"
        "\"f\": \"a string\",\n\"g\": [1,2,3,4,5],\n\"h\": {\n\t\"a\": 123,\n\t\"b\": {\n\t\t\"a\": 12\n\t},\n"
        "\"c\": [1, 2, 3, {\"a\": \"b\"}, [1,2,3]]\n}\n}";
    SECTION("replacements in a complex map") {
        string final_text = "{\n\"a\":123,\n\"b\": 123.456,\n\"c\": true,\n\"d\": false,\n\"e\": null,\n"
                "\"f\": \"a string\",\n\"g\": [1,2,3,4,5],\n\"h\": {\n\t\"a\": 123,\n\t\"b\": {\n\t\t\"a\": "
                "\"i am now a string\"\n\t},\n\"c\": [1, 2, 3, {\"a\": \"b\"}, [1,2,3]]\n}\n}";
        replace_test(original_text, final_text, "\"i am now a string\"", "h.b.a", config_syntax::JSON);
        replace_test(original_text, final_text, "\"i am now a string\"", "h.b.a", config_syntax::CONF);
    }

    SECTION("replace a value with a map") {
        string final_text = "{\n\"a\":123,\n\"b\": 123.456,\n\"c\": true,\n\"d\": false,\n\"e\": null,\n"
                "\"f\": \"a string\",\n\"g\": [1,2,3,4,5],\n\"h\": {\n\t\"a\": 123,\n\t\"b\": {\n\t\t\"a\": "
                "{\"a\":\"b\", \"c\":\"d\"}\n\t},\n\"c\": [1, 2, 3, {\"a\": \"b\"}, [1,2,3]]\n}\n}";
        replace_test(original_text, final_text, "{\"a\":\"b\", \"c\":\"d\"}", "h.b.a", config_syntax::JSON);
        replace_test(original_text, final_text, "{\"a\":\"b\", \"c\":\"d\"}", "h.b.a", config_syntax::CONF);
    }

    SECTION("replace a value with an array") {
        string final_text = "{\n\"a\":123,\n\"b\": 123.456,\n\"c\": true,\n\"d\": false,\n\"e\": null,\n"
                "\"f\": \"a string\",\n\"g\": [1,2,3,4,5],\n\"h\": {\n\t\"a\": 123,\n\t\"b\": {\n\t\t\"a\": "
                "[1,2,3,4,5]\n\t},\n\"c\": [1, 2, 3, {\"a\": \"b\"}, [1,2,3]]\n}\n}";
        replace_test(original_text, final_text, "[1,2,3,4,5]", "h.b.a", config_syntax::JSON);
        replace_test(original_text, final_text, "[1,2,3,4,5]", "h.b.a", config_syntax::CONF);
    }

    SECTION("replace a value with a concatenation") {
        string final_text = "{\n\"a\":123,\n\"b\": 123.456,\n\"c\": true,\n\"d\": false,\n\"e\": null,\n"
                "\"f\": \"a string\",\n\"g\": [1,2,3,4,5],\n\"h\": {\n\t\"a\": 123,\n\t\"b\": {\n\t\t\"a\": "
                "this is a concatenation 123 456 {a:b} [1,2,3] {a: this is another 123 concatenation null true}\n\t},\n"
                "\"c\": [1, 2, 3, {\"a\": \"b\"}, [1,2,3]]\n}\n}";
        replace_test(original_text, final_text, "this is a concatenation 123 456 {a:b} [1,2,3] "
                "{a: this is another 123 concatenation null true}", "h.b.a", config_syntax::CONF);
    }
}

TEST_CASE("removal of duplicates", "[config-doc]") {
    string original_text = "{a: b, a.b.c: d, a: e}";
    auto config_doc = config_document_factory::parse_string(original_text);
    REQUIRE("{a: 2}" == config_doc->with_value_text("a", "2")->render());

    original_text = "{a: b, a: e, a.b.c:d}";
    config_doc = config_document_factory::parse_string(original_text);
    REQUIRE("{a: 2, }" == config_doc->with_value_text("a", "2")->render());

    original_text = "{a.b.c: d}";
    config_doc = config_document_factory::parse_string(original_text);
    REQUIRE("{ a : 2}" == config_doc->with_value_text("a", "2")->render());
}

TEST_CASE("set new value", "[config-doc]") {
    SECTION("braced root node") {
        string original_text = "{\n\t\"a\":\"b\",\n\t\"c\":\"d\"\n}";
        string final_text_conf = "{\n\t\"a\":\"b\",\n\t\"c\":\"d\"\n\t\"e\" : \"f\"\n}";
        string final_text_json = "{\n\t\"a\":\"b\",\n\t\"c\":\"d\",\n\t\"e\" : \"f\"\n}";
        replace_test(original_text, final_text_json, "\"f\"", "\"e\"", config_syntax::JSON);
        replace_test(original_text, final_text_conf, "\"f\"", "\"e\"", config_syntax::CONF);
    }

    SECTION("no brace at root node") {
        string original_text = "\"a\":\"b\",\n\"c\":\"d\"\n";
        string final_text = "\"a\":\"b\",\n\"c\":\"d\"\n\"e\" : \"f\"\n";
        replace_test(original_text, final_text, "\"f\"", "\"e\"", config_syntax::CONF);
    }

    SECTION("multi-level") {
        string original_text = "a:b\nc:d";
        string final_text = "a:b\nc:d\ne : {\n  f : {\n    g : 12\n  }\n}";
        replace_test(original_text, final_text, "12", "e.f.g", config_syntax::CONF);

        original_text = "{\"a\":\"b\",\n\"c\":\"d\"}";
        final_text = "{\"a\":\"b\",\n\"c\":\"d\",\n  \"e\" : {\n    \"f\" : {\n      \"g\" : 12\n    }\n  }}";
        replace_test(original_text, final_text, "12", "e.f.g", config_syntax::JSON);
    }
}

TEST_CASE("parse from file", "[doc-parser]") {
    string original_text = "{ a : b }";
    string directory = TEST_FILE_DIR;
    auto doc = config_document_factory::parse_file(directory + "/test.conf");
    REQUIRE(doc->render() == original_text);
}