/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#ifndef NLOHMANN_JSON_SCHEMA_HPP__
#define NLOHMANN_JSON_SCHEMA_HPP__

#ifdef _WIN32
#	if defined(JSON_SCHEMA_VALIDATOR_EXPORTS)
#		define JSON_SCHEMA_VALIDATOR_API __declspec(dllexport)
#	elif defined(JSON_SCHEMA_VALIDATOR_IMPORTS)
#		define JSON_SCHEMA_VALIDATOR_API __declspec(dllimport)
#	else
#		define JSON_SCHEMA_VALIDATOR_API
#	endif
#else
#	define JSON_SCHEMA_VALIDATOR_API
#endif

#include <nlohmann/json.hpp>

#ifdef NLOHMANN_JSON_VERSION_MAJOR
#	if NLOHMANN_JSON_VERSION_MAJOR < 3 || NLOHMANN_JSON_VERSION_MINOR < 5 || NLOHMANN_JSON_VERSION_PATCH < 0
#		error "Please use this library with NLohmann's JSON version 3.5.0 or higher"
#	endif
#else
#	error "expected existing NLOHMANN_JSON_VERSION_MAJOR preproc variable, please update to NLohmann's JSON 3.5.0"
#endif

// make yourself a home - welcome to nlohmann's namespace
namespace nlohmann
{

// A class representing a JSON-URI for schemas derived from
// section 8 of JSON Schema: A Media Type for Describing JSON Documents
// draft-wright-json-schema-00
//
// New URIs can be derived from it using the derive()-method.
// This is useful for resolving refs or subschema-IDs in json-schemas.
//
// This is done implement the requirements described in section 8.2.
//
class JSON_SCHEMA_VALIDATOR_API json_uri
{
	std::string urn_;

	std::string proto_;
	std::string hostname_;
	std::string path_;
	nlohmann::json::json_pointer pointer_;

protected:
	// decodes a JSON uri and replaces all or part of the currently stored values
	void update(const std::string &uri);

	std::tuple<std::string, std::string, std::string, std::string, std::string> tie() const
	{
		return std::tie(urn_, proto_, hostname_, path_, pointer_);
	}

public:
	json_uri(const std::string &uri)
	{
		update(uri);
	}

	const std::string protocol() const { return proto_; }
	const std::string hostname() const { return hostname_; }
	const std::string path() const { return path_; }

	const nlohmann::json::json_pointer pointer() const { return pointer_; }

	const std::string url() const { return location(); }
	const std::string location() const;

	static std::string escape(const std::string &);

	// create a new json_uri based in this one and the given uri
	// resolves relative changes (pathes or pointers) and resets part if proto or hostname changes
	json_uri derive(const std::string &uri) const
	{
		json_uri u = *this;
		u.update(uri);
		return u;
	}

	// append a pointer-field to the pointer-part of this uri
	json_uri append(const std::string &field) const
	{
		json_uri u = *this;
		u.pointer_ = nlohmann::json::json_pointer(u.pointer_.to_string() + '/' + escape(field));
		return u;
	}

	std::string to_string() const;

	friend bool operator<(const json_uri &l, const json_uri &r)
	{
		return l.tie() < r.tie();
	}

	friend bool operator==(const json_uri &l, const json_uri &r)
	{
		return l.tie() == r.tie();
	}

	friend std::ostream &operator<<(std::ostream &os, const json_uri &u);
};

namespace json_schema
{

extern json draft7_schema_builtin;

class basic_error_handler
{
	bool error_{false};

public:
	virtual void error(const std::string & /*path*/, const json & /* instance */, const std::string & /*message*/)
	{
		error_ = true;
	}

	void reset() { error_ = false; }
	operator bool() const { return error_; }
};

class root_schema;

class JSON_SCHEMA_VALIDATOR_API json_validator
{
	std::unique_ptr<root_schema> root_;

public:
	json_validator(std::function<void(const json_uri &, json &)> loader = nullptr,
	               std::function<void(const std::string &, const std::string &)> format = nullptr);
	~json_validator();

	// insert and set thea root-schema
	void set_root_schema(const json &);

	// validate a json-document based on the root-schema
	void validate(const json &);

	// validate a json-document based on the root-schema with a custom error-handler
	void validate(const json &, basic_error_handler &);
};

} // namespace json_schema
} // namespace nlohmann

#endif /* NLOHMANN_JSON_SCHEMA_HPP__ */
