///
/// T I L E S O N   V E R S I O N   1 . 4 . 0
/// ------------------------------------------------
/// BSD 2-Clause License
///
/// Copyright (c) 2022, Robin Berg Pettersen
/// All rights reserved.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice, this
///    list of conditions and the following disclaimer.
///
/// 2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation
///    and/or other materials provided with the distribution.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
/// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
/// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
/// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
/// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
/// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
/// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef TILESON_TILESON_H
#define TILESON_TILESON_H


/*** Start of inlined file: json11.hpp ***/
/*** Start of inlined file: json11.cpp ***/

/*** Start of inlined file: json11.hpp ***/
/* Copyright (c) 2013 Dropbox, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <initializer_list>

#define JSON11_IS_DEFINED

#ifdef _MSC_VER
#if _MSC_VER <= 1800 // VS 2013
		#ifndef noexcept
			#define noexcept throw()
		#endif

		#ifndef snprintf
			#define snprintf _snprintf_s
		#endif
	#endif
#endif

namespace json11 {

	enum JsonParse {
		STANDARD, COMMENTS
	};

	class JsonValue;

	class Json final {
		public:
			// Types
			enum Type {
				NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
			};

			// Array and object typedefs
			typedef std::vector<Json> array;
			typedef std::map<std::string, Json> object;

			// Constructors for the various types of JSON value.
			inline Json() noexcept;                // NUL
			inline Json(std::nullptr_t) noexcept;  // NUL
			inline Json(double value);             // NUMBER
			inline Json(int value);                // NUMBER
			inline Json(bool value);               // BOOL
			inline Json(const std::string &value); // STRING
			inline Json(std::string &&value);      // STRING
			inline Json(const char * value);       // STRING
			inline Json(const array &values);      // ARRAY
			inline Json(array &&values);           // ARRAY
			inline Json(const object &values);     // OBJECT
			inline Json(object &&values);          // OBJECT

			// Implicit constructor: anything with a to_json() function.
			template <class T, class = decltype(&T::to_json)>
			inline Json(const T & t) : Json(t.to_json()) {}

			// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
			template <class M, typename std::enable_if<
					std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
					&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
					int>::type = 0>
			inline Json(const M & m) : Json(object(m.begin(), m.end())) {}

			// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
			template <class V, typename std::enable_if<
					std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
					int>::type = 0>
			inline Json(const V & v) : Json(array(v.begin(), v.end())) {}

			// This prevents Json(some_pointer) from accidentally producing a bool. Use
			// Json(bool(some_pointer)) if that behavior is desired.
			Json(void *) = delete;

			// Accessors
			inline Type type() const;

			inline bool is_null()   const { return type() == NUL; }
			inline bool is_number() const { return type() == NUMBER; }
			inline bool is_bool()   const { return type() == BOOL; }
			inline bool is_string() const { return type() == STRING; }
			inline bool is_array()  const { return type() == ARRAY; }
			inline bool is_object() const { return type() == OBJECT; }

			// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
			// distinguish between integer and non-integer numbers - number_value() and int_value()
			// can both be applied to a NUMBER-typed object.
			inline double number_value() const;
			inline int int_value() const;

			// Return the enclosed value if this is a boolean, false otherwise.
			inline bool bool_value() const;
			// Return the enclosed string if this is a string, "" otherwise.
			inline const std::string &string_value() const;
			// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
			inline const array &array_items() const;
			// Return the enclosed std::map if this is an object, or an empty map otherwise.
			inline const object &object_items() const;

			// Return a reference to arr[i] if this is an array, Json() otherwise.
			inline const Json & operator[](size_t i) const;
			// Return a reference to obj[key] if this is an object, Json() otherwise.
			inline const Json & operator[](const std::string &key) const;

			// Serialize.
			inline void dump(std::string &out) const;
			inline std::string dump() const {
				std::string out;
				dump(out);
				return out;
			}

			// Parse. If parse fails, return Json() and assign an error message to err.
			static inline Json parse(const std::string & in,
							  std::string & err,
							  JsonParse strategy = JsonParse::STANDARD);
			static inline Json parse(const char * in,
							  std::string & err,
							  JsonParse strategy = JsonParse::STANDARD) {
				if (in) {
					return parse(std::string(in), err, strategy);
				} else {
					err = "null input";
					return nullptr;
				}
			}
			// Parse multiple objects, concatenated or separated by whitespace
			static inline std::vector<Json> parse_multi(
					const std::string & in,
					std::string::size_type & parser_stop_pos,
					std::string & err,
					JsonParse strategy = JsonParse::STANDARD);

			static inline std::vector<Json> parse_multi(
					const std::string & in,
					std::string & err,
					JsonParse strategy = JsonParse::STANDARD) {
				std::string::size_type parser_stop_pos;
				return parse_multi(in, parser_stop_pos, err, strategy);
			}

			inline bool operator== (const Json &rhs) const;
			inline bool operator<  (const Json &rhs) const;
			inline bool operator!= (const Json &rhs) const { return !(*this == rhs); }
			inline bool operator<= (const Json &rhs) const { return !(rhs < *this); }
			inline bool operator>  (const Json &rhs) const { return  (rhs < *this); }
			inline bool operator>= (const Json &rhs) const { return !(*this < rhs); }

			/* has_shape(types, err)
			 *
			 * Return true if this is a JSON object and, for each item in types, has a field of
			 * the given type. If not, return false and set err to a descriptive message.
			 */
			typedef std::initializer_list<std::pair<std::string, Type>> shape;
			inline bool has_shape(const shape & types, std::string & err) const;

		private:
			std::shared_ptr<JsonValue> m_ptr;
	};

// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
	class JsonValue {
		protected:
			friend class Json;
			friend class JsonInt;
			friend class JsonDouble;
			virtual Json::Type type() const = 0;
			virtual bool equals(const JsonValue * other) const = 0;
			virtual bool less(const JsonValue * other) const = 0;
			virtual void dump(std::string &out) const = 0;
			virtual double number_value() const;
			virtual int int_value() const;
			virtual bool bool_value() const;
			virtual const std::string &string_value() const;
			virtual const Json::array &array_items() const;
			virtual const Json &operator[](size_t i) const;
			virtual const Json::object &object_items() const;
			virtual const Json &operator[](const std::string &key) const;
			virtual ~JsonValue() {}
	};

} // namespace json11

/*** End of inlined file: json11.hpp ***/

#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>

namespace json11 {

	static const int max_depth = 200;

	using std::string;
	using std::vector;
	using std::map;
	using std::make_shared;
	using std::initializer_list;
	using std::move;

/* Helper for representing null - just a do-nothing struct, plus comparison
 * operators so the helpers in JsonValue work. We can't use nullptr_t because
 * it may not be orderable.
 */
	struct NullStruct {
		bool operator==(NullStruct) const { return true; }
		bool operator<(NullStruct) const { return false; }
	};

/* * * * * * * * * * * * * * * * * * * *
 * Serialization
 */

	static void dump(NullStruct, string &out) {
		out += "null";
	}

	static void dump(double value, string &out) {
		if (std::isfinite(value)) {
			char buf[32];
			snprintf(buf, sizeof buf, "%.17g", value);
			out += buf;
		} else {
			out += "null";
		}
	}

	static void dump(int value, string &out) {
		char buf[32];
		snprintf(buf, sizeof buf, "%d", value);
		out += buf;
	}

	static void dump(bool value, string &out) {
		out += value ? "true" : "false";
	}

	static void dump(const string &value, string &out) {
		out += '"';
		for (size_t i = 0; i < value.length(); i++) {
			const char ch = value[i];
			if (ch == '\\') {
				out += "\\\\";
			} else if (ch == '"') {
				out += "\\\"";
			} else if (ch == '\b') {
				out += "\\b";
			} else if (ch == '\f') {
				out += "\\f";
			} else if (ch == '\n') {
				out += "\\n";
			} else if (ch == '\r') {
				out += "\\r";
			} else if (ch == '\t') {
				out += "\\t";
			} else if (static_cast<uint8_t>(ch) <= 0x1f) {
				char buf[8];
				snprintf(buf, sizeof buf, "\\u%04x", ch);
				out += buf;
			} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
					   && static_cast<uint8_t>(value[i+2]) == 0xa8) {
				out += "\\u2028";
				i += 2;
			} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
					   && static_cast<uint8_t>(value[i+2]) == 0xa9) {
				out += "\\u2029";
				i += 2;
			} else {
				out += ch;
			}
		}
		out += '"';
	}

	static void dump(const Json::array &values, string &out) {
		bool first = true;
		out += "[";
		for (const auto &value : values) {
			if (!first)
				out += ", ";
			value.dump(out);
			first = false;
		}
		out += "]";
	}

	static void dump(const Json::object &values, string &out) {
		bool first = true;
		out += "{";
		for (const auto &kv : values) {
			if (!first)
				out += ", ";
			dump(kv.first, out);
			out += ": ";
			kv.second.dump(out);
			first = false;
		}
		out += "}";
	}

	void Json::dump(string &out) const {
		m_ptr->dump(out);
	}

/* * * * * * * * * * * * * * * * * * * *
 * Value wrappers
 */

	template <Json::Type tag, typename T>
	class Value : public JsonValue {
		protected:

			// Constructors
			explicit Value(const T &value) : m_value(value) {}
			explicit Value(T &&value)      : m_value(move(value)) {}

			// Get type tag
			Json::Type type() const override {
				return tag;
			}

			// Comparisons
			bool equals(const JsonValue * other) const override {
				return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
			}
			bool less(const JsonValue * other) const override {
				return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
			}

			const T m_value;
			void dump(string &out) const override { json11::dump(m_value, out); }
	};

	class JsonDouble final : public Value<Json::NUMBER, double> {
			double number_value() const override { return m_value; }
			int int_value() const override { return static_cast<int>(m_value); }
			bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
			bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }
		public:
			explicit JsonDouble(double value) : Value(value) {}
	};

	class JsonInt final : public Value<Json::NUMBER, int> {
			double number_value() const override { return m_value; }
			int int_value() const override { return m_value; }
			bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
			bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }
		public:
			explicit JsonInt(int value) : Value(value) {}
	};

	class JsonBoolean final : public Value<Json::BOOL, bool> {
			bool bool_value() const override { return m_value; }
		public:
			explicit JsonBoolean(bool value) : Value(value) {}
	};

	class JsonString final : public Value<Json::STRING, string> {
			const string &string_value() const override { return m_value; }
		public:
			explicit JsonString(const string &value) : Value(value) {}
			explicit JsonString(string &&value)      : Value(move(value)) {}
	};

	class JsonArray final : public Value<Json::ARRAY, Json::array> {
			const Json::array &array_items() const override { return m_value; }
			const Json & operator[](size_t i) const override;
		public:
			explicit JsonArray(const Json::array &value) : Value(value) {}
			explicit JsonArray(Json::array &&value)      : Value(move(value)) {}
	};

	class JsonObject final : public Value<Json::OBJECT, Json::object> {
			const Json::object &object_items() const override { return m_value; }
			const Json & operator[](const string &key) const override;
		public:
			explicit JsonObject(const Json::object &value) : Value(value) {}
			explicit JsonObject(Json::object &&value)      : Value(move(value)) {}
	};

	class JsonNull final : public Value<Json::NUL, NullStruct> {
		public:
			JsonNull() : Value({}) {}
	};

/* * * * * * * * * * * * * * * * * * * *
 * Static globals - static-init-safe
 */
	struct Statics {
		const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
		const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
		const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
		const string empty_string;
		const vector<Json> empty_vector;
		const map<string, Json> empty_map;
		Statics() {}
	};

	static const Statics & statics() {
		static const Statics s {};
		return s;
	}

	static const Json & static_null() {
		// This has to be separate, not in Statics, because Json() accesses statics().null.
		static const Json json_null;
		return json_null;
	}

/* * * * * * * * * * * * * * * * * * * *
 * Constructors
 */

	Json::Json() noexcept                  : m_ptr(statics().null) {}
	Json::Json(std::nullptr_t) noexcept    : m_ptr(statics().null) {}
	Json::Json(double value)               : m_ptr(make_shared<JsonDouble>(value)) {}
	Json::Json(int value)                  : m_ptr(make_shared<JsonInt>(value)) {}
	Json::Json(bool value)                 : m_ptr(value ? statics().t : statics().f) {}
	Json::Json(const string &value)        : m_ptr(make_shared<JsonString>(value)) {}
	Json::Json(string &&value)             : m_ptr(make_shared<JsonString>(move(value))) {}
	Json::Json(const char * value)         : m_ptr(make_shared<JsonString>(value)) {}
	Json::Json(const Json::array &values)  : m_ptr(make_shared<JsonArray>(values)) {}
	Json::Json(Json::array &&values)       : m_ptr(make_shared<JsonArray>(move(values))) {}
	Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
	Json::Json(Json::object &&values)      : m_ptr(make_shared<JsonObject>(move(values))) {}

/* * * * * * * * * * * * * * * * * * * *
 * Accessors
 */

	inline Json::Type Json::type()                           const { return m_ptr->type();         }
	inline double Json::number_value()                       const { return m_ptr->number_value(); }
	inline int Json::int_value()                             const { return m_ptr->int_value();    }
	inline bool Json::bool_value()                           const { return m_ptr->bool_value();   }
	inline const string & Json::string_value()               const { return m_ptr->string_value(); }
	inline const vector<Json> & Json::array_items()          const { return m_ptr->array_items();  }
	inline const map<string, Json> & Json::object_items()    const { return m_ptr->object_items(); }
	inline const Json & Json::operator[] (size_t i)          const { return (*m_ptr)[i];           }
	inline const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key];         }

	inline double                    JsonValue::number_value()              const { return 0; }
	inline int                       JsonValue::int_value()                 const { return 0; }
	inline bool                      JsonValue::bool_value()                const { return false; }
	inline const string &            JsonValue::string_value()              const { return statics().empty_string; }
	inline const vector<Json> &      JsonValue::array_items()               const { return statics().empty_vector; }
	inline const map<string, Json> & JsonValue::object_items()              const { return statics().empty_map; }
	inline const Json &              JsonValue::operator[] (size_t)         const { return static_null(); }
	inline const Json &              JsonValue::operator[] (const string &) const { return static_null(); }

	inline const Json & JsonObject::operator[] (const string &key) const {
		auto iter = m_value.find(key);
		return (iter == m_value.end()) ? static_null() : iter->second;
	}
	inline const Json & JsonArray::operator[] (size_t i) const {
		if (i >= m_value.size()) return static_null();
		else return m_value[i];
	}

/* * * * * * * * * * * * * * * * * * * *
 * Comparison
 */

	bool Json::operator== (const Json &other) const {
		if (m_ptr == other.m_ptr)
			return true;
		if (m_ptr->type() != other.m_ptr->type())
			return false;

		return m_ptr->equals(other.m_ptr.get());
	}

	bool Json::operator< (const Json &other) const {
		if (m_ptr == other.m_ptr)
			return false;
		if (m_ptr->type() != other.m_ptr->type())
			return m_ptr->type() < other.m_ptr->type();

		return m_ptr->less(other.m_ptr.get());
	}

/* * * * * * * * * * * * * * * * * * * *
 * Parsing
 */

/* esc(c)
 *
 * Format char c suitable for printing in an error message.
 */
	static inline string esc(char c) {
		char buf[12];
		if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
			snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
		} else {
			snprintf(buf, sizeof buf, "(%d)", c);
		}
		return string(buf);
	}

	static inline bool in_range(long x, long lower, long upper) {
		return (x >= lower && x <= upper);
	}

	namespace {
/* JsonParser
 *
 * Object that tracks all state of an in-progress parse.
 */
		struct JsonParser final {

			/* State
			 */
			const string &str;
			size_t i;
			string &err;
			bool failed;
			const JsonParse strategy;

			/* fail(msg, err_ret = Json())
			 *
			 * Mark this parse as failed.
			 */
			Json fail(string &&msg) {
				return fail(move(msg), Json());
			}

			template <typename T>
			T fail(string &&msg, const T err_ret) {
				if (!failed)
					err = std::move(msg);
				failed = true;
				return err_ret;
			}

			/* consume_whitespace()
			 *
			 * Advance until the current character is non-whitespace.
			 */
			void consume_whitespace() {
				while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
					i++;
			}

			/* consume_comment()
			 *
			 * Advance comments (c-style inline and multiline).
			 */
			bool consume_comment() {
				bool comment_found = false;
				if (str[i] == '/') {
					i++;
					if (i == str.size())
						return fail("unexpected end of input after start of comment", false);
					if (str[i] == '/') { // inline comment
						i++;
						// advance until next line, or end of input
						while (i < str.size() && str[i] != '\n') {
							i++;
						}
						comment_found = true;
					}
					else if (str[i] == '*') { // multiline comment
						i++;
						if (i > str.size()-2)
							return fail("unexpected end of input inside multi-line comment", false);
						// advance until closing tokens
						while (!(str[i] == '*' && str[i+1] == '/')) {
							i++;
							if (i > str.size()-2)
								return fail(
										"unexpected end of input inside multi-line comment", false);
						}
						i += 2;
						comment_found = true;
					}
					else
						return fail("malformed comment", false);
				}
				return comment_found;
			}

			/* consume_garbage()
			 *
			 * Advance until the current character is non-whitespace and non-comment.
			 */
			void consume_garbage() {
				consume_whitespace();
				if(strategy == JsonParse::COMMENTS) {
					bool comment_found = false;
					do {
						comment_found = consume_comment();
						if (failed) return;
						consume_whitespace();
					}
					while(comment_found);
				}
			}

			/* get_next_token()
			 *
			 * Return the next non-whitespace character. If the end of the input is reached,
			 * flag an error and return 0.
			 */
			char get_next_token() {
				consume_garbage();
				if (failed) return static_cast<char>(0);
				if (i == str.size())
					return fail("unexpected end of input", static_cast<char>(0));

				return str[i++];
			}

			/* encode_utf8(pt, out)
			 *
			 * Encode pt as UTF-8 and add it to out.
			 */
			void encode_utf8(long pt, string & out) {
				if (pt < 0)
					return;

				if (pt < 0x80) {
					out += static_cast<char>(pt);
				} else if (pt < 0x800) {
					out += static_cast<char>((pt >> 6) | 0xC0);
					out += static_cast<char>((pt & 0x3F) | 0x80);
				} else if (pt < 0x10000) {
					out += static_cast<char>((pt >> 12) | 0xE0);
					out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
					out += static_cast<char>((pt & 0x3F) | 0x80);
				} else {
					out += static_cast<char>((pt >> 18) | 0xF0);
					out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
					out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
					out += static_cast<char>((pt & 0x3F) | 0x80);
				}
			}

			/* parse_string()
			 *
			 * Parse a string, starting at the current position.
			 */
			string parse_string() {
				string out;
				long last_escaped_codepoint = -1;
				while (true) {
					if (i == str.size())
						return fail("unexpected end of input in string", "");

					char ch = str[i++];

					if (ch == '"') {
						encode_utf8(last_escaped_codepoint, out);
						return out;
					}

					if (in_range(ch, 0, 0x1f))
						return fail("unescaped " + esc(ch) + " in string", "");

					// The usual case: non-escaped characters
					if (ch != '\\') {
						encode_utf8(last_escaped_codepoint, out);
						last_escaped_codepoint = -1;
						out += ch;
						continue;
					}

					// Handle escapes
					if (i == str.size())
						return fail("unexpected end of input in string", "");

					ch = str[i++];

					if (ch == 'u') {
						// Extract 4-byte escape sequence
						string esc = str.substr(i, 4);
						// Explicitly check length of the substring. The following loop
						// relies on std::string returning the terminating NUL when
						// accessing str[length]. Checking here reduces brittleness.
						if (esc.length() < 4) {
							return fail("bad \\u escape: " + esc, "");
						}
						for (size_t j = 0; j < 4; j++) {
							if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
								&& !in_range(esc[j], '0', '9'))
								return fail("bad \\u escape: " + esc, "");
						}

						long codepoint = strtol(esc.data(), nullptr, 16);

						// JSON specifies that characters outside the BMP shall be encoded as a pair
						// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
						// whether we're in the middle of such a beast: the previous codepoint was an
						// escaped lead (high) surrogate, and this is a trail (low) surrogate.
						if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
							&& in_range(codepoint, 0xDC00, 0xDFFF)) {
							// Reassemble the two surrogate pairs into one astral-plane character, per
							// the UTF-16 algorithm.
							encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
										 | (codepoint - 0xDC00)) + 0x10000, out);
							last_escaped_codepoint = -1;
						} else {
							encode_utf8(last_escaped_codepoint, out);
							last_escaped_codepoint = codepoint;
						}

						i += 4;
						continue;
					}

					encode_utf8(last_escaped_codepoint, out);
					last_escaped_codepoint = -1;

					if (ch == 'b') {
						out += '\b';
					} else if (ch == 'f') {
						out += '\f';
					} else if (ch == 'n') {
						out += '\n';
					} else if (ch == 'r') {
						out += '\r';
					} else if (ch == 't') {
						out += '\t';
					} else if (ch == '"' || ch == '\\' || ch == '/') {
						out += ch;
					} else {
						return fail("invalid escape character " + esc(ch), "");
					}
				}
			}

			/* parse_number()
			 *
			 * Parse a double.
			 */
			Json parse_number() {
				size_t start_pos = i;

				if (str[i] == '-')
					i++;

				// Integer part
				if (str[i] == '0') {
					i++;
					if (in_range(str[i], '0', '9'))
						return fail("leading 0s not permitted in numbers");
				} else if (in_range(str[i], '1', '9')) {
					i++;
					while (in_range(str[i], '0', '9'))
						i++;
				} else {
					return fail("invalid " + esc(str[i]) + " in number");
				}

				if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
					&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
					return std::atoi(str.c_str() + start_pos);
				}

				// Decimal part
				if (str[i] == '.') {
					i++;
					if (!in_range(str[i], '0', '9'))
						return fail("at least one digit required in fractional part");

					while (in_range(str[i], '0', '9'))
						i++;
				}

				// Exponent part
				if (str[i] == 'e' || str[i] == 'E') {
					i++;

					if (str[i] == '+' || str[i] == '-')
						i++;

					if (!in_range(str[i], '0', '9'))
						return fail("at least one digit required in exponent");

					while (in_range(str[i], '0', '9'))
						i++;
				}

				return std::strtod(str.c_str() + start_pos, nullptr);
			}

			/* expect(str, res)
			 *
			 * Expect that 'str' starts at the character that was just read. If it does, advance
			 * the input and return res. If not, flag an error.
			 */
			Json expect(const string &expected, Json res) {
				assert(i != 0);
				i--;
				if (str.compare(i, expected.length(), expected) == 0) {
					i += expected.length();
					return res;
				} else {
					return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
				}
			}

			/* parse_json()
			 *
			 * Parse a JSON object.
			 */
			Json parse_json(int depth) {
				if (depth > max_depth) {
					return fail("exceeded maximum nesting depth");
				}

				char ch = get_next_token();
				if (failed)
					return Json();

				if (ch == '-' || (ch >= '0' && ch <= '9')) {
					i--;
					return parse_number();
				}

				if (ch == 't')
					return expect("true", true);

				if (ch == 'f')
					return expect("false", false);

				if (ch == 'n')
					return expect("null", Json());

				if (ch == '"')
					return parse_string();

				if (ch == '{') {
					map<string, Json> data;
					ch = get_next_token();
					if (ch == '}')
						return data;

					while (1) {
						if (ch != '"')
							return fail("expected '\"' in object, got " + esc(ch));

						string key = parse_string();
						if (failed)
							return Json();

						ch = get_next_token();
						if (ch != ':')
							return fail("expected ':' in object, got " + esc(ch));

						data[std::move(key)] = parse_json(depth + 1);
						if (failed)
							return Json();

						ch = get_next_token();
						if (ch == '}')
							break;
						if (ch != ',')
							return fail("expected ',' in object, got " + esc(ch));

						ch = get_next_token();
					}
					return data;
				}

				if (ch == '[') {
					vector<Json> data;
					ch = get_next_token();
					if (ch == ']')
						return data;

					while (1) {
						i--;
						data.push_back(parse_json(depth + 1));
						if (failed)
							return Json();

						ch = get_next_token();
						if (ch == ']')
							break;
						if (ch != ',')
							return fail("expected ',' in list, got " + esc(ch));

						ch = get_next_token();
						(void)ch;
					}
					return data;
				}

				return fail("expected value, got " + esc(ch));
			}
		};
	}//namespace {

	Json Json::parse(const string &in, string &err, JsonParse strategy) {
		JsonParser parser { in, 0, err, false, strategy };
		Json result = parser.parse_json(0);

		// Check for any trailing garbage
		parser.consume_garbage();
		if (parser.failed)
			return Json();
		if (parser.i != in.size() &&
			((parser.i + 1) != in.size() && in[parser.i] != 0)) //RBP: If there is only 1 character diff, it is probably just a terminating zero from a memory read.
		{
			return parser.fail("unexpected trailing " + esc(in[parser.i]));
		}
		return result;
	}

// Documented in json11.hpp
	vector<Json> Json::parse_multi(const string &in,
								   std::string::size_type &parser_stop_pos,
								   string &err,
								   JsonParse strategy) {
		JsonParser parser { in, 0, err, false, strategy };
		parser_stop_pos = 0;
		vector<Json> json_vec;
		while (parser.i != in.size() && !parser.failed) {
			json_vec.push_back(parser.parse_json(0));
			if (parser.failed)
				break;

			// Check for another object
			parser.consume_garbage();
			if (parser.failed)
				break;
			parser_stop_pos = parser.i;
		}
		return json_vec;
	}

/* * * * * * * * * * * * * * * * * * * *
 * Shape-checking
 */

	bool Json::has_shape(const shape & types, string & err) const {
		if (!is_object()) {
			err = "expected JSON object, got " + dump();
			return false;
		}

		const auto& obj_items = object_items();
		for (auto & item : types) {
			const auto it = obj_items.find(item.first);
			if (it == obj_items.cend() || it->second.type() != item.second) {
				err = "bad type for " + item.first + " in " + dump();
				return false;
			}
		}

		return true;
	}

} // namespace json11

/*** End of inlined file: json11.cpp ***/

/*** End of inlined file: json11.hpp ***/


/*** Start of inlined file: tileson_parser.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_TILESON_PARSER_HPP
#define TILESON_TILESON_PARSER_HPP

//RBP: FS-namespace is defined in tileson_parser now!
#if _MSC_VER && !__INTEL_COMPILER
	#include <filesystem>
	namespace fs = std::filesystem;
#elif __MINGW64__
	#if __MINGW64_VERSION_MAJOR > 6
		#include <filesystem>
		namespace fs = std::filesystem;
	#else
		#include <experimental/filesystem>
		namespace fs = std::experimental::filesystem;
	#endif
#elif __clang__
	#if __clang_major__ < 8
		#include <experimental/filesystem>
		namespace fs = std::experimental::filesystem;
	#else
		#include <filesystem>
		namespace fs = std::filesystem;
	#endif
#else //Linux
	#if __GNUC__ < 8 //GCC major version less than 8
		#include <experimental/filesystem>
		namespace fs = std::experimental::filesystem;
	#else
		#include <filesystem>
		namespace fs = std::filesystem;
	#endif
#endif

#include <fstream>
#include <sstream>
#include <memory>


/*** Start of inlined file: Tools.hpp ***/
//
// Created by robin on 31.07.2020.
//

#ifndef TILESON_TOOLS_HPP
#define TILESON_TOOLS_HPP

#include <cstdint>
#include <vector>
#include <string_view>
namespace tson
{
	class Tools
	{

		public:
			Tools() = delete;
			~Tools() = delete;
			inline static std::vector<uint8_t> Base64DecodedStringToBytes(std::string_view str);
			inline static std::vector<uint32_t> BytesToUnsignedInts(const std::vector<uint8_t> &bytes);
			inline static std::vector<std::string> SplitString(const std::string &s, char delim);
			inline static bool Equal(float a, float b, float precision = 8192.f);

		private:
			template<typename Out>
			static void split(const std::string &s, char delim, Out result)
			{
				std::stringstream ss;
				ss.str(s);
				std::string item;

				while (std::getline(ss, item, delim))
				{
					*(result++) = item;
				}
			}
	};

	/*!
	 * When you have decoded a Base64 string, you'll get a string representing bytes. This function turns them into actual bytes.
	 * @param str
	 * @return
	 */
	std::vector<uint8_t> Tools::Base64DecodedStringToBytes(std::string_view str)
	{
		std::vector<uint8_t> bytes;
		for(size_t i = 0; i < str.size(); ++i)
		{
			uint8_t u8 = static_cast<uint8_t>(str[i]);
			bytes.push_back(u8);
		}
		return bytes;
	}

	/*!
	 * Converts bytes into unsigned int values. The bytes are converted in the Little Endian byte order to fit Tiled's specs.
	 * @param bytes A vector of bytes.
	 * @return Bytes converted to unsigned ints
	 */
	std::vector<uint32_t> Tools::BytesToUnsignedInts(const std::vector<uint8_t> &bytes)
	{
		std::vector<uint32_t> uints;
		std::vector<uint8_t> toConvert;
		//uint32_t size8 = (compressed[55] << 24) | (compressed[56] << 16) | (compressed[57] << 8) | compressed[58]; //Should be 66000

		for(size_t i = 0; i < bytes.size(); ++i)
		{
			toConvert.push_back(bytes[i]);
			if(toConvert.size() == 4)
			{
				uint32_t u32 = (toConvert[3] << 24) | (toConvert[2] << 16) | (toConvert[1] << 8) | toConvert[0];
				uints.push_back(u32);
				toConvert.clear();
			}
		}

		return uints;
	}

	std::vector<std::string> Tools::SplitString(const std::string &s, char delim)
	{
		std::vector<std::string> elems;
		split(s, delim, std::back_inserter(elems));
		return elems;
	}

	/*!
	 * Uses a threshold for comparing floats, as they are not precise in many cases.
	 * @param a
	 * @param b
	 * @return true if equal based on the currently defined precision
	 */
	bool Tools::Equal(float a, float b, float precision)
	{
		float threshold = 1.f / precision;
		float diff = fabsf(a - b);
		return diff <= threshold;
	}
}

#endif //TILESON_TOOLS_HPP

/*** End of inlined file: Tools.hpp ***/


/*** Start of inlined file: Base64Decompressor.hpp ***/
//
// Created by robin on 29.07.2020.
// The Base64 decoding logic is heavily based on: https://github.com/ReneNyffenegger/cpp-base64
//

#ifndef TILESON_BASE64DECOMPRESSOR_HPP
#define TILESON_BASE64DECOMPRESSOR_HPP


/*** Start of inlined file: IDecompressor.hpp ***/
//
// Created by robin on 29.07.2020.
//

#ifndef TILESON_IDECOMPRESSOR_HPP
#define TILESON_IDECOMPRESSOR_HPP

#include <string_view>

namespace tson
{
	template <class TIn, class TOut>
	class IDecompressor
	{
		public:
			/*!
			 * If the name matches with 'compression' or 'encoding' the decompress() function will
			 * be called automatically for the actual Layer. Encoding-related matching is handled first!
			 *
			 * Known values:
			 *
			 * compression: zlib, gzip, zstd (since Tiled 1.3) or empty (default) (tilelayer only).
			 * encoding: csv (default) or base64 (tilelayer only).
			 *
			 * @return
			 */
			[[nodiscard]] virtual const std::string &name() const = 0;

			/*!
			 * Used primarily for Tiled related decompression.
			 * @param input Input data
			 * @return Decompressed data
			 */
			virtual TOut decompress(const TIn &input) = 0;

			/*!
			 * Used for whole file decompression. Not related to Tiled
			 * @param path
			 * @return
			 */
			virtual TOut decompressFile(const fs::path &path) = 0;

			/*!
			 * Used for whole file decompression. Not related to Tiled
			 * @param path
			 * @return
			 */
			virtual TOut decompress(const void *data, size_t size) = 0;

			/*!
			 * Pure virtual class needs virtual destructor so derived classes can call their own destructors
			*/
			virtual ~IDecompressor() = default;
	};
}

#endif //TILESON_IDECOMPRESSOR_HPP

/*** End of inlined file: IDecompressor.hpp ***/

#include <string>

namespace tson
{
	class Base64Decompressor : public IDecompressor<std::string_view, std::string>
	{
		public:
			[[nodiscard]] inline const std::string &name() const override;

			inline std::string decompress(const std::string_view &s) override;

			inline std::string decompressFile(const fs::path &path) override;
			inline std::string decompress(const void *data, size_t size) override;

		private:
			inline unsigned int pos_of_char(const unsigned char chr);
			inline static const std::string NAME = "base64";
	};

	const std::string &Base64Decompressor::name() const
	{
		return NAME;
	}

	std::string Base64Decompressor::decompress(const std::string_view &s)
	{

		size_t length_of_string = s.length();
		if (!length_of_string) return std::string("");

		size_t in_len = length_of_string;
		size_t pos = 0;

		//
		// The approximate length (bytes) of the decoded string might be one ore
		// two bytes smaller, depending on the amount of trailing equal signs
		// in the encoded string. This approximation is needed to reserve
		// enough space in the string to be returned.
		//
		size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
		std::string ret;
		ret.reserve(approx_length_of_decoded_string);

		while (pos < in_len) {

			unsigned int pos_of_char_1 = pos_of_char(s[pos+1] );

			ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char(s[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4)));

			if (s[pos+2] != '=' && s[pos+2] != '.') { // accept URL-safe base 64 strings, too, so check for '.' also.

				unsigned int pos_of_char_2 = pos_of_char(s[pos+2] );
				ret.push_back(static_cast<std::string::value_type>( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2)));

				if (s[pos+3] != '=' && s[pos+3] != '.') {
					ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(s[pos+3])   ));
				}
			}

			pos += 4;
		}

		return ret;
	}

	unsigned int Base64Decompressor::pos_of_char(const unsigned char chr)
	{
		//
		// Return the position of chr within base64_encode()
		//

		if      (chr >= 'A' && chr <= 'Z') return chr - 'A';
		else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A')               + 1;
		else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
		else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
		else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_'

		throw "If input is correct, this line should never be reached.";
	}

	/*!
	 * UNUSED! Does nothing
	 * @param path
	 * @return
	 */
	std::string Base64Decompressor::decompressFile(const fs::path &)
	{
		return std::string();
	}

	/*!
	 * UNUSED! Does nothing
	 * @param path
	 * @return
	 */
	std::string Base64Decompressor::decompress(const void *, size_t)
	{
		return std::string();
	}
}

#endif //TILESON_BASE64DECOMPRESSOR_HPP

/*** End of inlined file: Base64Decompressor.hpp ***/


/*** Start of inlined file: Lzma.hpp ***/
//
// Created by robin on 16.01.2021.
//
//#include "../../extras/pocketlzma.hpp"
#ifdef POCKETLZMA_POCKETLZMA_H

#ifndef TILESON_LZMA_HPP
#define TILESON_LZMA_HPP

namespace tson
{
	class Lzma : public IDecompressor<std::vector<uint8_t>, std::vector<uint8_t>>
	{
		public:
			inline const std::string &name() const override
			{
				return NAME;
			}

			inline std::vector<uint8_t> decompress(const std::vector<uint8_t> &input) override
			{
				std::vector<uint8_t> out;

				plz::PocketLzma p;
				plz::StatusCode status = p.decompress(input, out);

				if(status != plz::StatusCode::Ok)
					return std::vector<uint8_t>();

				return out;
			}

			inline std::vector<uint8_t> decompressFile(const fs::path &path) override
			{
				std::vector<uint8_t> in;
				std::vector<uint8_t> out;

				plz::PocketLzma p;
				plz::FileStatus fileStatus = plz::File::FromFile(path.generic_string(), in);
				if(fileStatus.status() != plz::FileStatus::Code::Ok)
					return std::vector<uint8_t>();

				plz::StatusCode status = p.decompress(in, out);

				if(status != plz::StatusCode::Ok)
					return std::vector<uint8_t>();

				return out;
			}

			inline std::vector<uint8_t> decompress(const void *data, size_t size) override
			{
				std::vector<uint8_t> out;

				plz::PocketLzma p;
				plz::StatusCode status = p.decompress((uint8_t*) data, size, out);

				if(status != plz::StatusCode::Ok)
					return std::vector<uint8_t>();

				return out;
			}

		private:
			inline static const std::string NAME {"lzma"};
	};
}

#endif //TILESON_LZMA_HPP

#endif
/*** End of inlined file: Lzma.hpp ***/


/*** Start of inlined file: DecompressorContainer.hpp ***/
//
// Created by robin on 30.07.2020.
//

#ifndef TILESON_DECOMPRESSORCONTAINER_HPP
#define TILESON_DECOMPRESSORCONTAINER_HPP

#include <memory>
#include <vector>
#include <string_view>
#include <functional>
namespace tson
{
	class DecompressorContainer
	{
		public:
			inline DecompressorContainer() = default;
			template <typename T, typename... Args>
			inline void add(Args &&... args);
			inline void remove(std::string_view name);
			inline bool contains(std::string_view name) const;
			inline bool empty() const;
			inline size_t size() const;
			inline void clear();

			inline IDecompressor<std::string_view, std::string> *get(std::string_view name);
		private:
			//Key: name,
			std::vector<std::unique_ptr<IDecompressor<std::string_view, std::string>>> m_decompressors;
	};

	template<typename T, typename... Args>
	void DecompressorContainer::add(Args &&... args)
	{
		m_decompressors.emplace_back(new T(args...));
	}

	/*!
	 *
	 * @param name The name of the decompressor to check whether exists.
	 * @return Whether a decompressor with the given name exists or not.
	 */
	bool DecompressorContainer::contains(std::string_view name) const
	{
		auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item)
		{
			return item->name() == name;
		});

		return iter != m_decompressors.end();
	}

	/*!
	 * Removed an element with the given name.
	 * @param name The name of the decompressor
	 */
	void DecompressorContainer::remove(std::string_view name)
	{
		auto iter = std::remove_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item)
		{
			return item->name() == name;
		});
		m_decompressors.erase(iter);
	}

	size_t DecompressorContainer::size() const
	{
		return m_decompressors.size();
	}

	/*!
	 *
	 * @param name The name of the container
	 * @return An ICompressor pointer if it exists. nullptr otherwise.
	 */
	IDecompressor<std::string_view, std::string> *DecompressorContainer::get(std::string_view name)
	{
		auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item)
		{
			return item->name() == name;
		});

		return (iter != m_decompressors.end()) ? iter->get() : nullptr;
	}

	/*!
	 * Check if container is empty
	 * @return Whether or not the container is empty
	 */
	bool DecompressorContainer::empty() const
	{
		return m_decompressors.empty();
	}

	/*!
	 * Clears all IDecompressor elements in the container
	 */
	void DecompressorContainer::clear()
	{
		m_decompressors.clear();
	}
}
#endif //TILESON_DECOMPRESSORCONTAINER_HPP

/*** End of inlined file: DecompressorContainer.hpp ***/


/*** Start of inlined file: MemoryStream.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_MEMORYSTREAM_HPP
#define TILESON_MEMORYSTREAM_HPP


/*** Start of inlined file: MemoryBuffer.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_MEMORYBUFFER_HPP
#define TILESON_MEMORYBUFFER_HPP

#include <iostream>

namespace tson
{
	class MemoryBuffer : public std::basic_streambuf<char> {
		public:
			MemoryBuffer(const uint8_t *p, size_t l) {
				setg((char*)p, (char*)p, (char*)p + l);
			}
	};
}

#endif //TILESON_MEMORYBUFFER_HPP

/*** End of inlined file: MemoryBuffer.hpp ***/

namespace tson
{
	class MemoryStream : public std::istream {
		public:
			MemoryStream(const uint8_t *p, size_t l) :
					std::istream(&m_buffer),
					m_buffer(p, l) {
				rdbuf(&m_buffer);
			}

		private:
			MemoryBuffer m_buffer;
	};
}

#endif //TILESON_MEMORYSTREAM_HPP

/*** End of inlined file: MemoryStream.hpp ***/


/*** Start of inlined file: Map.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_MAP_HPP
#define TILESON_MAP_HPP


/*** Start of inlined file: Color.hpp ***/
//
// Created by robin on 09.08.2019.
//

#ifndef TILESON_COLOR_HPP
#define TILESON_COLOR_HPP

#include <type_traits>
#include <cstdint>
#include <string>

namespace tson
{

	template<typename T>
	class Color
	{

		public:
			/*!
			 * Parses color from Tiled's own color format, which is #aarrggbb in hex format or optionally #rrggbb.
			 * @param color Color in "#rrggbbaa" hex format.
			 * @example "#ffaa07ff" and "#aa07ff". In cases where alpha is not a value, it is set to 255.
			*/
			inline explicit Color(const std::string &color)
			{
				parseHexString(color);
			}
			inline Color(T red, T green, T blue, T alpha);
			inline Color() { r = g = b = 0; a = 255; }

			inline bool operator==(const Color &rhs) const;
			inline bool operator==(const std::string &rhs) const;
			inline bool operator!=(const Color &rhs) const;

			inline Color<float> asFloat();
			inline Color<uint8_t> asInt();

			/*! Red */
			T r;
			/*! Green */
			T g;
			/*! Blue */
			T b;
			/*! Alpha */
			T a;

		private:
			void parseHexString(const std::string &color)
			{
				if constexpr (std::is_same<T, float>::value)
				{
					if (color.size() == 9)
					{
						a = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255;
						r = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255;
						g = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255;
						b = (float) std::stoi(color.substr(7, 2), nullptr, 16) / 255;
					}
					else if (color.size() == 7)
					{
						r = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255;
						g = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255;
						b = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255;
						a = 1.f;
					}
				}
				else if constexpr (std::is_same<T, uint8_t>::value)
				{
					if (color.size() == 9)
					{
						a = static_cast<uint8_t>(std::stoi(color.substr(1, 2), nullptr, 16));
						r = static_cast<uint8_t>(std::stoi(color.substr(3, 2), nullptr, 16));
						g = static_cast<uint8_t>(std::stoi(color.substr(5, 2), nullptr, 16));
						b = static_cast<uint8_t>(std::stoi(color.substr(7, 2), nullptr, 16));
					}
					else if (color.size() == 7)
					{
						r = static_cast<uint8_t>(std::stoi(color.substr(1, 2), nullptr, 16));
						g = static_cast<uint8_t>(std::stoi(color.substr(3, 2), nullptr, 16));
						b = static_cast<uint8_t>(std::stoi(color.substr(5, 2), nullptr, 16));
						a = 255;
					}
				}
			}

	};

	typedef Color<uint8_t> Colori;
	typedef Color<float> Colorf;

	/*!
	 * Gets the Color as a float. Only useful if the template related to the current color is NOT float
	 * @tparam T The template type
	 * @return If the T type is float, the value will be returned as a copy of itself. Else: All values will be divided by 255
	 * before returning.
	 */
	template<typename T>
	tson::Colorf Color<T>::asFloat()
	{
		if constexpr (std::is_same<T, float>::value)
			*this;
		else
			return tson::Colorf((float) r / 255, (float) g / 255, (float) b / 255, (float) a / 255);
	}

	/*!
	 * Gets the Color as an 32-bit variable, where each channel is 8-bit.
	 * Only useful if the template related to the current color is NOT already 8-bit int
	 * @tparam T The template type
	 * @return If the T type is float, the value of each channel will be multiplied by 255. Else: The value will be returned as a copy of itself.
	 */
	template<typename T>
	tson::Colori Color<T>::asInt()
	{
		if constexpr (std::is_same<T, float>::value)
			return tson::Colori(static_cast<std::uint8_t>((float) r * 255),
							  static_cast<std::uint8_t>((float) g * 255),
							  static_cast<std::uint8_t>((float) b * 255),
							  static_cast<std::uint8_t>((float) a * 255));
		else
			*this;
	}

	/*!
	 * Create a new color in rgba (red, green, blue, alpha) format
	 * @tparam T the template type for each channel. Usually uint8_t (8-bit int) or float.
	 * @param red Red channel
	 * @param green Green channel
	 * @param blue Blue channel
	 * @param alpha Alpha channel
	 */
	template<typename T>
	Color<T>::Color(T red, T green, T blue, T alpha)
	{
		r = red;
		g = green;
		b = blue;
		a = alpha;
	}

	template<typename T>
	bool Color<T>::operator==(const std::string &rhs) const {
		Color other {rhs};
		return *this == other;
	}

	template<typename T>
	bool Color<T>::operator==(const Color &rhs) const
	{
		return r == rhs.r &&
			   g == rhs.g &&
			   b == rhs.b &&
			   a == rhs.a;
	}

	template<typename T>
	bool Color<T>::operator!=(const Color &rhs) const
	{
		return !(rhs == *this);
	}

}

#endif //TILESON_COLOR_HPP

/*** End of inlined file: Color.hpp ***/


/*** Start of inlined file: Vector2.hpp ***/
//
// Created by robin on 31.07.2019.
//

#ifndef TILESON_VECTOR2_HPP
#define TILESON_VECTOR2_HPP

namespace tson
{
	template<typename T>
	class Vector2
	{

		public:
			inline Vector2(T xPos, T yPos);
			inline Vector2() { x = y = 0; }

			inline bool operator==(const Vector2 &rhs) const;
			inline bool operator!=(const Vector2 &rhs) const;

			T x;
			T y;
	};

	/*!
	 *
	 * @tparam T template type
	 * @param xPos x-position
	 * @param yPos y-position
	 */
	template<typename T>
	Vector2<T>::Vector2(T xPos, T yPos)
	{
		x = xPos;
		y = yPos;
	}

	template<typename T>
	bool Vector2<T>::operator==(const Vector2 &rhs) const
	{
		return x == rhs.x &&
			   y == rhs.y;
	}

	template<typename T>
	bool Vector2<T>::operator!=(const Vector2 &rhs) const
	{
		return !(rhs == *this);
	}

	typedef Vector2<int> Vector2i;
	typedef Vector2<float> Vector2f;
}

#endif //TILESON_VECTOR2_HPP

/*** End of inlined file: Vector2.hpp ***/

//#include "../external/json.hpp"

/*** Start of inlined file: IJson.hpp ***/
//
// Created by robin on 06.01.2021.
//

#ifndef TILESON_IJSON_HPP
#define TILESON_IJSON_HPP

namespace tson
{
	class IJson
	{
		public:

			virtual IJson& operator[](std::string_view key) = 0;
			virtual IJson &at(std::string_view key) = 0;
			virtual IJson &at(size_t pos) = 0;
			/*!
			 * If current json object is an array, this will get all elements of it!
			 * @return An array
			 */
			[[nodiscard]] virtual std::vector<std::unique_ptr<IJson>> array() = 0;
			[[nodiscard]] virtual std::vector<std::unique_ptr<IJson>> &array(std::string_view key) = 0;
			/*!
			 * Get the size of an object. This will be equal to the number of
			 * variables an object contains.
			 * @return
			 */
			[[nodiscard]] virtual size_t size() const = 0;
			[[nodiscard]] virtual bool parse(const fs::path &path) = 0;
			[[nodiscard]] virtual bool parse(const void *data, size_t size) = 0;

			template <typename T>
			[[nodiscard]] T get(std::string_view key);
			template <typename T>
			[[nodiscard]] T get();
			[[nodiscard]] virtual size_t count(std::string_view key) const = 0;
			[[nodiscard]] virtual bool any(std::string_view key) const = 0;
			[[nodiscard]] virtual bool isArray() const = 0;
			[[nodiscard]] virtual bool isObject() const = 0;
			[[nodiscard]] virtual bool isNull() const = 0;

			/*!
			 * Get the directory where the json was loaded.
			 * Only assigned if json is parsed by file.
			 * @return
			 */
			[[nodiscard]] virtual fs::path directory() const = 0;
			virtual void directory(const fs::path &directory) = 0;

			/*!
			 * Pure virtual class needs virtual destructor so derived classes can call their own destructors
			*/
			virtual ~IJson() = default;

		protected:
			[[nodiscard]] virtual int32_t getInt32(std::string_view key) = 0;
			[[nodiscard]] virtual uint32_t getUInt32(std::string_view key) = 0;
			[[nodiscard]] virtual int64_t getInt64(std::string_view key) = 0;
			[[nodiscard]] virtual uint64_t getUInt64(std::string_view key) = 0;
			[[nodiscard]] virtual double getDouble(std::string_view key) = 0;
			[[nodiscard]] virtual float getFloat(std::string_view key) = 0;
			[[nodiscard]] virtual std::string getString(std::string_view key) = 0;
			[[nodiscard]] virtual bool getBool(std::string_view key) = 0;

			[[nodiscard]] virtual int32_t getInt32() = 0;
			[[nodiscard]] virtual uint32_t getUInt32() = 0;
			[[nodiscard]] virtual int64_t getInt64() = 0;
			[[nodiscard]] virtual uint64_t getUInt64() = 0;
			[[nodiscard]] virtual double getDouble() = 0;
			[[nodiscard]] virtual float getFloat() = 0;
			[[nodiscard]] virtual std::string getString() = 0;
			[[nodiscard]] virtual bool getBool() = 0;
	};

	template<typename T>
	T IJson::get(std::string_view key)
	{
		if constexpr (std::is_same<T, double>::value)
			return getDouble(key);
		if constexpr (std::is_same<T, float>::value)
			return getFloat(key);
		else if constexpr (std::is_same<T, int32_t>::value)
			return getInt32(key);
		else if constexpr (std::is_same<T, uint32_t>::value)
			return getUInt32(key);
		else if constexpr (std::is_same<T, int64_t>::value)
			return getInt64(key);
		else if constexpr (std::is_same<T, uint64_t>::value)
			return getUInt64(key);
		else if constexpr (std::is_same<T, std::string>::value)
			return getString(key);
		else if constexpr (std::is_same<T, bool>::value)
			return getBool(key);
		else
			return nullptr;
	}

	template<typename T>
	T IJson::get()
	{
		if constexpr (std::is_same<T, double>::value)
			return getDouble();
		if constexpr (std::is_same<T, float>::value)
			return getFloat();
		else if constexpr (std::is_same<T, int32_t>::value)
			return getInt32();
		else if constexpr (std::is_same<T, uint32_t>::value)
			return getUInt32();
		else if constexpr (std::is_same<T, int64_t>::value)
			return getInt64();
		else if constexpr (std::is_same<T, uint64_t>::value)
			return getUInt64();
		else if constexpr (std::is_same<T, std::string>::value)
			return getString();
		else if constexpr (std::is_same<T, bool>::value)
			return getBool();
		else
			return nullptr;
	}

}

#endif //TILESON_IJSON_HPP

/*** End of inlined file: IJson.hpp ***/



/*** Start of inlined file: NlohmannJson.hpp ***/
//
// Created by robin on 08.01.2021.
//

#ifdef INCLUDE_NLOHMANN_JSON_HPP_

#ifndef TILESON_NLOHMANNJSON_HPP
#define TILESON_NLOHMANNJSON_HPP

namespace tson
{
	class NlohmannJson : public tson::IJson
	{
		public:
			inline NlohmannJson() = default;

			IJson &operator[](std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
					m_arrayCache[key.data()] = std::make_unique<NlohmannJson>(&m_json->operator[](key.data()));//.front());

				return *m_arrayCache[key.data()].get();
			}

			inline explicit NlohmannJson(nlohmann::json *json) : m_json {json}
			{

			}

			inline IJson& at(std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
					m_arrayCache[key.data()] = std::make_unique<NlohmannJson>(&m_json->operator[](key.data()));//.front());

				return *m_arrayCache[key.data()].get();
			}

			inline IJson& at(size_t pos) override
			{
				if(m_arrayPosCache.count(pos) == 0)
					m_arrayPosCache[pos] = std::make_unique<NlohmannJson>(&m_json->at(pos));

				return *m_arrayPosCache[pos];
			}

			std::vector<std::unique_ptr<IJson>> array() override
			{
				std::vector<std::unique_ptr<IJson>> vec;
				for(auto &item : *m_json)
				{
					nlohmann::json *ptr = &item;
					vec.emplace_back(std::make_unique<NlohmannJson>(ptr));
				}

				return vec;
			}

			inline std::vector<std::unique_ptr<IJson>> &array(std::string_view key) override
			{
				if(m_arrayListDataCache.count(key.data()) == 0)
				{
					if (m_json->count(key.data()) > 0 && m_json->operator[](key.data()).is_array())
					{
						std::for_each(m_json->operator[](key.data()).begin(), m_json->operator[](key.data()).end(), [&](nlohmann::json &item)
						{
							nlohmann::json *ptr = &item;
							m_arrayListDataCache[key.data()].emplace_back(std::make_unique<NlohmannJson>(ptr));
						});
					}
				}

				return m_arrayListDataCache[key.data()];
			}

			[[nodiscard]] inline size_t size() const override
			{
				return m_json->size();
			}

			inline bool parse(const fs::path &path) override
			{
				clearCache();
				m_data = nullptr;
				m_json = nullptr;
				if (fs::exists(path) && fs::is_regular_file(path))
				{
					m_path = path.parent_path();
					m_data = std::make_unique<nlohmann::json>();
					std::ifstream i(path.generic_string());
					try
					{
						i >> *m_data;
						m_json = m_data.get();
					}
					catch (const nlohmann::json::parse_error &error)
					{
						std::string message = "Parse error: ";
						message += std::string(error.what());
						message += std::string("\n");
						std::cerr << message;
						return false;
					}
					return true;
				}
				return false;
			}

			inline bool parse(const void *data, size_t size) override
			{
				clearCache();
				m_json = nullptr;
				m_data = std::make_unique<nlohmann::json>();
				tson::MemoryStream mem{(uint8_t *) data, size};
				try
				{
					mem >> *m_data;
					m_json = m_data.get();
				}
				catch (const nlohmann::json::parse_error &error)
				{
					std::string message = "Parse error: ";
					message += std::string(error.what());
					message += std::string("\n");
					std::cerr << message;
					return false;
				}
				return true;
			}

			[[nodiscard]] inline size_t count(std::string_view key) const override
			{
				return m_json->count(key);
			}

			[[nodiscard]] inline bool any(std::string_view key) const override
			{
				return count(key) > 0;
			}

			[[nodiscard]] inline bool isArray() const override
			{
				return m_json->is_array();
			}

			[[nodiscard]] inline bool isObject() const override
			{
				return m_json->is_object();
			}

			[[nodiscard]] inline bool isNull() const override
			{
				return m_json->is_null();
			}

			fs::path directory() const override
			{
				return m_path;
			}

			void directory(const fs::path &directory) override
			{
				m_path = directory;
			}

		protected:
			[[nodiscard]] inline int32_t getInt32(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<int32_t>();
			}

			[[nodiscard]] inline uint32_t getUInt32(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<uint32_t>();
			}

			[[nodiscard]] inline int64_t getInt64(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<int64_t>();
			}

			[[nodiscard]] inline uint64_t getUInt64(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<uint64_t>();
			}

			[[nodiscard]] inline double getDouble(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<double>();
			}

			[[nodiscard]] inline std::string getString(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<std::string>();
			}

			[[nodiscard]] inline bool getBool(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<bool>();
			}

			[[nodiscard]] float getFloat(std::string_view key) override
			{
				return m_json->operator[](key.data()).get<float>();
			}

			[[nodiscard]] inline int32_t getInt32() override
			{
				return m_json->get<int32_t>();
			}

			[[nodiscard]] inline uint32_t getUInt32() override
			{
				return m_json->get<uint32_t>();
			}

			[[nodiscard]] inline int64_t getInt64() override
			{
				return m_json->get<int64_t>();
			}

			[[nodiscard]] inline uint64_t getUInt64() override
			{
				return m_json->get<uint64_t>();
			}

			[[nodiscard]] inline double getDouble() override
			{
				return m_json->get<double>();
			}

			[[nodiscard]] inline std::string getString() override
			{
				return m_json->get<std::string>();
			}

			[[nodiscard]] inline bool getBool() override
			{
				return m_json->get<bool>();
			}

			[[nodiscard]] float getFloat() override
			{
				return m_json->get<float>();
			}

		private:
			inline void clearCache()
			{
				m_arrayCache.clear();
				m_arrayPosCache.clear();
				m_arrayListDataCache.clear();
			}

			nlohmann::json *m_json = nullptr;
			std::unique_ptr<nlohmann::json> m_data = nullptr; //Only used if this is the owner json!
			fs::path m_path;

			//Cache!
			std::map<std::string, std::unique_ptr<IJson>> m_arrayCache;
			std::map<size_t, std::unique_ptr<IJson>> m_arrayPosCache;
			std::map<std::string, std::vector<std::unique_ptr<IJson>>> m_arrayListDataCache;

	};
}
#endif //TILESON_NLOHMANNJSON_HPP

#endif //INCLUDE_NLOHMANN_JSON_HPP_
/*** End of inlined file: NlohmannJson.hpp ***/


/*** Start of inlined file: PicoJson.hpp ***/
//
// Created by robin on 11.01.2021.
//

#ifdef picojson_h
#ifndef TILESON_PICOJSON_HPP
#define TILESON_PICOJSON_HPP

namespace tson
{
	class PicoJson : public tson::IJson
	{
		public:
			inline PicoJson() = default;

			IJson &operator[](std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
				{
					if(m_json->is<picojson::object>())
					{
						picojson::object &o = m_json->get<picojson::object>();
						m_arrayCache[key.data()] = std::make_unique<PicoJson>(&o[key.data()]);
					}
				}

				return *m_arrayCache[key.data()].get();
			}

			inline explicit PicoJson(picojson::value *json) : m_json {json}
			{

			}

			inline IJson& at(std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
				{
					if(m_json->is<picojson::object>())
					{
						picojson::object &o = m_json->get<picojson::object>();
						m_arrayCache[key.data()] = std::make_unique<PicoJson>(&o[key.data()]);
					}
				}
				return *m_arrayCache[key.data()].get();
			}

			inline IJson& at(size_t pos) override
			{
				if(m_arrayPosCache.count(pos) == 0)
				{
					picojson::array &a = m_json->get<picojson::array>();
					m_arrayPosCache[pos] = std::make_unique<PicoJson>(&a.at(pos));
				}

				return *m_arrayPosCache[pos];
			}

			std::vector<std::unique_ptr<IJson>> array() override
			{
				std::vector<std::unique_ptr<IJson>> vec;
				if(m_json->is<picojson::array>())
				{
					picojson::array &a = m_json->get<picojson::array>();
					for (auto &item : a)
					{
						picojson::value *ptr = &item;
						vec.emplace_back(std::make_unique<PicoJson>(ptr));
					}
				}

				return vec;
			}

			inline std::vector<std::unique_ptr<IJson>> &array(std::string_view key) override
			{
				if(m_arrayListDataCache.count(key.data()) == 0)
				{
					if(count(key.data()) > 0)
					{
						if (isObject())
						{
							picojson::object &obj = m_json->get<picojson::object>();
							picojson::value &v = obj.at(key.data());
							bool isArray = v.is<picojson::array>();
							if (isArray)
							{
								picojson::array &a = v.get<picojson::array>();

								std::for_each(a.begin(), a.end(), [&](picojson::value &item)
								{
									picojson::value *ptr = &item;
									m_arrayListDataCache[key.data()].emplace_back(std::make_unique<PicoJson>(ptr));
								});
							}
						}
					}
				}

				return m_arrayListDataCache[key.data()];
			}

			[[nodiscard]] inline size_t size() const override
			{
				if (m_json->is<picojson::object>())
				{
					picojson::object obj = m_json->get<picojson::object>();
					return obj.size();
				}
				return 0;
			}

			inline bool parse(const fs::path &path) override
			{
				clearCache();
				m_data = nullptr;
				m_json = nullptr;
				if (fs::exists(path) && fs::is_regular_file(path))
				{
					m_path = path.parent_path();
					m_data = std::make_unique<picojson::value>();
					std::ifstream i(path.generic_string());
					try
					{
						std::string error = picojson::parse(*m_data, i);
						if(!error.empty())
						{
							std::cerr << "PicoJson parse error: " << error << "\n";
							return false;
						}
						//i >> *m_data;
						m_json = m_data.get();
					}
					catch (const std::exception &error)
					{
						std::string message = "Parse error: ";
						message += std::string(error.what());
						message += std::string("\n");
						std::cerr << message;
						return false;
					}
					return true;
				}
				return false;
			}

			inline bool parse(const void *data, size_t size) override
			{
				clearCache();
				m_json = nullptr;
				m_data = std::make_unique<picojson::value>();
				tson::MemoryStream mem{(uint8_t *) data, size};
				try
				{
					std::string error = picojson::parse(*m_data, mem);
					if(!error.empty())
					{
						std::cerr << "PicoJson parse error: " << error << "\n";
						return false;
					}
					//mem >> *m_data;
					m_json = m_data.get();
				}
				catch (const std::exception &error)
				{
					std::string message = "Parse error: ";
					message += std::string(error.what());
					message += std::string("\n");
					std::cerr << message;
					return false;
				}
				return true;
			}

			[[nodiscard]] inline size_t count(std::string_view key) const override
			{
				if (isObject())
				{
					picojson::object obj = m_json->get<picojson::object>();
					return obj.count(key.data());
				}

				return m_json->contains(key.data()) ? 1 : 0;
			}

			[[nodiscard]] inline bool any(std::string_view key) const override
			{
				return count(key) > 0;
			}

			[[nodiscard]] inline bool isArray() const override
			{
				return m_json->is<picojson::array>();
			}

			[[nodiscard]] inline bool isObject() const override
			{
				return m_json->is<picojson::object>();
			}

			[[nodiscard]] inline bool isNull() const override
			{
				return m_json->is<picojson::null>();
			}

			fs::path directory() const override
			{
				return m_path;
			}

			void directory(const fs::path &directory) override
			{
				m_path = directory;
			}

		protected:
			[[nodiscard]] inline int32_t getInt32(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return static_cast<int32_t>(getDouble(key));
			}

			[[nodiscard]] inline uint32_t getUInt32(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return static_cast<uint32_t>(getDouble(key));
			}

			[[nodiscard]] inline int64_t getInt64(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return static_cast<int64_t>(getDouble(key));
			}

			[[nodiscard]] inline uint64_t getUInt64(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return static_cast<uint64_t>(getDouble(key));
			}

			[[nodiscard]] inline double getDouble(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return obj[key.data()].get<double>();
			}

			[[nodiscard]] inline std::string getString(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return obj[key.data()].get<std::string>();
			}

			[[nodiscard]] inline bool getBool(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return obj[key.data()].get<bool>();
			}

			[[nodiscard]] float getFloat(std::string_view key) override
			{
				picojson::object obj = m_json->get<picojson::object>();
				return static_cast<float>(getDouble(key));
			}

			[[nodiscard]] inline int32_t getInt32() override
			{
				return static_cast<int32_t>(getDouble());
			}

			[[nodiscard]] inline uint32_t getUInt32() override
			{
				return static_cast<uint32_t>(getDouble());
			}

			[[nodiscard]] inline int64_t getInt64() override
			{
				return static_cast<int64_t>(getDouble());
			}

			[[nodiscard]] inline uint64_t getUInt64() override
			{
				return static_cast<uint64_t>(getDouble());
			}

			[[nodiscard]] inline double getDouble() override
			{
				return m_json->get<double>();
			}

			[[nodiscard]] inline std::string getString() override
			{
				return m_json->get<std::string>();
			}

			[[nodiscard]] inline bool getBool() override
			{
				return m_json->get<bool>();
			}

			[[nodiscard]] float getFloat() override
			{
				return static_cast<float>(getDouble());
			}

		private:
			inline void clearCache()
			{
				m_arrayCache.clear();
				m_arrayPosCache.clear();
				m_arrayListDataCache.clear();
			}

			picojson::value *m_json = nullptr;
			std::unique_ptr<picojson::value> m_data = nullptr; //Only used if this is the owner json!
			fs::path m_path;

			//Cache!
			std::map<std::string, std::unique_ptr<IJson>> m_arrayCache;
			std::map<size_t, std::unique_ptr<IJson>> m_arrayPosCache;
			std::map<std::string, std::vector<std::unique_ptr<IJson>>> m_arrayListDataCache;

	};
}
#endif //TILESON_PICOJSON_HPP
#endif

/*** End of inlined file: PicoJson.hpp ***/

//#include "../json/Gason.hpp" //Unsupported

/*** Start of inlined file: Json11.hpp ***/
//
// Created by robin on 16.01.2021.
//

#ifndef TILESON_JSON11_HPP
#define TILESON_JSON11_HPP

namespace tson
{
	class Json11 : public tson::IJson
	{
		public:
			inline Json11() = default;

			IJson &operator[](std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
				{
					if(m_json->is_object())
					{
						m_arrayCache[key.data()] = std::make_unique<Json11>(m_json->operator[](key.data()));
					}
				}

				return *m_arrayCache[key.data()].get();
			}

			inline explicit Json11(const json11::Json &json) : m_json {&json}
			{

			}

			inline IJson& at(std::string_view key) override
			{
				if(m_arrayCache.count(key.data()) == 0)
				{
					if(m_json->is_object())
					{
						m_arrayCache[key.data()] = std::make_unique<Json11>(m_json->operator[](key.data()));
					}
				}
				return *m_arrayCache[key.data()].get();
			}

			inline IJson& at(size_t pos) override
			{
				if(m_arrayPosCache.count(pos) == 0)
				{
					const std::vector<json11::Json> &a = m_json->array_items();
					m_arrayPosCache[pos] = std::make_unique<Json11>(a.at(pos));
				}

				return *m_arrayPosCache[pos];
			}

			std::vector<std::unique_ptr<IJson>> array() override
			{
				std::vector<std::unique_ptr<IJson>> vec;
				if(m_json->is_array())
				{
					for (const json11::Json &item : m_json->array_items())
					{
						vec.emplace_back(std::make_unique<Json11>(item));
					}
				}

				return vec;
			}

			inline std::vector<std::unique_ptr<IJson>> &array(std::string_view key) override
			{
				if(m_arrayListDataCache.count(key.data()) == 0)
				{
					if(count(key.data()) > 0)
					{
						if(isObject())
						{
							const json11::Json &v = m_json->operator[](key.data());
							if(v.is_array())
							{
								for (const json11::Json &item : v.array_items())
								{
									m_arrayListDataCache[key.data()].emplace_back(std::make_unique<Json11>(item));
								}
							}
						}
					}
				}

				return m_arrayListDataCache[key.data()];
			}

			[[nodiscard]] inline size_t size() const override
			{
				if(m_json->is_object())
					return m_json->object_items().size();
				else if(m_json->is_array())
					return m_json->array_items().size();

				return 0;
			}

			inline bool parse(const fs::path &path) override
			{
				clearCache();
				m_data = nullptr;
				m_json = nullptr;
				if (fs::exists(path) && fs::is_regular_file(path))
				{
					std::ifstream file(path.generic_string());
					std::string str;
					m_path = path.parent_path();

					file.seekg(0, std::ios::end);
					str.reserve(static_cast<size_t>(file.tellg()));
					file.seekg(0, std::ios::beg);

					str.assign((std::istreambuf_iterator<char>(file)),
							   std::istreambuf_iterator<char>());

					m_data = std::make_unique<json11::Json>();

					try
					{
						std::string strError;
						*m_data = json11::Json::parse(str, strError);
						if(!strError.empty())
						{
							std::cerr << strError << "\n";
							return false;
						}
						m_json = m_data.get();
					}
					catch (const std::exception &error)
					{
						std::string message = "Json11 parse error: ";
						message += std::string(error.what());
						message += std::string("\n");
						std::cerr << message;
						return false;
					}
					return true;
				}
				return false;
			}

			inline bool parse(const void *data, size_t size) override
			{
				clearCache();
				m_json = nullptr;
				std::string str;

				str.reserve(size);

				tson::MemoryStream mem{(uint8_t *) data, size};

				str.assign((std::istreambuf_iterator<char>(mem)),
						   std::istreambuf_iterator<char>());

				m_data = std::make_unique<json11::Json>();

				try
				{
					std::string strError;

					*m_data = json11::Json::parse(str, strError);
					if(!strError.empty())
					{
						std::cout << strError << "\n";
						return false;
					}
					m_json = m_data.get();
				}
				catch (const std::exception &error)
				{
					std::string message = "Json11 parse error: ";
					message += std::string(error.what());
					message += std::string("\n");
					std::cerr << message;
					return false;
				}
				return true;
			}

			[[nodiscard]] inline size_t count(std::string_view key) const override
			{
				if (isObject())
				{
					//const json11::Json &j = m_json->operator[](key.data());
					//size_t s1 = j.object_items().size();
					return m_json->object_items().count(key.data());
				}

				return 0;
			}

			[[nodiscard]] inline bool any(std::string_view key) const override
			{
				return count(key) > 0;
			}

			[[nodiscard]] inline bool isArray() const override
			{
				return m_json->is_array();
			}

			[[nodiscard]] inline bool isObject() const override
			{
				return m_json->is_object();
			}

			[[nodiscard]] inline bool isNull() const override
			{
				return m_json->is_null();
			}

			fs::path directory() const override
			{
				return m_path;
			}

			void directory(const fs::path &directory) override
			{
				m_path = directory;
			}

		protected:
			[[nodiscard]] inline int32_t getInt32(std::string_view key) override
			{
				return static_cast<int32_t>(getDouble(key));
			}

			[[nodiscard]] inline uint32_t getUInt32(std::string_view key) override
			{
				return static_cast<uint32_t>(getDouble(key));
			}

			[[nodiscard]] inline int64_t getInt64(std::string_view key) override
			{
				return static_cast<int64_t>(getDouble(key));
			}

			[[nodiscard]] inline uint64_t getUInt64(std::string_view key) override
			{
				return static_cast<uint64_t>(getDouble(key));
			}

			[[nodiscard]] inline double getDouble(std::string_view key) override
			{
				return m_json->operator[](key.data()).number_value();
			}

			[[nodiscard]] inline std::string getString(std::string_view key) override
			{
				return m_json->operator[](key.data()).string_value(); // .get<std::string>();
			}

			[[nodiscard]] inline bool getBool(std::string_view key) override
			{
				return m_json->operator[](key.data()).bool_value();
			}

			[[nodiscard]] float getFloat(std::string_view key) override
			{
				return static_cast<float>(getDouble(key));
			}

			[[nodiscard]] inline int32_t getInt32() override
			{
				return static_cast<int32_t>(getDouble());
			}

			[[nodiscard]] inline uint32_t getUInt32() override
			{
				return static_cast<uint32_t>(getDouble());
			}

			[[nodiscard]] inline int64_t getInt64() override
			{
				return static_cast<int64_t>(getDouble());
			}

			[[nodiscard]] inline uint64_t getUInt64() override
			{
				return static_cast<uint64_t>(getDouble());
			}

			[[nodiscard]] inline double getDouble() override
			{
				return m_json->number_value();
			}

			[[nodiscard]] inline std::string getString() override
			{
				return m_json->string_value();
			}

			[[nodiscard]] inline bool getBool() override
			{
				return m_json->bool_value();
			}

			[[nodiscard]] float getFloat() override
			{
				return static_cast<float>(getDouble());
			}

		private:

			inline void clearCache()
			{
				m_arrayCache.clear();
				m_arrayPosCache.clear();
				m_arrayListDataCache.clear();
			}

			//Owner values
			std::unique_ptr<json11::Json> m_data = nullptr; //Only used if this is the owner json!

			const json11::Json *m_json = nullptr;
			fs::path m_path;

			//Cache!
			std::map<std::string, std::unique_ptr<IJson>> m_arrayCache;
			std::map<size_t, std::unique_ptr<IJson>> m_arrayPosCache;
			std::map<std::string, std::vector<std::unique_ptr<IJson>>> m_arrayListDataCache;

	};
}

#endif //TILESON_JSON11_HPP

/*** End of inlined file: Json11.hpp ***/



/*** Start of inlined file: Layer.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_LAYER_HPP
#define TILESON_LAYER_HPP

#include <set>
//#include "../external/json.hpp"


/*** Start of inlined file: Chunk.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_CHUNK_HPP
#define TILESON_CHUNK_HPP

//#include "../external/json.hpp"

namespace tson
{
	class Chunk
	{
		public:
			inline Chunk() = default;
			inline explicit Chunk(IJson &json);
			inline bool parse(IJson &json);

			[[nodiscard]] inline const std::vector<int> &getData() const;
			[[nodiscard]] inline const std::string &getBase64Data() const;
			[[nodiscard]] inline const Vector2i &getSize() const;
			[[nodiscard]] inline const Vector2i &getPosition() const;

		private:
			std::vector<int> m_data;        /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. */
			std::string      m_base64Data;  /*! 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. */
			tson::Vector2i   m_size;        /*!  x='width' (in tiles) and y='height' (in tiles): */
			tson::Vector2i   m_position;    /*! 'x' and 'y' position in tiles */
	};
}

#endif //TILESON_CHUNK_HPP

/*!
 * Parses 'chunk' data from Tiled json and stores the values in this class
 * @param json json-data
 */
tson::Chunk::Chunk(IJson &json)
{
	parse(json);
}

/*!
 * Parses 'chunk' data from Tiled json and stores the values in this class
 * @param json json-data
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Chunk::parse(IJson &json)
{
	bool allFound = true;

	if(json.count("width") > 0 && json.count("height") > 0)
		m_size = {json["width"].get<int>(), json["height"].get<int>()}; else allFound = false;
	if(json.count("x") > 0 && json.count("y") > 0)
		m_position = {json["x"].get<int>(), json["y"].get<int>()}; else allFound = false;

	//Handle DATA (Optional)
	if(json.count("data") > 0)
	{
		if(json["data"].isArray())
		{
			auto &data = json.array("data");
			std::for_each(data.begin(), data.end(), [&](std::unique_ptr<IJson> &item) { m_data.push_back(item->get<int>()); });
		}
		else
			m_base64Data = json["data"].get<std::string>();
	}

	return allFound;
}

/*!
 * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only.
 * @return list of tile ids
 */
const std::vector<int> &tson::Chunk::getData() const
{
	return m_data;
}

/*!
 * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data.
 * @return base64 string
 */
const std::string &tson::Chunk::getBase64Data() const
{
	return m_base64Data;
}

/*!
 * x='width' (in tiles) and y='height' (in tiles).
 * @return Size (x and y), containing the values from the fields 'width' and 'height' in Tiled
 */
const tson::Vector2i &tson::Chunk::getSize() const
{
	return m_size;
}

/*!
 * 'x' and 'y' position in tiles
 * @return Position in int
 */
const tson::Vector2i &tson::Chunk::getPosition() const
{
	return m_position;
}
/*** End of inlined file: Chunk.hpp ***/


/*** Start of inlined file: Object.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_OBJECT_HPP
#define TILESON_OBJECT_HPP

//#include "../external/json.hpp"


/*** Start of inlined file: PropertyCollection.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_PROPERTYCOLLECTION_HPP
#define TILESON_PROPERTYCOLLECTION_HPP


/*** Start of inlined file: Property.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_PROPERTY_HPP
#define TILESON_PROPERTY_HPP

//#include "../../TilesonConfig.h"

//#if USE_CPP17_FILESYSTEM

#include <any>
#include <string>

/*** Start of inlined file: Enums.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_ENUMS_HPP
#define TILESON_ENUMS_HPP
#include <cstdint>

/*** Start of inlined file: EnumBitflags.hpp ***/
//
// Created by robin on 08.11.2020.
//

#ifndef TILESON_ENUMBITFLAGS_HPP
#define TILESON_ENUMBITFLAGS_HPP

#include <type_traits>
#include <iostream>

#define TILESON_ENABLE_BITMASK_OPERATORS(x)  \
namespace tson {                         \
	ENABLE_BITMASK_OPERATORS(x) \
}

namespace tson
{
	#define ENABLE_BITMASK_OPERATORS(x)  \
	template<>                           \
	struct EnableBitMaskOperators<x>     \
	{                                    \
		static const bool enable = true; \
	};

	template<typename Enum>
	struct EnableBitMaskOperators
	{
		static const bool enable = false;
	};

	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	operator |(Enum lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		return static_cast<Enum> (
				static_cast<underlying>(lhs) |
				static_cast<underlying>(rhs)
		);
	}

	//Permissions operator &(Permissions lhs, Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	operator &(Enum lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		return static_cast<Enum> (
				static_cast<underlying>(lhs) &
				static_cast<underlying>(rhs)
		);
	}

	//Permissions operator ^(Permissions lhs, Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	operator ^(Enum lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		return static_cast<Enum> (
				static_cast<underlying>(lhs) ^
				static_cast<underlying>(rhs)
		);
	}

	//Permissions operator ~(Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	operator ~(Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		return static_cast<Enum> (
				~static_cast<underlying>(rhs)
		);
	}

	//Permissions& operator |=(Permissions &lhs, Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	&operator |=(Enum &lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		lhs = static_cast<Enum> (
				static_cast<underlying>(lhs) |
				static_cast<underlying>(rhs)
		);

		return lhs;
	}

	//Permissions& operator &=(Permissions &lhs, Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	&operator &=(Enum &lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		lhs = static_cast<Enum> (
				static_cast<underlying>(lhs) &
				static_cast<underlying>(rhs)
		);

		return lhs;
	}

	//Permissions& operator ^=(Permissions &lhs, Permissions rhs)
	template<typename Enum>
	typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type
	&operator ^=(Enum &lhs, Enum rhs)
	{
		static_assert(std::is_enum<Enum>::value,
					  "template parameter is not an enum type");

		using underlying = typename std::underlying_type<Enum>::type;

		lhs = static_cast<Enum> (
				static_cast<underlying>(lhs) ^
				static_cast<underlying>(rhs)
		);

		return lhs;
	}
}

#endif //TILESON_ENUMBITFLAGS_HPP

/*** End of inlined file: EnumBitflags.hpp ***/


namespace tson
{
	/*!
	 * Type used in Property.hpp
	 */
	enum class Type : uint8_t
	{
			Undefined = 0,
			Color = 1, /*! color */
			File = 2, /*! file */
			Int = 3, /*! int */
			Boolean = 4, /*! bool */
			Float = 5, /*! float */
			String = 6, /*! string */
			Class = 7, /*! class */
			Enum = 8,  /*! 'string' or 'int' with a value in 'propertyType' */
			Object = 9 /*! object */
	};

	/*!
	 * Layer.hpp - LayerType
	 * //'type': tilelayer, objectgroup, imagelayer or group
	 */
	enum class LayerType : uint8_t
	{
			Undefined = 0,
			TileLayer = 1,
			ObjectGroup = 2,
			ImageLayer = 3,
			Group = 4
	};

	/*!
	 * Map.hpp - ParseStatus
	 */
	enum class ParseStatus : uint8_t
	{
			OK = 0, //OK unless otherwise stated
			FileNotFound = 1,
			ParseError = 2,
			MissingData = 3,
			DecompressionError = 4
	};

	/*!
	 * Object.hpp - ObjectType
	 */
	enum class ObjectType : uint8_t
	{
			Undefined = 0,
			Object = 1,
			Ellipse = 2,
			Rectangle = 3,
			Point = 4,
			Polygon = 5,
			Polyline = 6,
			Text = 7,
			Template = 8
	};

	static constexpr uint32_t FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
	static constexpr uint32_t FLIPPED_VERTICALLY_FLAG   = 0x40000000;
	static constexpr uint32_t FLIPPED_DIAGONALLY_FLAG   = 0x20000000;
	/*!
	 * Object.hpp - ObjectFlipFlags
	 */
	enum class TileFlipFlags : uint32_t
	{
			None = 0,
			Diagonally = FLIPPED_DIAGONALLY_FLAG,
			Vertically = FLIPPED_VERTICALLY_FLAG,
			Horizontally = FLIPPED_HORIZONTALLY_FLAG
	};

	/*!
	 * Tileset.hpp - ObjectAlignment
	 */
	enum class ObjectAlignment : uint8_t
	{
			Unspecified = 0,    //unspecified
			TopLeft = 1,        //topleft
			Top = 2,            //top
			TopRight = 3,       //topright
			Left = 4,           //left
			Center = 5,         //center
			Right = 6,          //right
			BottomLeft = 7,     //bottomleft
			Bottom = 8,         //bottom
			BottomRight = 9     //bottomright
	};

	/*!
	 * Text.hpp - TextAlignment
	 */
	enum class TextAlignment : uint8_t
	{
		Unresolved = 0,
		Left = 1,           //left
		Center = 2,         //center
		Right = 3,          //right
		Justify = 4,        //justify
		Top = 5,            //top
		Bottom = 6          //bottom
	};

	/*!
	 * Tileset.hpp - TileRenderSize - From Tiled v1.9
	 */
	enum class TileRenderSize : uint8_t
	{
		Undefined = 0,
		Tile = 1,  //tile (default)
		Grid = 2   //grid
	};

	/*!
	 * Tileset.hpp - FillMode - From Tiled v1.9
	 */
	enum class FillMode : uint8_t
	{
		Undefined = 0,
		Stretch = 1,            //stretch (default)
		PreserveAspectFit = 2   //preserve-aspect-fit
	};

	enum class EnumStorageType : uint8_t
	{
		Unspecified = 0,
		Int = 1,
		String = 2
	};

	ENABLE_BITMASK_OPERATORS(TileFlipFlags)
}

#endif //TILESON_ENUMS_HPP

/*** End of inlined file: Enums.hpp ***/


//#include "../external/json.hpp"

namespace tson
{
	class Project;

	class Property
	{
		public:

			//enum class Type : uint8_t
			//{
			//        Undefined = 0,
			//        Color = 1, /*! color */
			//        File = 2, /*! file */
			//        Int = 3, /*! int */
			//        Boolean = 4, /*! bool */
			//        Float = 5, /*! float */
			//        String = 6 /*! string */
			//};

			inline Property();
			inline explicit Property(IJson &json, tson::Project *project = nullptr);
			inline Property(std::string name, std::any value, Type type);

			inline void setValue(const std::any &value);
			inline void setStrValue(const std::string &value);
			inline void setName(const std::string &name);

			[[nodiscard]] inline const std::type_info& getValueType() const;
			inline std::string getValueTypeInfo();
			[[nodiscard]]inline const std::any &getValue() const;
			template <typename T>
			inline T getValue() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline Type getType() const;
			[[nodiscard]] inline const std::string &getPropertyType() const;

			//Became public in v1.4.0
			inline void setValueByType(IJson &json); //Definition in tileson_forward.hpp

		protected:
			inline void setTypeByString(const std::string &str);

			tson::Project *m_project = nullptr; //Used for resolving 'enum' and 'class' objects
			Type m_type = Type::Undefined;
			std::string m_name {};
			std::string m_propertyType {};
			std::any m_value; //Using std::any to assign any type
	};

	template<typename T>
	T Property::getValue() const
	{
		bool isCorrectType = (m_value.type() == typeid(T));

		if(isCorrectType)
		{
			T value = std::any_cast<T>(m_value);
			return value;
		}
		else
		{
			static T defaultValue;
			return defaultValue;
		}
	}
}

tson::Property::Property() : m_name {"unnamed"}
{

}

tson::Property::Property(IJson &json, tson::Project *project) : m_project {project}
{
	m_name = json["name"].get<std::string>();
	if(json.count("propertytype") > 0)
		m_propertyType = json["propertytype"].get<std::string>();
	else if(json.count("propertyType") > 0) //Somehow Tiled's class objects uses propertyType with 'T'.
		m_propertyType = json["propertyType"].get<std::string>();

	setTypeByString(json["type"].get<std::string>());
	setValueByType(json["value"]);
}

tson::Property::Property(std::string name, std::any value, Type type) : m_type {type}, m_name { move(name) }, m_value { move(value) }
{

}

void tson::Property::setValue(const std::any &value)
{
	m_value = value;
}

/*!
 * Sets the value specifically as string.
 * When not specified as std::string, the default is that the value will be set as char * when adding a value like "test"
 * This function is to make sure the value is added as string.
 * @param value
 */
void tson::Property::setStrValue(const std::string &value)
{
	m_value = value;
}

const std::any &tson::Property::getValue() const
{
	return m_value;
}

void tson::Property::setName(const std::string &name)
{
	m_name = name;
}

const std::string &tson::Property::getName() const
{
	return m_name;
}

/*!
 * Gets the value type as std::value_info.
 * This can easily be compared to types like this:
 * Check if int: getValueType() == typeid(int)
 * @return
 */

const std::type_info &tson::Property::getValueType() const
{
	return m_value.type();
}

/*!
 * Gets the value type as std::string
 * Examples of known types:
 * "i" = int
 * "f" = float
 * "b" = bool
 * @return
 */
std::string tson::Property::getValueTypeInfo()
{
	return m_value.type().name();
}

tson::Type tson::Property::getType() const
{
	return m_type;
}

void tson::Property::setTypeByString(const std::string &str)
{
	if(str == "color")
		m_type = tson::Type::Color;
	else if(str == "file")
		m_type = tson::Type::File;
	else if(str == "int")
		m_type = tson::Type::Int;
	else if(str == "bool")
		m_type = tson::Type::Boolean;
	else if(str == "float")
		m_type = tson::Type::Float;
	else if(str == "string")
		m_type = tson::Type::String;
	else if(str == "class")
		m_type = tson::Type::Class;
	else if(str == "object")
		m_type = tson::Type::Object;
	else
		m_type = tson::Type::Undefined;
}

const std::string &tson::Property::getPropertyType() const
{
	return m_propertyType;
}

#endif //TILESON_PROPERTY_HPP

/*** End of inlined file: Property.hpp ***/

//#include "../external/json.hpp"
#include <map>

namespace tson
{
	class PropertyCollection
	{
		public:
			inline PropertyCollection() = default;

			inline explicit PropertyCollection(std::string id);

			inline tson::Property * add(const tson::Property &property);
			inline tson::Property * add(IJson &json, tson::Project *project = nullptr);
			inline tson::Property * add(const std::string &name, const std::any &value, tson::Type type);

			inline void remove(const std::string &name);

			inline void setValue(const std::string &name, const std::any &value);
			inline void setProperty(const std::string &name, const tson::Property &value);
			inline void setId(const std::string &id);

			inline bool hasProperty(const std::string &name);
			inline tson::Property * getProperty(const std::string &name);
			inline std::map<std::string, Property> &getProperties();
			inline std::vector<Property*> get();
			template <typename T>
			inline T getValue(const std::string &name);
			[[nodiscard]] inline const std::string &getId() const;
			[[nodiscard]] inline size_t getSize() const;

		protected:
			std::string m_id;
			std::map<std::string, tson::Property> m_properties;
	};
}

template<typename T>
T tson::PropertyCollection::getValue(const std::string &name)
{
	static T defaultT;
	return (m_properties.count(name) > 0) ? m_properties[name].getValue<T>() : defaultT;
}

tson::PropertyCollection::PropertyCollection(std::string id) : m_id {std::move(id)}
{

}

tson::Property *tson::PropertyCollection::add(const tson::Property &property)
{
	m_properties[property.getName()] = property;
	return &m_properties[property.getName()];
}

tson::Property *tson::PropertyCollection::add(IJson &json, tson::Project *project)
{
	tson::Property property = tson::Property(json, project);
	std::string name = property.getName();
	m_properties[name] = std::move(property);
	return &m_properties[name];
}

tson::Property *tson::PropertyCollection::add(const std::string &name, const std::any &value, tson::Type type)
{
	m_properties[name] = {name, value, type};
	return &m_properties[name];
}

void tson::PropertyCollection::remove(const std::string &name)
{
	m_properties.erase(name);
}

/*!
 * Sets a value IF the property already exists. Does nothing otherwise.
 * See add() for adding new properties
 * @param name
 * @param value
 */
void tson::PropertyCollection::setValue(const std::string &name, const std::any &value)
{
	if(m_properties.count(name) > 0)
		m_properties[name].setValue(value);
}

/*!
 * Overwrites the current property if it exists, or adds it if it doesn't.
 * @param name
 * @param value
 */
void tson::PropertyCollection::setProperty(const std::string &name, const tson::Property &value)
{
	m_properties[name] = value;
}

void tson::PropertyCollection::setId(const std::string &id)
{
	m_id = id;
}

bool tson::PropertyCollection::hasProperty(const std::string &name)
{
	return m_properties.count(name) > 0;
}

tson::Property *tson::PropertyCollection::getProperty(const std::string &name)
{
	return (m_properties.count(name) > 0) ? &m_properties[name] : nullptr;
}

std::map<std::string, tson::Property> &tson::PropertyCollection::getProperties()
{
	return m_properties;
}

/*!
 * Gets vector of pointers to all the existing properties
 * @return
 */
std::vector<tson::Property *> tson::PropertyCollection::get()
{
	std::vector<tson::Property *> props;
	for(auto &i : m_properties)
		props.emplace_back(&i.second);

	return props;
}

const std::string &tson::PropertyCollection::getId() const
{
	return m_id;
}

size_t tson::PropertyCollection::getSize() const
{
	return m_properties.size();
}

#endif //TILESON_PROPERTYCOLLECTION_HPP

/*** End of inlined file: PropertyCollection.hpp ***/


/*** Start of inlined file: Text.hpp ***/
//
// Created by robin on 05.08.2019.
//

#ifndef TILESON_TEXT_HPP
#define TILESON_TEXT_HPP

#include <string>

namespace tson
{
	class Text
	{
		public:
			inline Text() = default;
			/*!
			 *
			 * @param _text Text
			 * @param _wrap If the text is marked as wrapped
			 */
			//inline Text(std::string _text, bool _wrap, tson::Colori _color) : text {std::move(_text)}, wrap {_wrap}, color {_color} {};
			inline explicit Text(IJson &json)
			{
				bool hasColor = json.count("color") > 0;
				tson::Color c = (hasColor) ? tson::Colori(json["color"].get<std::string>()) : tson::Colori();
				color = c;
				text = (json.count("text") > 0) ? json["text"].get<std::string>() : "";
				wrap = (json.count("wrap") > 0) ? json["wrap"].get<bool>() : false;

				//Previously missing properties
				bold = (json.count("bold") > 0) ? json["bold"].get<bool>() : false;
				fontFamily = (json.count("fontfamily") > 0) ? json["fontfamily"].get<std::string>() : "sans-serif";
				horizontalAlignment = (json.count("halign") > 0) ? resolveTextAlignmentByString(json["halign"].get<std::string>()) : TextAlignment::Left;
				italic = (json.count("italic") > 0) ? json["italic"].get<bool>() : false;
				kerning = (json.count("kerning") > 0) ? json["kerning"].get<bool>() : true;
				pixelSize = (json.count("pixelsize") > 0) ? json["pixelsize"].get<int32_t>() : 16;
				strikeout = (json.count("strikeout") > 0) ? json["strikeout"].get<bool>() : false;
				underline = (json.count("underline") > 0) ? json["underline"].get<bool>() : false;
				verticalAlignment = (json.count("valign") > 0) ? resolveTextAlignmentByString(json["valign"].get<std::string>()) : TextAlignment::Top;
			};

			//Just make it simple
			std::string text {};
			tson::Colori color {};
			bool wrap{};

			//Previously missing properties
			bool bold {false};
			std::string fontFamily {"sans-serif"};
			TextAlignment horizontalAlignment {TextAlignment::Left};
			bool italic {false};
			bool kerning {true};
			int pixelSize {16};
			bool strikeout {false};
			bool underline {false};
			TextAlignment verticalAlignment {TextAlignment::Top};

		private:
			[[nodiscard]] TextAlignment resolveTextAlignmentByString(const std::string &str) const
			{
				if(str == "left") return TextAlignment::Left;
				if(str == "center") return TextAlignment::Center;
				if(str == "right") return TextAlignment::Right;
				if(str == "justify") return TextAlignment::Justify;
				if(str == "top") return TextAlignment::Top;
				if(str == "bottom") return TextAlignment::Bottom;

				return TextAlignment::Unresolved;
			}
	};
}

#endif //TILESON_TEXT_HPP

/*** End of inlined file: Text.hpp ***/

#include <optional>

namespace tson
{
	class TiledClass;
	class Map;
	class Object
	{
		public:
			//enum class Type : uint8_t
			//{
			//        Undefined = 0,
			//        Object = 1,
			//        Ellipse = 2,
			//        Rectangle = 3,
			//        Point = 4,
			//        Polygon = 5,
			//        Polyline = 6,
			//        Text = 7,
			//        Template = 8
			//};

			inline Object() = default;
			inline explicit Object(IJson &json, tson::Map *map);
			inline bool parse(IJson &json, tson::Map *map);

			[[nodiscard]] inline ObjectType getObjectType() const;
			[[nodiscard]] inline bool isEllipse() const;
			[[nodiscard]] inline uint32_t getGid() const;
			[[nodiscard]] inline const Vector2i &getSize() const;
			[[nodiscard]] inline int getId() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline bool isPoint() const;
			[[nodiscard]] inline float getRotation() const;
			[[nodiscard]] inline const std::string &getTemplate() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */
			[[nodiscard]] inline bool isVisible() const;
			[[nodiscard]] inline const Vector2i &getPosition() const;

			[[nodiscard]] inline const std::vector<tson::Vector2i> &getPolygons() const;
			[[nodiscard]] inline const std::vector<tson::Vector2i> &getPolylines() const;
			[[nodiscard]] inline PropertyCollection &getProperties();
			[[nodiscard]] inline const Text &getText() const;

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			//v1.2.0-stuff
			[[nodiscard]] inline TileFlipFlags getFlipFlags() const;
			inline bool hasFlipFlags(TileFlipFlags flags);

		private:
			inline void setObjectTypeByJson(IJson &json);

			ObjectType                        m_objectType = ObjectType::Undefined;    /*! Says with object type this is */
			bool                              m_ellipse {};                            /*! 'ellipse': Used to mark an object as an ellipse */
			uint32_t                          m_gid {};                                /*! 'gid': GID, only if object comes from a Tilemap */
			tson::Vector2i                    m_size;                                  /*! x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.)*/
			int                               m_id{};                                  /*! 'id': Incremental id - unique across all objects */
			std::string                       m_name;                                  /*! 'name':  String assigned to name field in editor*/
			bool                              m_point {};                              /*! 'point': Used to mark an object as a point */
			std::vector<tson::Vector2i>       m_polygon; 	                           /*! 'polygon': A list of x,y coordinates in pixels */
			std::vector<tson::Vector2i>       m_polyline; 	                           /*! 'polyline': A list of x,y coordinates in pixels */
			tson::PropertyCollection          m_properties; 	                       /*! 'properties': A list of properties (name, value, type). */
			float                             m_rotation {};                           /*! 'rotation': Angle in degrees clockwise */
			std::string                       m_template;                              /*! 'template': Reference to a template file, in case object is a template instance */
			tson::Text                        m_text; 	                               /*! first: 'text' second: 'wrap' */
			std::string                       m_type;                                  /*! 'type': String assigned to type field in editor */
			bool                              m_visible {};                            /*! 'visible': Whether object is shown in editor. */
			tson::Vector2i                    m_position;                              /*! 'x' and 'y': coordinate in pixels */

			//v1.2.0-stuff
			tson::TileFlipFlags               m_flipFlags = TileFlipFlags::None;       /*! Resolved using bit 32, 31 and 30 from gid */

			tson::Map *m_map {nullptr};
			std::shared_ptr<tson::TiledClass> m_class {};
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::Object::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

/*!
 * Parses a json Tiled object
 * @param json
 */
tson::Object::Object(IJson &json, tson::Map *map)
{
	parse(json, map);
}

/*!
 * Parses a json Tiled object and autoamtically determines the object type based on the data presented.
 * Call getObjectType() to see what object type it is.
 * @param json
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Object::parse(IJson &json, tson::Map *map)
{
	m_map = map;
	bool allFound = true;

	if(json.count("ellipse") > 0) m_ellipse = json["ellipse"].get<bool>(); //Optional
	if(json.count("gid") > 0)
	{
		uint32_t gid = json["gid"].get<uint32_t>(); //Optional
		if (gid & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally;
		if (gid & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically;
		if (gid & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally;

		// Clear flags
		gid &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG);

		m_gid = gid;
	}
	if(json.count("id") > 0) m_id = json["id"].get<int>(); else allFound = false;
	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;
	if(json.count("point") > 0) m_point = json["point"].get<bool>(); //Optional
	if(json.count("rotation") > 0) m_rotation = json["rotation"].get<float>(); else allFound = false;
	if(json.count("template") > 0) m_template = json["template"].get<std::string>(); //Optional

	if(json.count("type") > 0) m_type = json["type"].get<std::string>();
	else if(json.count("class") > 0) m_type = json["class"].get<std::string>(); //Tiled v1.9 renamed 'type' to 'class'
	else allFound = false;

	if(json.count("visible") > 0) m_visible = json["visible"].get<bool>(); else allFound = false;

	if(json.count("width") > 0 && json.count("height") > 0)
		m_size = {json["width"].get<int>(), json["height"].get<int>()}; else allFound = false;
	if(json.count("x") > 0 && json.count("y") > 0)
		m_position = {json["x"].get<int>(), json["y"].get<int>()}; else allFound = false;

	if(json.count("text") > 0)
	{
		//Old logic
		//bool hasColor = json["text"].count("color") > 0;
		//tson::Color c = (hasColor) ? tson::Colori(json["text"]["color"].get<std::string>()) : tson::Colori();
		//m_text = {json["text"]["text"].get<std::string>(), json["text"]["wrap"].get<bool>(), c}; //Optional
		m_text = tson::Text(json["text"]);
		//

	}

	setObjectTypeByJson(json);

	if(m_objectType == ObjectType::Template)
		allFound = true; //Just accept anything with this type

	//More advanced data
	if(json.count("polygon") > 0 && json["polygon"].isArray())
	{
		auto &polygon = json.array("polygon");
		std::for_each(polygon.begin(), polygon.end(),[&](std::unique_ptr<IJson> &item)
		{
			IJson &j = *item;
			m_polygon.emplace_back(j["x"].get<int>(), j["y"].get<int>());
		});

	}

	if(json.count("polyline") > 0 && json["polyline"].isArray())
	{
		auto &polyline = json.array("polyline");
		std::for_each(polyline.begin(), polyline.end(),[&](std::unique_ptr<IJson> &item)
		{
			IJson &j = *item;
			m_polyline.emplace_back(j["x"].get<int>(), j["y"].get<int>());
		});
	}

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item)
		{
			m_properties.add(*item);
		});
	}

	return allFound;
}

/*!
 * Sets an object type based on json data.
 * @param json
 */
void tson::Object::setObjectTypeByJson(IJson &json)
{
	m_objectType = ObjectType::Undefined;
	if(m_ellipse)
		m_objectType = ObjectType::Ellipse;
	else if(m_point)
		m_objectType = ObjectType::Point;
	else if(json.count("polygon") > 0)
		m_objectType = ObjectType::Polygon;
	else if(json.count("polyline") > 0)
		m_objectType = ObjectType::Polyline;
	else if(json.count("text") > 0)
		m_objectType = ObjectType::Text;
	else if(json.count("gid") > 0)
		m_objectType = ObjectType::Object;
	else if(json.count("template") > 0)
		m_objectType = ObjectType::Template;
	else
		m_objectType = ObjectType::Rectangle;
}

/*!
 * Gets what type of object this is.
 * @return
 */

tson::ObjectType tson::Object::getObjectType() const
{
	return m_objectType;
}

/*!
 * 'ellipse': Used to mark an object as an ellipse
 * @return
 */
bool tson::Object::isEllipse() const
{
	return m_ellipse;
}

/*!
 * 'gid': GID, only if object comes from a Tilemap
 * @return
 */
uint32_t tson::Object::getGid() const
{
	return m_gid;
}

/*!
 * x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.)
 * @return
 */
const tson::Vector2i &tson::Object::getSize() const
{
	return m_size;
}

/*!
 * 'id': Incremental id - unique across all objects
 * @return
 */
int tson::Object::getId() const
{
	return m_id;
}

/*!
 * 'name': String assigned to name field in editor
 * @return
 */
const std::string &tson::Object::getName() const
{
	return m_name;
}

/*!
 * 'point': Used to mark an object as a point
 * @return true if the object is of type point
 */
bool tson::Object::isPoint() const
{
	return m_point;
}

/*!
 * 'rotation': Angle in degrees clockwise
 * @return
 */
float tson::Object::getRotation() const
{
	return m_rotation;
}

/*!
 * 'template': Reference to a template file, in case object is a template instance
 * @return
 */
const std::string &tson::Object::getTemplate() const
{
	return m_template;
}

/*!
 * 'type': String assigned to type field in editor
 * This was renamed to 'class' in Tiled v1.9
 * @return
 */
const std::string &tson::Object::getType() const
{
	return m_type;
}

/*!
 * 'class': String assigned to class field in editor
 * This was renamed from 'type' to 'class' in Tiled v1.9
 * @return
 */
const std::string &tson::Object::getClassType() const
{
	return m_type;
}

/*!
 * 'visible': Whether object is shown in editor.
 * @return
 */
bool tson::Object::isVisible() const
{
	return m_visible;
}

/*!
 * 'x' and 'y': coordinate in pixels
 * @return
 */
const tson::Vector2i &tson::Object::getPosition() const
{
	return m_position;
}

/*!
 * 'polygon': A list of x,y coordinates in pixels.
 * If this is a Polygon type, this function will return the points used to create it
 * @return
 */
const std::vector<tson::Vector2i> &tson::Object::getPolygons() const
{
	return m_polygon;
}

/*!
 * 'polyline': A list of x,y coordinates in pixels
 * If this is a Polyline type, this function will return the points used to create it
 * @return
 */
const std::vector<tson::Vector2i> &tson::Object::getPolylines() const
{
	return m_polyline;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::Object::getProperties()
{
	return m_properties;
}

/*!
 * 'type': String assigned to type field in editor
 * @return
 */
const tson::Text &tson::Object::getText() const
{
	return m_text;
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Object::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);
	return nullptr;
}

/*!
 * Get all flip flags
 * @return
 */
tson::TileFlipFlags tson::Object::getFlipFlags() const
{
	return m_flipFlags;
}

/*!
 *
 * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator.
 * Example:
 * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally)
 *
 * @return true if the flag(s) specified are set
 */
bool tson::Object::hasFlipFlags(TileFlipFlags flags)
{
	return ((m_flipFlags & flags) == flags) ? true : false;
}

#endif //TILESON_OBJECT_HPP

/*** End of inlined file: Object.hpp ***/


/*** Start of inlined file: TileObject.hpp ***/
//
// Created by robin on 26.07.2020.
//

#ifndef TILESON_TILEOBJECT_HPP
#define TILESON_TILEOBJECT_HPP


/*** Start of inlined file: Rect.hpp ***/
//
// Created by robin on 24.07.2020.
//

#ifndef TILESON_RECT_HPP
#define TILESON_RECT_HPP

namespace tson
{
	class Rect
	{
		public:

			inline Rect() = default;
			inline Rect(int x_, int y_, int width_, int height_);

			inline bool operator==(const Rect &rhs) const;
			inline bool operator!=(const Rect &rhs) const;

			int x{};
			int y{};
			int width{};
			int height{};
	};

	Rect::Rect(int x_, int y_, int width_, int height_)
	{
		x = x_;
		y = y_;
		width = width_;
		height = height_;
	}

	bool Rect::operator==(const Rect &rhs) const
	{
		return x == rhs.x &&
			   y == rhs.y &&
			   width == rhs.width &&
			   height == rhs.height;
	}

	bool Rect::operator!=(const Rect &rhs) const
	{
		return !(rhs == *this);
	}
}

#endif //TILESON_RECT_HPP

/*** End of inlined file: Rect.hpp ***/

namespace tson
{
	class Tile;
	class TileObject
	{
		public:
			inline TileObject() = default;
			inline TileObject(const std::tuple<int, int> &posInTileUnits, tson::Tile *tile);

			inline void initialize(const std::tuple<int, int> &posInTileUnits, tson::Tile *tile); //Defined in tileson_forward.hpp

			inline Tile *getTile();
			inline const Vector2i &getPositionInTileUnits() const;
			inline const Vector2f &getPosition() const;
			inline const tson::Rect &getDrawingRect() const; //Defined in tileson_forward.hpp

		private:
			tson::Tile *m_tile;
			tson::Vector2i m_posInTileUnits;
			tson::Vector2f m_position;

	};

	TileObject::TileObject(const std::tuple<int, int> &posInTileUnits, tson::Tile *tile)
	{
		initialize(posInTileUnits, tile);
	}

	/*!
	 * Get a pointer to the related tile
	 * @return
	 */
	Tile *TileObject::getTile()
	{
		return m_tile;
	}

	/*!
	 * Gets the position of the tile in tile units
	 * @return
	 */
	const Vector2i &TileObject::getPositionInTileUnits() const
	{
		return m_posInTileUnits;
	}

	/*!
	 * Gets the position of the tile in pixels.
	 * @return
	 */
	const Vector2f &TileObject::getPosition() const
	{
		return m_position;
	}
}

#endif //TILESON_TILEOBJECT_HPP

/*** End of inlined file: TileObject.hpp ***/


/*** Start of inlined file: FlaggedTile.hpp ***/
//
// Created by robin on 13.11.2020.
//

#ifndef TILESON_FLAGGEDTILE_HPP
#define TILESON_FLAGGEDTILE_HPP

namespace tson
{
	class FlaggedTile
	{

		public:
			FlaggedTile(size_t x_, size_t y_, uint32_t id_, uint32_t tileId_) : x {x_}, y {y_}, id {id_}, tileId {tileId_}
			{

			}
			size_t x;
			size_t y;
			/*! Full ID, including flag */
			uint32_t id;
			/*! ID of the flagged tile */
			uint32_t tileId;
	};
}
#endif //TILESON_FLAGGEDTILE_HPP

/*** End of inlined file: FlaggedTile.hpp ***/

namespace tson
{
	class Tile;
	class Map;

	class Layer
	{
		public:
			inline Layer() = default;
			inline Layer(IJson &json, tson::Map *map);
			inline bool parse(IJson &json, tson::Map *map); //Defined in tileson_forward

			[[nodiscard]] inline const std::string &getCompression() const;
			[[nodiscard]] inline const std::vector<uint32_t> &getData() const;
			[[nodiscard]] inline const std::string &getBase64Data() const;
			[[nodiscard]] inline const std::string &getDrawOrder() const;
			[[nodiscard]] inline const std::string &getEncoding() const;
			[[nodiscard]] inline int getId() const;
			[[nodiscard]] inline const std::string &getImage() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline const Vector2f &getOffset() const;
			[[nodiscard]] inline float getOpacity() const;
			[[nodiscard]] inline const Vector2i &getSize() const;
			[[nodiscard]] inline const Colori &getTransparentColor() const;
			[[nodiscard]] inline const Vector2f &getParallax() const;
			[[nodiscard]] inline bool hasRepeatX() const;
			[[nodiscard]] inline bool hasRepeatY() const;

			[[nodiscard]] inline LayerType getType() const;
			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */

			[[nodiscard]] inline const std::string &getTypeStr() const;
			[[nodiscard]] inline bool isVisible() const;
			[[nodiscard]] inline int getX() const;
			[[nodiscard]] inline int getY() const;

			[[nodiscard]] inline std::vector<tson::Chunk> &getChunks();
			[[nodiscard]] inline std::vector<tson::Layer> &getLayers();
			[[nodiscard]] inline std::vector<tson::Object> &getObjects();
			[[nodiscard]] inline PropertyCollection &getProperties();

			inline tson::Object *getObj(int id);
			inline tson::Object *firstObj(const std::string &name);
			inline std::vector<tson::Object> getObjectsByName(const std::string &name);
			inline std::vector<tson::Object> getObjectsByType(tson::ObjectType type);

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			inline void assignTileMap(std::map<uint32_t, tson::Tile*> *tileMap);
			inline void createTileData(const Vector2i &mapSize, bool isInfiniteMap);

			[[nodiscard]] inline const std::map<std::tuple<int, int>, tson::Tile *> &getTileData() const;
			inline tson::Tile * getTileData(int x, int y);

			//v1.2.0-stuff
			[[nodiscard]] inline const Colori &getTintColor() const;
			[[nodiscard]] inline tson::Map *getMap() const;

			[[nodiscard]] inline std::map<std::tuple<int, int>, tson::TileObject> &getTileObjects();
			inline tson::TileObject * getTileObject(int x, int y);
			[[nodiscard]] inline const std::set<uint32_t> &getUniqueFlaggedTiles() const;
			inline void resolveFlaggedTiles();

		private:
			inline void setTypeByString();

			std::vector<tson::Chunk>                       m_chunks; 	                      /*! 'chunks': Array of chunks (optional). tilelayer only. */
			std::string                                    m_compression;                     /*! 'compression': zlib, gzip or empty (default). tilelayer only. */
			std::vector<uint32_t>                          m_data;                            /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded
																							   *   data. tilelayer only. */
			std::string                                    m_base64Data;                      /*! 'data' (when string):     Array of unsigned int (GIDs) or base64-encoded
																							   *   data. tilelayer only. */
			std::string                                    m_drawOrder;                       /*! 'draworder': topdown (default) or index. objectgroup only. */
			std::string                                    m_encoding;                        /*! 'encoding': csv (default) or base64. tilelayer only. */
			int                                            m_id{};                            /*! 'id': Incremental id - unique across all layers */
			std::string                                    m_image;                           /*! 'image': Image used by this layer. imagelayer only. */
			std::vector<tson::Layer>                       m_layers; 	                      /*! 'layers': Array of layers. group on */
			std::string                                    m_name;                            /*! 'name': Name assigned to this layer */
			std::vector<tson::Object>                      m_objects;                         /*! 'objects': Array of objects. objectgroup only. */
			tson::Vector2f                                 m_offset;                          /*! 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels
																							   *  (default: {0, 0}) */
			float                                          m_opacity{};                       /*! 'opacity': Value between 0 and 1 */
			tson::PropertyCollection                       m_properties; 	                  /*! 'properties': A list of properties (name, value, type). */
			tson::Vector2i                                 m_size;                            /*! x = 'width': (Column count. Same as map width for fixed-size maps.)
																								  y = 'height': Row count. Same as map height for fixed-size maps. */
			tson::Colori                                   m_transparentColor;                /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional, imagelayer only */
			std::string                                    m_typeStr;                         /*! 'type': tilelayer, objectgroup, imagelayer or group */
			LayerType                                      m_type {LayerType::Undefined};     /*! Layer type as enum*/
			bool                                           m_visible{};                       /*! 'visible': Whether layer is shown or hidden in editor */
			int                                            m_x{};                             /*! 'x': Horizontal layer offset in tiles. Always 0. */
			int                                            m_y{};                             /*! 'y': Vertical layer offset in tiles. Always 0. */
			tson::Vector2f                                 m_parallax{1.f, 1.f};    /*! Tiled v1.5: parallax factor for this layer. Defaults to 1.
																								  x = 'parallaxx', y = 'parallaxy'*/
			bool                                           m_repeatX {};                         /*! 'repeatx': Whether the image drawn by this layer is repeated along the X axis. (since Tiled 1.8)*/
			bool                                           m_repeatY {};                         /*! 'repeaty': Whether the image drawn by this layer is repeated along the Y axis. (since Tiled 1.8)*/

			std::map<uint32_t, tson::Tile*>                *m_tileMap;
			std::map<std::tuple<int, int>, tson::Tile*>    m_tileData;                        /*! Key: Tuple of x and y pos in tile units. */

			//v1.2.0-stuff
			tson::Colori                                        m_tintColor;                  /*! 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with
																							   *        any graphics drawn by this layer or any child layers (optional). */
			inline void decompressData();                                                     /*! Defined in tileson_forward.hpp */
			inline void queueFlaggedTile(size_t x, size_t y, uint32_t id);                    /*! Queue a flagged tile */

			tson::Map *                                         m_map;                        /*! The map who owns this layer */
			std::map<std::tuple<int, int>, tson::TileObject>    m_tileObjects;
			std::set<uint32_t>                                  m_uniqueFlaggedTiles;
			std::vector<tson::FlaggedTile>                      m_flaggedTiles;

			std::string                                         m_classType{};              /*! 'class': The class of this map (since 1.9, defaults to “”). */
			std::shared_ptr<tson::TiledClass>                   m_class {};
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T Layer::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

/*!
 * Parses a Tiled layer from json
 * @param json
 */
tson::Layer::Layer(IJson &json, tson::Map *map)
{
	parse(json, map);
}

void tson::Layer::queueFlaggedTile(size_t x, size_t y, uint32_t id)
{
	uint32_t tileId = id;
	tileId &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG);
	m_uniqueFlaggedTiles.insert(id);
	m_flaggedTiles.emplace_back(x, y, id, tileId);
}

/*!
 * Copies all objects with a name that equals the parameter
 * @param name Name of the objects to return
 * @return All objects with a matching name
 */
std::vector<tson::Object> tson::Layer::getObjectsByName(const std::string &name)
{
	std::vector<tson::Object> found;

	std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item)
	{
		return item.getName() == name;
	});

	return found;
}

/*!
 * Copies all objects with a type that equals the parameter
 * @param type LayerType of the objects to return
 * @return All objects with a matching type
 */
std::vector<tson::Object> tson::Layer::getObjectsByType(tson::ObjectType type)
{
	std::vector<tson::Object> found;

	std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item)
	{
		return item.getObjectType() == type;
	});

	return found;
}

/*!
 * Returns the first object with the given name
 * @param name Name of the object to find.
 * @return A pointer to the object if found. nullptr otherwise.
 */
tson::Object *tson::Layer::firstObj(const std::string &name)
{
	auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getName() == name; });
	if(result == m_objects.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Get an object by ID
 * @param id Unique ID of the object
 * @return A pointer to the object if found. nullptr otherwise.
 */
tson::Object *tson::Layer::getObj(int id)
{
	auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getId() == id; });
	if(result == m_objects.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Set type by string
 * tilelayer, objectgroup, imagelayer or group
 */
void tson::Layer::setTypeByString()
{
	if(m_typeStr == "tilelayer") m_type = LayerType::TileLayer;
	else if(m_typeStr == "objectgroup") m_type = LayerType::ObjectGroup;
	else if(m_typeStr == "imagelayer") m_type = LayerType::ImageLayer;
	else if(m_typeStr == "group") m_type = LayerType::Group;
	else m_type = LayerType::Undefined;
}

/*!
 * 'compression': zlib, gzip or empty (default). tilelayer only.
 * @return
 */
const std::string &tson::Layer::getCompression() const
{
	return m_compression;
}

/*!
 * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only.
 * @return
 */
const std::vector<uint32_t> &tson::Layer::getData() const
{
	return m_data;
}

/*!
 * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only.
 * @return
 */
const std::string &tson::Layer::getBase64Data() const
{
	return m_base64Data;
}

/*!
 * 'draworder': topdown (default) or index. objectgroup only.
 * @return
 */
const std::string &tson::Layer::getDrawOrder() const
{
	return m_drawOrder;
}

/*!
 * 'encoding': csv (default) or base64. tilelayer only.
 * @return
 */
const std::string &tson::Layer::getEncoding() const
{
	return m_encoding;
}

/*!
 * 'id': Incremental id - unique across all layers
 * @return
 */
int tson::Layer::getId() const
{
	return m_id;
}

/*!
 * 'image': Image used by this layer. imagelayer only.
 * @return
 */
const std::string &tson::Layer::getImage() const
{
	return m_image;
}

/*!
 * 'name': Name assigned to this layer
 * @return
 */
const std::string &tson::Layer::getName() const
{
	return m_name;
}

/*!
 * 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels (default: {0, 0})
 * @return
 */
const tson::Vector2f &tson::Layer::getOffset() const
{
	return m_offset;
}

/*!
 * 'opacity': Value between 0 and 1
 * @return
 */
float tson::Layer::getOpacity() const
{
	return m_opacity;
}

/*!
 * x = 'width': (Column count. Same as map width for fixed-size maps.)
 * y = 'height': Row count. Same as map height for fixed-size maps.
 * @return width and height as a single size
 */
const tson::Vector2i &tson::Layer::getSize() const
{
	return m_size;
}

/*!
 * 'transparentcolor': Color created from a hex color (#RRGGBB) (optional, imagelayer only)
 * @return color as color object with rgba channel.
 */
const tson::Colori &tson::Layer::getTransparentColor() const
{
	return m_transparentColor;
}

/*!
 * 'type': tilelayer, objectgroup, imagelayer or group
 * @return string with the object type
 */
const std::string &tson::Layer::getTypeStr() const
{
	return m_typeStr;
}

/*!
 * 'visible': Whether layer is shown or hidden in editor
 * @return
 */
bool tson::Layer::isVisible() const
{
	return m_visible;
}

/*!
 * 'x': Horizontal layer offset in tiles. Always 0.
 * @return x value (always 0 for layer)
 */
int tson::Layer::getX() const
{
	return m_x;
}

/*!
 * 'y': Horizontal layer offset in tiles. Always 0.
 * @return y value (always 0 for layer)
 */
int tson::Layer::getY() const
{
	return m_y;
}

/*!
 * 'chunks': Array of chunks (optional). tilelayer only.
 * @return
 */
std::vector<tson::Chunk> &tson::Layer::getChunks()
{
	return m_chunks;
}

/*!
 * 'layers': Array of layers. group on
 * @return
 */
std::vector<tson::Layer> &tson::Layer::getLayers()
{
	return m_layers;
}

/*!
 * 'objects': Array of objects. objectgroup only.
 * @return
 */
std::vector<tson::Object> &tson::Layer::getObjects()
{
	return m_objects;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::Layer::getProperties()
{
	return m_properties;
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Layer::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);
	return nullptr;
}

/*!
 * Get layer type
 * @return Layer type as enum
 */
tson::LayerType tson::Layer::getType() const
{
	return m_type;
}

/*!
 * Assigns a tilemap of pointers to existing tiles.
 * @param tileMap The tilemap. key: tile id, value: pointer to Tile.
 */
void tson::Layer::assignTileMap(std::map<uint32_t, tson::Tile *> *tileMap)
{
	m_tileMap = tileMap;
}

/*!
 * Get tile data as some kind of map with x and y position with pointers to existing tiles.
 * Map only contains tiles that are not empty. x and y position is in tile units.
 *
 * Example of getting tile from the returned map:
 *
 * Tile *tile = tileData[{0, 4}];
 *
 * @return A map that represents the data returned from getData() in a 2D map with Tile pointers.
 */
const std::map<std::tuple<int, int>, tson::Tile *> &tson::Layer::getTileData() const
{
	return m_tileData;
}

/*!
 * A safe way to get tile data
 * Get tile data as some kind of map with x and y position with pointers to existing tiles.
 * Map only contains tiles that are not empty. x and y position is in tile units.
 *
 * Example of getting tile:
 * Tile *tile = layer->getTileData(0, 4)
 *
 * @param x X position in tile units
 * @param y Y position in tile units
 * @return pointer to tile, if it exists. nullptr otherwise.
 */
tson::Tile *tson::Layer::getTileData(int x, int y)
{
	return (m_tileData.count({x, y}) > 0) ? m_tileData[{x,y}] : nullptr;
}

/*!
 * Used for getting the tson::Map who is the parent of this Layer.
 * @return a pointer to the tson::Map where this layer is contained.
 */
tson::Map *tson::Layer::getMap() const
{
	return m_map;
}

/*!
 *
 * This is only supported for non-infinite maps!
 *
 * @param mapSize The size of the map
 * @param isInfiniteMap Whether or not the current map is infinte.
 */
void tson::Layer::createTileData(const Vector2i &mapSize, bool isInfiniteMap)
{
	size_t x = 0;
	size_t y = 0;
	if(!isInfiniteMap)
	{
		std::for_each(m_data.begin(), m_data.end(), [&](uint32_t tileId)
		{
			if (static_cast<int>(x) == mapSize.x)
			{
				++y;
				x = 0;
			}

			if (tileId > 0 && m_tileMap->count(tileId) > 0)
			{
				m_tileData[{static_cast<int>(x), static_cast<int>(y)}] = m_tileMap->at(tileId);
				m_tileObjects[{static_cast<int>(x), static_cast<int>(y)}] = {{static_cast<int>(x), static_cast<int>(y)}, m_tileData[{static_cast<int>(x), static_cast<int>(y)}]};
			}
			else if(tileId > 0 && m_tileMap->count(tileId) == 0) //Tile with flip flags!
			{
				queueFlaggedTile(x, y, tileId);
			}
			x++;
		});

	}
}

std::map<std::tuple<int, int>, tson::TileObject> &tson::Layer::getTileObjects()
{
	return m_tileObjects;
}

tson::TileObject *tson::Layer::getTileObject(int x, int y)
{
	return (m_tileObjects.count({x, y}) > 0) ? &m_tileObjects[{x,y}] : nullptr;
}

const std::set<uint32_t> &tson::Layer::getUniqueFlaggedTiles() const
{
	return m_uniqueFlaggedTiles;
}

void tson::Layer::resolveFlaggedTiles()
{
	std::for_each(m_flaggedTiles.begin(), m_flaggedTiles.end(), [&](const tson::FlaggedTile &tile)
	{
		if (tile.id > 0 && m_tileMap->count(tile.id) > 0)
		{
			m_tileData[{static_cast<int>(tile.x), static_cast<int>(tile.y)}] = m_tileMap->at(tile.id);
			m_tileObjects[{static_cast<int>(tile.x), static_cast<int>(tile.y)}] = {{static_cast<int>(tile.x), static_cast<int>(tile.y)}, m_tileData[{static_cast<int>(tile.x), static_cast<int>(tile.y)}]};
		}
	});
}

/*!
 * 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional).
 *
 * @return tintcolor
 */
const tson::Colori &tson::Layer::getTintColor() const
{
	return m_tintColor;
}

/*!
 * New in Tiled v1.5
 * Gets the parallax factor for current layer. Defaults to 1.
 * @return A vector with the x and y values of the parallax factor.
 */
const tson::Vector2f &tson::Layer::getParallax() const
{
	return m_parallax;
}

/*!
 * New in Tiled v1.8
 * 'repeatx': Whether the image drawn by this layer is repeated along the X axis.
 * @return true if image layer is repeated along the X axis, false otherwise.
 */
bool tson::Layer::hasRepeatX() const
{
	return m_repeatX;
}

/*!
 * New in Tiled v1.8
 * 'repeatx': Whether the image drawn by this layer is repeated along the Y axis.
 * @return true if image layer is repeated along the Y axis, false otherwise.
 */
bool tson::Layer::hasRepeatY() const
{
	return m_repeatY;
}

const std::string &tson::Layer::getClassType() const
{
	return m_classType;
}

#endif //TILESON_LAYER_HPP

/*** End of inlined file: Layer.hpp ***/


/*** Start of inlined file: Tileset.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_TILESET_HPP
#define TILESON_TILESET_HPP

//#include "../external/json.hpp"


/*** Start of inlined file: Transformations.hpp ***/
//
// Created by robin on 04.04.2021.
//

#ifndef TILESON_TRANSFORMATIONS_HPP
#define TILESON_TRANSFORMATIONS_HPP

namespace tson
{
	class Transformations
	{
		public:
			inline Transformations() = default;
			inline explicit Transformations(IJson &json);
			inline bool parse(IJson &json);

			inline bool allowHflip() const;
			inline bool allowPreferuntransformed() const;
			inline bool allowRotation() const;
			inline bool allowVflip() const;

		private:
			bool m_hflip {};                /*! hflip: Whether the tiles in this set can be flipped horizontally (default false) */
			bool m_preferuntransformed {};  /*! preferuntransformed: Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations (default false) */
			bool m_rotate {};               /*! rotate: Whether the tiles in this set can be rotated in 90 degree increments (default false) */
			bool m_vflip {};                /*! vflip: Whether the tiles in this set can be flipped vertically (default false) */
	};

	Transformations::Transformations(IJson &json)
	{
		parse(json);
	}

	bool Transformations::parse(IJson &json)
	{
		if(json.count("hflip") > 0) m_hflip = json["hflip"].get<bool>(); //Optional
		if(json.count("preferuntransformed") > 0) m_preferuntransformed = json["preferuntransformed"].get<bool>(); //Optional
		if(json.count("rotate") > 0) m_rotate = json["rotate"].get<bool>(); //Optional
		if(json.count("vflip") > 0) m_vflip = json["vflip"].get<bool>(); //Optional

		return true;
	}

	/*!
	 *
	 * @return hflip: Whether the tiles in this set can be flipped horizontally (default false)
	 */
	bool Transformations::allowHflip() const
	{
		return m_hflip;
	}

	/*!
	 *
	 * @return preferuntransformed: Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations (default false)
	 */
	bool Transformations::allowPreferuntransformed() const
	{
		return m_preferuntransformed;
	}

	/*!
	 *
	 * @return rotate: Whether the tiles in this set can be rotated in 90 degree increments (default false)
	 */
	bool Transformations::allowRotation() const
	{
		return m_rotate;
	}

	/*!
	 *
	 * @return vflip: Whether the tiles in this set can be flipped vertically (default false)
	 */
	bool Transformations::allowVflip() const
	{
		return m_vflip;
	}
}

#endif //TILESON_TRANSFORMATIONS_HPP

/*** End of inlined file: Transformations.hpp ***/


/*** Start of inlined file: WangSet.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_WANGSET_HPP
#define TILESON_WANGSET_HPP

//#include "../external/json.hpp"

/*** Start of inlined file: WangColor.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_WANGCOLOR_HPP
#define TILESON_WANGCOLOR_HPP

//#include "../external/json.hpp"

namespace tson
{
	class WangColor
	{
		public:
			inline WangColor() = default;
			inline explicit WangColor(IJson &json, tson::Map *map);
			inline bool parse(IJson &json, tson::Map *map);

			[[nodiscard]] inline const Colori &getColor() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline float getProbability() const;
			[[nodiscard]] inline int getTile() const;

			inline PropertyCollection &getProperties();
			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */

		private:
			tson::Colori      m_color;              /*! 'color': Hex-formatted color (#RRGGBB or #AARRGGBB) */
			std::string       m_name;               /*! 'name': Name of the Wang color */
			float             m_probability{};      /*! 'probability': Probability used when randomizing */
			int               m_tile{};             /*! 'tile': Local ID of tile representing the Wang color */

			//New in Tiled v1.5
			tson::PropertyCollection     m_properties; 	  /*! 'properties': A list of properties (name, value, type). */
			tson::Map *                  m_map;
			std::string                  m_classType {};              /*! 'class': The class of this map (since 1.9, defaults to “”). */
			std::shared_ptr<tson::TiledClass> m_class {};

	};
}

tson::WangColor::WangColor(IJson &json, tson::Map *map)
{
	parse(json, map);
}

bool tson::WangColor::parse(IJson &json, tson::Map *map)
{
	m_map = map;
	bool allFound = true;

	if(json.count("color") > 0) m_color = tson::Colori(json["color"].get<std::string>()); else allFound = false;
	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;
	if(json.count("probability") > 0) m_probability = json["probability"].get<float>(); else allFound = false;
	if(json.count("tile") > 0) m_tile = json["tile"].get<int>(); else allFound = false;
	if(json.count("class") > 0) m_classType = json["class"].get<std::string>();                     //Optional

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item); });
	}

	return allFound;
}

/*!
 * 'color': Color object created from hex-formatted string (#RRGGBB or #AARRGGBB)
 * @return
 */
const tson::Colori &tson::WangColor::getColor() const
{
	return m_color;
}

/*!
 * 'name': Name of the Wang color
 * @return
 */
const std::string &tson::WangColor::getName() const
{
	return m_name;
}

/*!
 * 'probability': Probability used when randomizing
 * @return
 */
float tson::WangColor::getProbability() const
{
	return m_probability;
}

/*!
 * 'tile': Local ID of tile representing the Wang color
 * @return
 */
int tson::WangColor::getTile() const
{
	return m_tile;
}

/*!
 * New property in Tiled v1.5 when data is contained in 'colors' of a wangset
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::WangColor::getProperties()
{
	return m_properties;
}

/*!
 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
 * @tparam T The template value
 * @param name Name of the property
 * @return The actual value, if it exists. Otherwise: The default value of the type.
 */
template<typename T>
T tson::WangColor::get(const std::string &name)
{
	return m_properties.getValue<T>(name);
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::WangColor::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);

	return nullptr;
}

const std::string &tson::WangColor::getClassType() const
{
	return m_classType;
}

#endif //TILESON_WANGCOLOR_HPP

/*** End of inlined file: WangColor.hpp ***/



/*** Start of inlined file: WangTile.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_WANGTILE_HPP
#define TILESON_WANGTILE_HPP

//#include "../external/json.hpp"

namespace tson
{
	class WangTile
	{
		public:
			inline WangTile() = default;
			inline explicit WangTile(IJson &json);
			inline bool parse(IJson &json);

			[[nodiscard]] inline bool hasDFlip() const;
			[[nodiscard]] inline bool hasHFlip() const;
			[[nodiscard]] inline uint32_t getTileid() const;
			[[nodiscard]] inline bool hasVFlip() const;

			[[nodiscard]] inline const std::vector<uint32_t> &getWangIds() const;

		private:
			bool                    m_dflip{};     /*! 'dflip': Tile is flipped diagonally */
			bool                    m_hflip{};     /*! 'hflip': Tile is flipped horizontally */
			uint32_t                m_tileid{};    /*! 'tileid': Local ID of tile */
			bool                    m_vflip{};     /*! 'vflip': Tile is flipped vertically */
			std::vector<uint32_t>   m_wangId;      /*! 'wangid': Array of Wang color indexes (uchar[8])*/
	};
}

tson::WangTile::WangTile(IJson &json)
{
	parse(json);
}

/*!
 * Parses a wang tile from Tiled json.
 * @param json A Tiled json file
 * @return true if all mandatory fields were found. False otherwise.
 */
bool tson::WangTile::parse(IJson &json)
{
	bool allFound = true;

	if(json.count("dflip") > 0) m_dflip = json["dflip"].get<bool>(); //Removed in Tiled v1.5 and is now optional
	if(json.count("hflip") > 0) m_hflip = json["hflip"].get<bool>(); //Removed in Tiled v1.5 and is now optional
	if(json.count("vflip") > 0) m_vflip = json["vflip"].get<bool>(); //Removed in Tiled v1.5 and is now optional

	if(json.count("tileid") > 0) m_tileid = json["tileid"].get<uint32_t>(); else allFound = false;
	if(json.count("wangid") > 0 && json["wangid"].isArray())
	{
		auto &wangid = json.array("wangid");
		std::for_each(wangid.begin(), wangid.end(), [&](std::unique_ptr<IJson> &item) { m_wangId.emplace_back(item->get<uint32_t>()); });
	}

	return allFound;
}

/*!
 * 'dflip': Tile is flipped diagonally
 *
 * NB! This property got removed in Tiled v1.5
 * @return
 */
bool tson::WangTile::hasDFlip() const
{
	return m_dflip;
}

/*!
 * 'hflip': Tile is flipped horizontally
 *
 * NB! This property got removed in Tiled v1.5
 * @return
 */
bool tson::WangTile::hasHFlip() const
{
	return m_hflip;
}

/*!
 * 'tileid': Local ID of tile
 * @return
 */
uint32_t tson::WangTile::getTileid() const
{
	return m_tileid;
}

/*!
 * 'vflip': Tile is flipped vertically
 *
 * NB! This property got removed in Tiled v1.5
 * @return
 */
bool tson::WangTile::hasVFlip() const
{
	return m_vflip;
}

/*!
 * 'wangid': Array of Wang color indexes (uchar[8])
 * @return
 */
const std::vector<uint32_t> &tson::WangTile::getWangIds() const
{
	return m_wangId;
}

#endif //TILESON_WANGTILE_HPP

/*** End of inlined file: WangTile.hpp ***/

namespace tson
{
	class WangSet
	{
		public:
			inline WangSet() = default;
			inline explicit WangSet(IJson &json, tson::Map *map);
			inline bool parse(IJson &json, tson::Map *map);

			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline int getTile() const;

			[[nodiscard]] inline const std::vector<tson::WangTile> &getWangTiles() const;
			[[nodiscard]] inline const std::vector<tson::WangColor> &getCornerColors() const;
			[[nodiscard]] inline const std::vector<tson::WangColor> &getEdgeColors() const;

			inline tson::WangColor * getColor(const std::string &name);
			inline const std::vector<tson::WangColor> &getColors() const;
			inline PropertyCollection &getProperties();

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */

		private:

			inline bool parseTiled15Props(IJson &json);

			std::string                  m_name;          /*! 'name': Name of the Wang set */
			int                          m_tile{};        /*! 'tile': Local ID of tile representing the Wang set */
			std::vector<tson::WangTile>  m_wangTiles;     /*! 'wangtiles': Array of Wang tiles */
			std::vector<tson::WangColor> m_cornerColors;  /*! 'cornercolors': Array of Wang colors */
			std::vector<tson::WangColor> m_edgeColors;    /*! 'edgecolors': Array of Wang colors */
			tson::PropertyCollection     m_properties; 	  /*! 'properties': A list of properties (name, value, type). */

			//Tiled v1.5
			std::vector<tson::WangColor> m_colors;        /*! 'colors': */

			tson::Map *                  m_map;
			std::string                  m_classType {};              /*! 'class': The class of this map (since 1.9, defaults to “”). */
			std::shared_ptr<tson::TiledClass> m_class {};

	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::WangSet::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

tson::WangSet::WangSet(IJson &json, tson::Map *map)
{
	parse(json, map);
}

bool tson::WangSet::parse(IJson &json, tson::Map *map)
{
	m_map = map;
	bool allFound = true;

	if(json.count("tile") > 0) m_tile = json["tile"].get<int>(); else allFound = false;
	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;

	//More advanced data
	if(json.count("wangtiles") > 0 && json["wangtiles"].isArray())
	{
		auto &wangtiles = json.array("wangtiles");
		std::for_each(wangtiles.begin(), wangtiles.end(), [&](std::unique_ptr<IJson> &item) { m_wangTiles.emplace_back(*item); });
	}
	if(json.count("cornercolors") > 0 && json["cornercolors"].isArray())
	{
		auto &cornercolors = json.array("cornercolors");
		std::for_each(cornercolors.begin(), cornercolors.end(), [&](std::unique_ptr<IJson> &item) { m_cornerColors.emplace_back(*item, m_map); });
	}
	if(json.count("edgecolors") > 0 && json["edgecolors"].isArray())
	{
		auto &edgecolors = json.array("edgecolors");
		std::for_each(edgecolors.begin(), edgecolors.end(), [&](std::unique_ptr<IJson> &item) { m_edgeColors.emplace_back(*item, m_map); });
	}
	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item); });
	}

	if(json.count("class") > 0) m_classType = json["class"].get<std::string>();                     //Optional

	if(!parseTiled15Props(json))
		allFound = false;

	return allFound;
}

/*!
 * Parsing data related to Tiled v1.5 changes
 * @param json The json containing data
 * @return Returns true if success
 */

bool tson::WangSet::parseTiled15Props(tson::IJson &json)
{
	if(json.count("colors") > 0 && json["colors"].isArray())
	{
		auto &colors = json.array("colors");
		std::for_each(colors.begin(), colors.end(), [&](std::unique_ptr<IJson> &item) { m_colors.emplace_back(*item, m_map); });
	}
	return true;
}

/*!
 * 'name': Name of the Wang set
 * @return
 */
const std::string &tson::WangSet::getName() const
{
	return m_name;
}

/*!
 * 'tile': Local ID of tile representing the Wang set
 * @return
 */
int tson::WangSet::getTile() const
{
	return m_tile;
}

/*!
 * 'wangtiles': Array of Wang tiles
 * @return
 */
const std::vector<tson::WangTile> &tson::WangSet::getWangTiles() const
{
	return m_wangTiles;
}

/*!
 * 'cornercolors': Array of Wang colors
 * @return
 */
const std::vector<tson::WangColor> &tson::WangSet::getCornerColors() const
{
	return m_cornerColors;
}

/*!
 * 'edgecolors': Array of Wang colors
 * @return
 */
const std::vector<tson::WangColor> &tson::WangSet::getEdgeColors() const
{
	return m_edgeColors;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::WangSet::getProperties()
{
	return m_properties;
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::WangSet::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);

	return nullptr;
}

/*!
 * Get Wangset colors (new in Tiled v1.5)
 * @return
 */
const std::vector<tson::WangColor> &tson::WangSet::getColors() const
{
	return m_colors;
}

/*!
 * NB! Will only work with maps created/modified by Tiled v1.5 or later!
 * Gets a color from a wangset by its name.
 *
 * @param name 'name' of WangColor
 * @return The WangColor with the given name or nullptr if it doesn't exist.
 */
tson::WangColor *tson::WangSet::getColor(const std::string &name)
{
	auto color = std::find_if(m_colors.begin(), m_colors.end(), [&](const auto &c) { return c.getName() == name; });

	if(color != m_colors.end())
		return &color.operator*();

	return nullptr;
}

const std::string &tson::WangSet::getClassType() const
{
	return m_classType;
}

#endif //TILESON_WANGSET_HPP

/*** End of inlined file: WangSet.hpp ***/


/*** Start of inlined file: Tile.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_TILE_HPP
#define TILESON_TILE_HPP

//#include "../external/json.hpp"


/*** Start of inlined file: Frame.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_FRAME_HPP
#define TILESON_FRAME_HPP

//#include "../external/json.hpp"

namespace tson
{
	class Frame
	{
		public:
			inline Frame() = default;
			inline Frame(int duration, uint32_t tileId);
			inline explicit Frame(IJson &json);

			inline bool parse(IJson &json);

			[[nodiscard]] inline int getDuration() const;
			[[nodiscard]] inline uint32_t getTileId() const;

		private:
			int m_duration {};  /*! 'duration': Frame duration in milliseconds */
			uint32_t m_tileId {};    /*! 'tileid': Local tile ID representing this frame */
	};
}

/*!
 *
 * @param duration duration in milliseconds
 * @param tileId TileId
 */
tson::Frame::Frame(int duration, uint32_t tileId) : m_duration {duration}, m_tileId {tileId}
{

}

/*!
 * Parses frame data from json
 * @param json
 */
tson::Frame::Frame(IJson &json)
{
	parse(json);
}

/*!
 * Parses frame data from json
 * @param json
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Frame::parse(IJson &json)
{
	bool allFound = true;

	if(json.count("duration") > 0) m_duration = json["duration"].get<int>(); else allFound = false;
	if(json.count("tileid") > 0) m_tileId = json["tileid"].get<uint32_t>() + 1; else allFound = false;

	return allFound;
}

/*!
 * 'duration': Frame duration in milliseconds
 * @return Duration in milliseconds
 */
int tson::Frame::getDuration() const
{
	return m_duration;
}

/*!
 * 'tileid': Local tile ID representing this frame
 * @return tile id
 */
uint32_t tson::Frame::getTileId() const
{
	return m_tileId;
}

#endif //TILESON_FRAME_HPP

/*** End of inlined file: Frame.hpp ***/


/*** Start of inlined file: Animation.hpp ***/
//
// Created by robin on 21.05.2021.
//

#ifndef TILESON_ANIMATION_HPP
#define TILESON_ANIMATION_HPP

namespace tson
{
	class Animation
	{
		public:
			inline Animation() = default;
			inline Animation(const std::vector<tson::Frame> &frames) : m_frames {frames} {};

			inline void update(float timeDeltaMs);
			inline void reset();

			inline void setFrames(const std::vector<tson::Frame> &frames);
			inline void setCurrentFrame(uint32_t currentFrame);
			inline void setTimeDelta(float timeDelta);

			inline const std::vector<tson::Frame> &getFrames() const;
			inline const tson::Frame *getCurrentFrame() const;
			inline uint32_t getCurrentFrameNumber() const;
			inline uint32_t getCurrentTileId() const;
			inline float getTimeDelta() const;

			inline bool any() const;
			inline size_t size() const;

		private:
			inline int nextFrame();
			std::vector<tson::Frame> m_frames;
			uint32_t m_currentFrame {0};
			float m_timeDelta {0};
	};

	const std::vector<tson::Frame> &Animation::getFrames() const
	{
		return m_frames;
	}

	/*!
	 * Resets the current frame and time delta to 0.
	 */
	void Animation::reset()
	{
		m_currentFrame = 0;
		m_timeDelta = 0.f;
	}

	/*!
	 * Gets the current frame or nullptr if no frame is found.
	 * @return
	 */
	const tson::Frame *Animation::getCurrentFrame() const
	{
		return (m_frames.size() == 0 || m_currentFrame >= m_frames.size()) ? nullptr : &m_frames.at(m_currentFrame);
	}

	size_t Animation::size() const
	{
		return m_frames.size();
	}

	/*!
	 * Update animation based on the fra
	 * @param timedeltaMs Time in milliseconds
	 */
	void Animation::update(float timeDeltaMs)
	{
		const tson::Frame *frame = getCurrentFrame();
		if(frame != nullptr)
		{
			m_timeDelta += timeDeltaMs;
			if(m_timeDelta >= frame->getDuration())
			{
				m_timeDelta = static_cast<float>((int32_t)m_timeDelta % frame->getDuration());
				m_currentFrame = nextFrame();
			}
		}
	}

	int Animation::nextFrame()
	{
		return (m_currentFrame+1 >= m_frames.size()) ? 0 : m_currentFrame + 1;
	}

	float Animation::getTimeDelta() const
	{
		return m_timeDelta;
	}

	uint32_t Animation::getCurrentFrameNumber() const
	{
		return m_currentFrame;
	}

	uint32_t Animation::getCurrentTileId() const
	{
		return (getCurrentFrame() != nullptr) ? getCurrentFrame()->getTileId() : 0;
	}

	void Animation::setFrames(const std::vector<tson::Frame> &frames)
	{
		m_frames = frames;
	}

	void Animation::setCurrentFrame(uint32_t currentFrame)
	{
		m_currentFrame = currentFrame;
	}

	void Animation::setTimeDelta(float timeDelta)
	{
		m_timeDelta = timeDelta;
	}

	/*!
	 * True if any frames exists, false otherwise
	 * @return
	 */
	bool Animation::any() const
	{
		return m_frames.size() > 0;
	}
}

#endif //TILESON_ANIMATION_HPP

/*** End of inlined file: Animation.hpp ***/

namespace tson
{
	class Tileset;
	class TiledClass;

	class Tile
	{
		public:
			inline Tile() = default;
			inline Tile(IJson &json, tson::Tileset *tileset, tson::Map *map);
			inline Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map);
			inline Tile(uint32_t id, tson::Map *map); //v1.2.0
			inline bool parse(IJson &json, tson::Tileset *tileset, tson::Map *map);
			inline bool parseId(IJson &json);

			[[nodiscard]] inline uint32_t getId() const;
			[[nodiscard]] inline const fs::path &getImage() const;
			[[nodiscard]] inline const Vector2i &getImageSize() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */

			//[[nodiscard]] inline const std::vector<tson::Frame> &getAnimation() const;
			[[nodiscard]] inline tson::Animation &getAnimation();
			[[nodiscard]] inline Layer &getObjectgroup();
			[[nodiscard]] inline PropertyCollection &getProperties();
			[[nodiscard]] inline const std::vector<int> &getTerrain() const;

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			//v1.2.0-stuff
			inline void setProperties(const tson::PropertyCollection &properties);

			[[nodiscard]] inline tson::Tileset * getTileset() const;
			[[nodiscard]] inline tson::Map * getMap() const;
			[[nodiscard]] inline const tson::Rect &getDrawingRect() const;
			[[nodiscard]] inline const Rect &getSubRectangle() const;

			inline const tson::Vector2f getPosition(const std::tuple<int, int> &tileDataPos);
			inline const tson::Vector2i getPositionInTileUnits(const std::tuple<int, int> &tileDataPos);
			[[nodiscard]] inline const tson::Vector2i getTileSize() const;                       /*! Declared in tileson_forward.hpp */

			[[nodiscard]] inline TileFlipFlags getFlipFlags() const;
			inline bool hasFlipFlags(TileFlipFlags flags);
			[[nodiscard]] inline uint32_t getGid() const;

			inline void addTilesetAndPerformCalculations(tson::Tileset *tileset); //v1.2.0

		private:
			tson::Animation                  m_animation{};      /*! 'animation': Array of Frames */
			uint32_t                         m_id {};            /*! 'id': Local ID of the tile */

			fs::path                         m_image;            /*! 'image': Image representing this tile (optional)*/

			tson::Vector2i                   m_imageSize;        /*! x = 'imagewidth' and y = 'imageheight': in pixels */
			tson::Layer                      m_objectgroup; 	 	/*! 'objectgroup': Layer with type objectgroup (optional) */
			tson::PropertyCollection         m_properties; 	    /*! 'properties': A list of properties (name, value, type). */
			std::vector<int>                 m_terrain;          /*! 'terrain': Index of terrain for each corner of tile */
			std::string                      m_type;             /*! 'type': The type of the tile (optional) */

			//v1.2.0-stuff
			uint32_t                    m_gid {};                                    /*! id without flip flags */
			tson::Tileset *             m_tileset;                                   /*! A pointer to the tileset where this Tile comes from */
			tson::Map *                 m_map;                                       /*! A pointer to the map where this tile is contained */
			tson::Rect                  m_drawingRect;                               /*! A rect that shows which part of the tileset that is used for this tile */
			tson::Rect                  m_subRect;                                   /*! Tiled 1.9: Contains the newly added sub-rectangle variables: 'x', 'y', 'width' and 'height'*/
			tson::TileFlipFlags         m_flipFlags = TileFlipFlags::None;           /*! Resolved using bit 32, 31 and 30 from gid */
			inline void performDataCalculations();                                   /*! Declared in tileson_forward.hpp - Calculate all the values used in the tile class. */
			inline void manageFlipFlagsByIdThenRemoveFlags(uint32_t &id);
			friend class Layer;
			std::shared_ptr<tson::TiledClass> m_class {};
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::Tile::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

tson::Tile::Tile(IJson &json, tson::Tileset *tileset, tson::Map *map)
{
	parse(json, tileset, map);
}

/*!
 * Used in cases where you have a tile without any property
 * @param id
 */
tson::Tile::Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map) : m_id {id}, m_gid {id}
{
	m_tileset = tileset;
	m_map = map;
	manageFlipFlagsByIdThenRemoveFlags(m_gid);
	performDataCalculations();
}

/*!
 * Used in cases where you have a FLIP FLAGGED tile
 * @param id
 */
tson::Tile::Tile(uint32_t id, tson::Map *map) : m_id {id}, m_gid {id}
{
	m_map = map;
	manageFlipFlagsByIdThenRemoveFlags(m_gid);
}

/*!
 * For flip flagged tiles, tilesets must be resolved later.
 * @param tileset
 */
void tson::Tile::addTilesetAndPerformCalculations(tson::Tileset *tileset)
{
	m_tileset = tileset;
	performDataCalculations();
}

/*!
 * Parses a tile from a Tiled json. id on tile is store as id + 1 to match the references in data containers.
 * @param json
 * @return
 */
bool tson::Tile::parse(IJson &json, tson::Tileset *tileset, tson::Map *map)
{
	m_tileset = tileset;
	m_map = map;

	if(json.count("image") > 0) m_image = fs::path(json["image"].get<std::string>()); //Optional

	bool allFound = parseId(json);

	if(json.count("type") > 0) m_type = json["type"].get<std::string>(); //Optional
	else if(json.count("class") > 0) m_type = json["class"].get<std::string>(); //Tiled v1.9 renamed 'type' to 'class'

	if(json.count("objectgroup") > 0) m_objectgroup = tson::Layer(json["objectgroup"], m_map); //Optional

	if(json.count("imagewidth") > 0 && json.count("imageheight") > 0)
		m_imageSize = {json["imagewidth"].get<int>(), json["imageheight"].get<int>()}; //Optional

	m_subRect = {0,0, m_imageSize.x, m_imageSize.y};
	if(json.count("x") > 0) m_subRect.x = json["x"].get<int>(); //Optional
	if(json.count("y") > 0) m_subRect.y = json["y"].get<int>(); //Optional
	if(json.count("width") > 0) m_subRect.width = json["width"].get<int>(); //Optional
	if(json.count("height") > 0) m_subRect.height = json["height"].get<int>(); //Optional

	//More advanced data
	if(json.count("animation") > 0 && json["animation"].isArray())
	{
		auto &animation = json.array("animation");
		std::vector<tson::Frame> frames;
		std::for_each(animation.begin(), animation.end(), [&](std::unique_ptr<IJson> &item) { frames.emplace_back(*item); });
		if(frames.size() > 0)
		{
			m_animation.setFrames(frames);
		}
	}
	if(json.count("terrain") > 0 && json["terrain"].isArray())
	{
		auto &terrain = json.array("terrain");
		std::for_each(terrain.begin(), terrain.end(), [&](std::unique_ptr<IJson> &item) { m_terrain.emplace_back(item->get<int>()); });
	}

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item); });
	}

	performDataCalculations();

	return allFound;
}

/*!
 * 'id': Local ID of the tile
 * @return
 */
uint32_t tson::Tile::getId() const
{
	return m_id;
}

/*!
 * 'image': Image representing this tile (optional)
 * @return
 */

const fs::path &tson::Tile::getImage() const { return m_image; }

/*!
 * x = 'imagewidth' and y = 'imageheight': in pixels
 * @return
 */
const tson::Vector2i &tson::Tile::getImageSize() const
{
	return m_imageSize;
}

/*!
 * 'type': The type of the tile (optional)
 * This was renamed to 'class' in Tiled v1.9
 * @return
 */
const std::string &tson::Tile::getType() const
{
	return m_type;
}

/*!
  * 'class': String assigned to class field in editor (optional)
 * This was renamed from 'type' to 'class' in Tiled v1.9
 * @return
 */
const std::string &tson::Tile::getClassType() const
{
	return m_type;
}

/*!
 * 'animation': Array of Frames
 * @return
 */
tson::Animation &tson::Tile::getAnimation()
{
	return m_animation;
}

/*!
 * 'objectgroup': Layer with type objectgroup (optional)
 * @return
 */
tson::Layer &tson::Tile::getObjectgroup()
{
	return m_objectgroup;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::Tile::getProperties()
{
	return m_properties;
}

/*!
 * 'terrain': Index of terrain for each corner of tile
 * @return
 */
const std::vector<int> &tson::Tile::getTerrain() const
{
	return m_terrain;
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Tile::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);

	return nullptr;
}

/*!
 * Used for getting the tson::Tileset who is the parent of this Tile.
 * @return a pointer to the tson::Tileset where this tile is contained.
 */
tson::Tileset *tson::Tile::getTileset() const
{
	return m_tileset;
}

/*!
 * Used for getting the tson::Map who is the parent of this Tile.
 * @return a pointer to the tson::Map where this tile is contained.
 */
tson::Map *tson::Tile::getMap() const
{
	return m_map;
}

/*!
 * Get the information needed to draw the Tile based on its current tileset
 * @return a tson::Rect containing the information needed to draw the tile.
 */
const tson::Rect &tson::Tile::getDrawingRect() const
{
	return m_drawingRect;
}

/*!
 * Helper function.
 *
 * Get the position of the tile in tile units.
 * The size of each unit is determined by the tile size property of the map.
 * Example: If the tile size is 16x16 in the map, a tile unit of [2, 4] would be [32, 64] in pixels.
 * If you want the position in pixels: use getPosition() instead.
 *
 * @return Position of tile in tile units.
 */
const tson::Vector2i tson::Tile::getPositionInTileUnits(const std::tuple<int, int> &tileDataPos)
{
	return {std::get<0>(tileDataPos), std::get<1>(tileDataPos)};
}

void tson::Tile::manageFlipFlagsByIdThenRemoveFlags(uint32_t &id)
{
	if (id & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally;
	if (id & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically;
	if (id & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally;

	id &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG);
}

tson::TileFlipFlags tson::Tile::getFlipFlags() const
{
	return m_flipFlags;
}

/*!
 *
 * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator.
 * Example:
 * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally)
 *
 * @return true if the flag(s) specified are set
 */
bool tson::Tile::hasFlipFlags(tson::TileFlipFlags flags)
{
	return ((m_flipFlags & flags) == flags) ? true : false;
}

uint32_t tson::Tile::getGid() const
{
	return m_gid;
}

void tson::Tile::setProperties(const tson::PropertyCollection &properties)
{
	m_properties = properties;
}

/*!
 * Tiled 1.9: Contains the newly added sub-rectangle variables: 'x', 'y', 'width' and 'height'
 * @return A tson::Rect with the 'x', 'y', 'width' and 'height' values
 */
const tson::Rect &tson::Tile::getSubRectangle() const
{
	return m_subRect;
}

#endif //TILESON_TILE_HPP

/*** End of inlined file: Tile.hpp ***/


/*** Start of inlined file: Terrain.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_TERRAIN_HPP
#define TILESON_TERRAIN_HPP

//#include "../external/json.hpp"

namespace tson
{
	class Terrain
	{
		public:
			inline Terrain() = default;
			inline Terrain(std::string name, int tile);
			inline explicit Terrain(IJson &json);

			inline bool parse(IJson &json);

			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline int getTile() const;
			[[nodiscard]] inline PropertyCollection &getProperties();

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

		private:
			std::string                 m_name;        /*! 'name': Name of terrain */
			int                         m_tile {};     /*! 'tile': Local ID of tile representing terrain */
			tson::PropertyCollection    m_properties;  /*! 'properties': A list of properties (name, value, type). */
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::Terrain::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

tson::Terrain::Terrain(std::string name, int tile) : m_name {std::move(name)}, m_tile {tile}
{

}

tson::Terrain::Terrain(IJson &json)
{
	parse(json);
}

bool tson::Terrain::parse(IJson &json)
{
	bool allFound = true;

	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;
	if(json.count("tile") > 0) m_tile = json["tile"].get<int>(); else allFound = false;

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item); });
	}

	return allFound;
}

/*!
 * 'name': Name of terrain
 * @return
 */
const std::string &tson::Terrain::getName() const
{
	return m_name;
}

/*!
 * 'tile': Local ID of tile representing terrain
 * @return
 */
int tson::Terrain::getTile() const
{
	return m_tile;
}

/*!
 * 'properties': A list of properties (name, value, type). *Missing from the official Tiled documentation...*
 * @return
 */
tson::PropertyCollection &tson::Terrain::getProperties()
{
	return m_properties;
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Terrain::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);
	return nullptr;
}

#endif //TILESON_TERRAIN_HPP

/*** End of inlined file: Terrain.hpp ***/


/*** Start of inlined file: Grid.hpp ***/
//
// Created by robin on 22.03.2020.
//

#ifndef TILESON_GRID_HPP
#define TILESON_GRID_HPP

#include <string>
//#include "../external/json.hpp"

namespace tson
{
	class Grid
	{
		public:
			inline Grid() = default;
			inline explicit Grid(IJson &json);

			inline bool parse(IJson &json);

			[[nodiscard]] inline const std::string &getOrientation() const;
			[[nodiscard]] inline const Vector2i &getSize() const;

		private:
			std::string m_orientation; /*! 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric) */
			tson::Vector2i m_size; /*! 'width' and 'height': Size. */
	};
}

/*!
 * Parses Tiled grid data from json
 * @param json
 */
tson::Grid::Grid(IJson &json)
{
	parse(json);
}

/*!
 * Parses Tiled grid data from json
 * @param json
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Grid::parse(IJson &json)
{
	bool allFound = true;

	if(json.count("orientation") > 0) m_orientation = json["orientation"].get<std::string>(); //Optional

	if(json.count("width") > 0 && json.count("height") > 0)
		m_size = {json["width"].get<int>(), json["height"].get<int>()}; else allFound = false;

	return allFound;
}

/*!
 * 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric)
 * @return orientation as string
 */
const std::string &tson::Grid::getOrientation() const
{
	return m_orientation;
}

/*!
 * 'width' and 'height': Size.
 * @return size as int
 */
const tson::Vector2i &tson::Grid::getSize() const
{
	return m_size;
}

#endif //TILESON_GRID_HPP

/*** End of inlined file: Grid.hpp ***/

#include <functional>

namespace tson
{
	class Map;
	class Tileset
	{
		public:
			inline Tileset() = default;
			inline explicit Tileset(IJson &json, tson::Map *map);
			inline bool parse(IJson &json, tson::Map *map);

			[[nodiscard]] inline int getColumns() const;
			[[nodiscard]] inline int getFirstgid() const;

			[[nodiscard]] inline const fs::path &getImagePath() const;
			[[nodiscard]] inline const fs::path &getImage() const;
			[[nodiscard]] inline const Vector2i &getImageSize() const;
			[[nodiscard]] inline int getMargin() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline int getSpacing() const;
			[[nodiscard]] inline int getTileCount() const;
			[[nodiscard]] inline const Vector2i &getTileSize() const;
			[[nodiscard]] inline const Colori &getTransparentColor() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */
			[[nodiscard]] inline std::vector<tson::Tile> &getTiles();
			[[nodiscard]] inline const std::vector<tson::WangSet> &getWangsets() const;
			[[nodiscard]] inline PropertyCollection &getProperties();
			[[nodiscard]] inline const std::vector<tson::Terrain> &getTerrains() const;
			[[nodiscard]] inline const Vector2i &getTileOffset() const;
			[[nodiscard]] inline const Grid &getGrid() const;
			[[nodiscard]] inline TileRenderSize getTileRenderSize() const;
			[[nodiscard]] inline FillMode getFillMode() const;

			inline tson::Tile * getTile(uint32_t id);
			inline tson::Terrain * getTerrain(const std::string &name);

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			//v1.2.0-stuff
			[[nodiscard]] inline tson::Map *getMap() const;
			[[nodiscard]] inline ObjectAlignment getObjectAlignment() const;

			inline static tson::ObjectAlignment StringToAlignment(std::string_view str);

			//v1.3.0
			inline tson::Vector2i getMarginSpacingOffset(const tson::Vector2i &posInTileUnits);
			inline tson::WangSet * getWangset(const std::string &name);
			inline const Transformations &getTransformations() const;

			#ifndef TSON_TEST_ENABLED
		private:
			#endif
			inline void generateMissingTiles();

			int                           m_columns {};       /*! 'columns': The number of tile columns in the tileset */
			int                           m_firstgid {};      /*! 'firstgid': GID corresponding to the first tile in the set */

			fs::path                      m_image;            /*! 'image': Image used for tiles in this set */

			tson::Vector2i                m_imageSize;        /*! x = 'imagewidth' and y = 'imageheight': in pixels */
			int                           m_margin {};        /*! 'margin': Buffer between image edge and first tile (pixels)*/
			std::string                   m_name;             /*! 'name': Name given to this tileset */
			int                           m_spacing {};       /*! 'spacing': Spacing between adjacent tiles in image (pixels)*/
			int                           m_tileCount {};     /*! 'tilecount': The number of tiles in this tileset */
			tson::Vector2i                m_tileSize;         /*! x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set */
			tson::Colori                  m_transparentColor; /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional) */
			std::string                   m_type;             /*! 'type': tileset (for tileset files, since 1.0) */

			std::vector<tson::Tile>       m_tiles;            /*! 'tiles': Array of Tiles (optional) */
			std::vector<tson::WangSet>    m_wangsets;         /*! 'wangsets':Array of Wang sets (since 1.1.5) */
			tson::PropertyCollection      m_properties; 	  /*! 'properties': A list of properties (name, value, type). */

			std::vector<tson::Terrain>    m_terrains;         /*! 'terrains': Array of Terrains (optional) */
			tson::Vector2i                m_tileOffset;       /*! 'x' and 'y': See <tileoffset> (optional) */
			tson::Grid                    m_grid;             /*! 'grid': This element is only used in case of isometric orientation, and determines
																   how tile overlays for terrain and collision information are rendered. */

			//v1.2.0-stuff
			tson::ObjectAlignment         m_objectAlignment{tson::ObjectAlignment::Unspecified};  /*! 'objectalignment': Alignment to use for tile objects. Tiled 1.4.*/
			tson::Map *                   m_map;              /*! The map who owns this tileset */

			//v1.3.0-stuff
			fs::path                      m_source {};           /*! 'source': exists only when tileset is contained in an external file*/
			fs::path                      m_path {};             /*! Has the full path to the tileset if 'source' has an existing value */
			Transformations               m_transformations {};  /*! New in Tiled v1.5 - This element is used to describe which transformations can be applied to
																	 the tiles (e.g. to extend a Wang set by transforming existing tiles).*/

			//v1.4.0-stuff
			TileRenderSize                m_tileRenderSize {};   /*! 'tilerendersize': The size to use when rendering tiles from this tileset on a tile layer. Valid values are 'tile' (the default) and 'grid'.
 *                                                                    When set to 'grid', the tile is drawn at the tile grid size of the map. (since 1.9)*/
			FillMode                      m_fillMode {};         /*! 'fillmode': The fill mode to use when rendering tiles from this tileset. Valid values are 'stretch' (the default) and 'preserve-aspect-fit'.
 *                                                                    Only relevant when the tiles are not rendered at their native size, so this applies to resized tile objects or in combination with 'tilerendersize' set to 'grid'. (since 1.9)*/

			std::string                   m_classType {};              /*! 'class': The class of this map (since 1.9, defaults to “”). */
			std::shared_ptr<tson::TiledClass> m_class {};
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::Tileset::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

tson::Tileset::Tileset(IJson &json, tson::Map *map)
{
	parse(json, map);
}

bool tson::Tileset::parse(IJson &json, tson::Map *map)
{
	m_map = map;
	bool allFound = true;

	if(json.count("firstgid") > 0) m_firstgid = json["firstgid"].get<int>(); else allFound = false;

	//Tileset is stored in external file if 'source' exists
	if(json.count("source") > 0)
	{
		if(!allFound)
			return allFound;

		std::string sourceStr = json["source"].get<std::string>();
		m_source = fs::path(sourceStr);
		m_path = json.directory() / m_source;

		if(!json.parse(m_path))
			return false;
	}

	if(json.count("columns") > 0) m_columns = json["columns"].get<int>(); else allFound = false;

	if(json.count("image") > 0) m_image = fs::path(json["image"].get<std::string>()); else allFound = false;

	if(json.count("margin") > 0) m_margin = json["margin"].get<int>(); else allFound = false;
	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;
	if(json.count("spacing") > 0) m_spacing = json["spacing"].get<int>(); else allFound = false;
	if(json.count("tilecount") > 0) m_tileCount = json["tilecount"].get<int>(); else allFound = false;
	if(json.count("transparentcolor") > 0) m_transparentColor = tson::Colori(json["transparentcolor"].get<std::string>()); //Optional
	if(json.count("type") > 0) m_type = json["type"].get<std::string>();
	if(json.count("grid") > 0) m_grid = tson::Grid(json["grid"]);
	if(json.count("class") > 0) m_classType = json["class"].get<std::string>();                     //Optional

	if(json.count("imagewidth") > 0 && json.count("imageheight") > 0)
		m_imageSize = {json["imagewidth"].get<int>(), json["imageheight"].get<int>()}; else allFound = false;
	if(json.count("tilewidth") > 0 && json.count("tileheight") > 0)
		m_tileSize = {json["tilewidth"].get<int>(), json["tileheight"].get<int>()}; else allFound = false;
	if(json.count("tileoffset") > 0)
		m_tileOffset = {json["tileoffset"]["x"].get<int>(), json["tileoffset"]["y"].get<int>()};

	if(json.count("tilerendersize") > 0)
	{
		std::string tileRenderStr = json["tilerendersize"].get<std::string>();
		if(tileRenderStr == "tile") m_tileRenderSize = TileRenderSize::Tile;
		else if(tileRenderStr == "grid") m_tileRenderSize = TileRenderSize::Grid;
	}

	if(json.count("fillmode") > 0)
	{
		std::string fillmode = json["fillmode"].get<std::string>();
		if(fillmode == "stretch") m_fillMode = FillMode::Stretch;
		else if(fillmode == "preserve-aspect-fit") m_fillMode = FillMode::PreserveAspectFit;
	}

	//More advanced data
	if(json.count("wangsets") > 0 && json["wangsets"].isArray())
	{
		auto &wangsets = json.array("wangsets");
		std::for_each(wangsets.begin(), wangsets.end(), [&](std::unique_ptr<IJson> &item) { m_wangsets.emplace_back(*item, m_map); });
	}
	if(json.count("tiles") > 0 && json["tiles"].isArray())
	{
		auto &tiles = json.array("tiles");
		std::for_each(tiles.begin(), tiles.end(), [&](std::unique_ptr<IJson> &item) { m_tiles.emplace_back(*item, this, m_map); });
	}
	if(json.count("terrains") > 0 && json["terrains"].isArray())
	{
		auto &terrains = json.array("terrains");
		std::for_each(terrains.begin(), terrains.end(), [&](std::unique_ptr<IJson> &item) { m_terrains.emplace_back(*item); });
	}

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item); });
	}

	if(json.count("objectalignment") > 0)
	{
		std::string alignment = json["objectalignment"].get<std::string>();
		m_objectAlignment = StringToAlignment(alignment);
	}

	if(json.count("transformations") > 0)
	{
		m_transformations.parse(json["transformations"]);
	}

	generateMissingTiles();

	return allFound;
}

/*!
 * 'columns': The number of tile columns in the tileset
 * @return
 */
int tson::Tileset::getColumns() const
{
	return m_columns;
}

/*!
 * 'firstgid': GID corresponding to the first tile in the set
 * @return
 */
int tson::Tileset::getFirstgid() const
{
	return m_firstgid;
}

/*!
 * 'image': Image used for tiles in this set
 * @return
 */

const fs::path &tson::Tileset::getImagePath() const { return m_image; }

/*!
 * x = 'imagewidth' and y = 'imageheight': in pixels
 * @return
 */
const tson::Vector2i &tson::Tileset::getImageSize() const
{
	return m_imageSize;
}

/*!
 * 'margin': Buffer between image edge and first tile (pixels)
 * @return
 */
int tson::Tileset::getMargin() const
{
	return m_margin;
}

/*!
 * 'name': Name given to this tileset
 * @return
 */
const std::string &tson::Tileset::getName() const
{
	return m_name;
}

/*!
 * 'spacing': Spacing between adjacent tiles in image (pixels)
 * @return
 */
int tson::Tileset::getSpacing() const
{
	return m_spacing;
}

/*!
 * 'tilecount': The number of tiles in this tileset
 * @return
 */
int tson::Tileset::getTileCount() const
{
	return m_tileCount;
}

/*!
 * x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set
 * @return
 */
const tson::Vector2i &tson::Tileset::getTileSize() const
{
	return m_tileSize;
}

/*!
 * 'transparentcolor': Color object created by hex-formatted color (#RRGGBB) (optional)
 * @return
 */
const tson::Colori &tson::Tileset::getTransparentColor() const
{
	return m_transparentColor;
}

/*!
 * 'type': tileset (for tileset files, since 1.0)
 * @return
 */
const std::string &tson::Tileset::getType() const
{
	return m_type;
}

/*!
 * 'image': Image used for tiles in this set
 * @return
 */

const fs::path &tson::Tileset::getImage() const { return m_image; }

/*!
 * 'tiles': Array of Tiles (optional)
 * @return
 */
std::vector<tson::Tile> &tson::Tileset::getTiles()
{
	return m_tiles;
}

/*!
 * 'wangsets':Array of Wang sets (since Tiled 1.1.5)
 * @return
 */
const std::vector<tson::WangSet> &tson::Tileset::getWangsets() const
{
	return m_wangsets;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::Tileset::getProperties()
{
	return m_properties;
}

/*!
 * 'terrains': Array of Terrains (optional)
 * @return
 */
const std::vector<tson::Terrain> &tson::Tileset::getTerrains() const
{
	return m_terrains;
}

/*!
 * 'x' and 'y': See <tileoffset> (optional)
 * @return
 */
const tson::Vector2i &tson::Tileset::getTileOffset() const
{
	return m_tileOffset;
}

/*!
 * 'grid': This element is only used in case of isometric orientation, and determines
 * how tile overlays for terrain and collision information are rendered.
 * @return
 */
const tson::Grid &tson::Tileset::getGrid() const
{
	return m_grid;
}

/*!
 * Gets a tile by ID (Tiled ID + 1)
 * @param id The ID of the tile stored in Tiled map + 1. Example: If ID was stored in Tiled map as 0, the corresponding value in Tileson is 1.
 * This is to make sure the IDs of tiles matches their references in containers.
 * @return A pointer to the Tile if found. nullptr otherwise.
 */
tson::Tile *tson::Tileset::getTile(uint32_t id)
{
	auto result = std::find_if(m_tiles.begin(), m_tiles.end(), [&](const tson::Tile & item) { return item.getId() == id;});
	if(result == m_tiles.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Get an existing Terrain object by name
 * @param name
 * @return A pointer to the Terrain if found. nullptr otherwise.
 */
tson::Terrain *tson::Tileset::getTerrain(const std::string &name)
{
	auto result = std::find_if(m_terrains.begin(), m_terrains.end(), [&](const tson::Terrain & item) { return item.getName() == name;});
	if(result == m_terrains.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Tileset::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);

	return nullptr;
}

/*!
 * Tiled only has tiles with a property stored in the map. This function makes sure even the ones with no properties will exist.
 */
void tson::Tileset::generateMissingTiles()
{
	std::vector<uint32_t> tileIds;
	for(auto &tile : m_tiles)
		tileIds.push_back(tile.getId());

	for(uint32_t i = m_firstgid; i < m_firstgid + (uint32_t) m_tileCount; ++i)
	{
		if(std::count(tileIds.begin(), tileIds.end(), i) == 0)
		{
			m_tiles.emplace_back(Tile(i, this, m_map));
		}
	}
}

/*!
 * Used for getting the tson::Map who is the parent of this Tileset.
 * @return a pointer to the tson::Map where this tileset is contained.
 */
tson::Map *tson::Tileset::getMap() const
{
	return m_map;
}

/*!
 *
 * @param str The string you want to convert
 * @return Alignment enum based on the string from the input.
 */
tson::ObjectAlignment tson::Tileset::StringToAlignment(std::string_view str)
{
	if(str == "unspecified") return tson::ObjectAlignment::Unspecified;
	else if(str == "topleft") return tson::ObjectAlignment::TopLeft;
	else if(str == "top") return tson::ObjectAlignment::Top;
	else if(str == "topright") return tson::ObjectAlignment::TopRight;
	else if(str == "left") return tson::ObjectAlignment::Left;
	else if(str == "center") return tson::ObjectAlignment::Center;
	else if(str == "right") return tson::ObjectAlignment::Right;
	else if(str == "bottomleft") return tson::ObjectAlignment::BottomLeft;
	else if(str == "bottom") return tson::ObjectAlignment::Bottom;
	else if(str == "bottomright") return tson::ObjectAlignment::BottomRight;
	else
		return tson::ObjectAlignment::Unspecified;
}

tson::ObjectAlignment tson::Tileset::getObjectAlignment() const
{
	return m_objectAlignment;
}

/*!
 * Helper function to calculate the correct additional offset when margin and/or spacing is used in a tileset
 * Created to solve issue #31.
 *
 * @param posInTileUnits Position of the current tile in tile units.
 * @return Calculated additional offset in pixels.
 */
tson::Vector2i tson::Tileset::getMarginSpacingOffset(const tson::Vector2i &posInTileUnits)
{
	if(m_margin == 0 && m_spacing == 0)
		return {0,0};

	tson::Vector2i offset {(posInTileUnits.x * m_spacing) + m_margin, (posInTileUnits.y * m_spacing) + m_margin};
	return offset;
}

/*!
 * Get a wangset by name
 * @param name
 * @return
 */
tson::WangSet *tson::Tileset::getWangset(const std::string &name)
{
	auto wangset = std::find_if(m_wangsets.begin(), m_wangsets.end(), [&](const auto &w) { return w.getName() == name; });

	if(wangset != m_wangsets.end())
		return &wangset.operator*();

	return nullptr;
}

/*!
 * New in Tiled v1.5 - This element is used to describe which transformations can be applied to
 * the tiles (e.g. to extend a Wang set by transforming existing tiles).
 *
 * @return
 */
const tson::Transformations &tson::Tileset::getTransformations() const
{
	return m_transformations;
}

tson::TileRenderSize tson::Tileset::getTileRenderSize() const
{
	return m_tileRenderSize;
}

tson::FillMode tson::Tileset::getFillMode() const
{
	return m_fillMode;
}

const std::string &tson::Tileset::getClassType() const
{
	return m_classType;
}

#endif //TILESON_TILESET_HPP
/*** End of inlined file: Tileset.hpp ***/

namespace tson
{
	class Map
	{
		public:
			inline Map() = default;
			inline Map(ParseStatus status, std::string description);
			inline explicit Map(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project);
			inline bool parse(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project);

			[[nodiscard]] inline const Colori &getBackgroundColor() const;
			[[nodiscard]] inline const Vector2i &getSize() const;
			[[nodiscard]] inline int getHexsideLength() const;
			[[nodiscard]] inline bool isInfinite() const;
			[[nodiscard]] inline int getNextLayerId() const;
			[[nodiscard]] inline int getNextObjectId() const;
			[[nodiscard]] inline const std::string &getOrientation() const;
			[[nodiscard]] inline const std::string &getRenderOrder() const;
			[[nodiscard]] inline const std::string &getStaggerAxis() const;
			[[nodiscard]] inline const std::string &getStaggerIndex() const;
			[[nodiscard]] inline const std::string &getTiledVersion() const;
			[[nodiscard]] inline const Vector2i &getTileSize() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const std::string &getClassType() const;
			[[nodiscard]] inline tson::TiledClass *getClass(); /*! Declared in tileson_forward.hpp */
			[[nodiscard]] inline const Vector2f &getParallaxOrigin() const;
			//[[nodiscard]] inline int getVersion() const; //Removed - Tileson v1.3.0

			[[nodiscard]] inline std::vector<tson::Layer> &getLayers();
			[[nodiscard]] inline PropertyCollection &getProperties();
			[[nodiscard]] inline std::vector<tson::Tileset> &getTilesets();

			[[nodiscard]] inline ParseStatus getStatus() const;
			[[nodiscard]] inline const std::string &getStatusMessage() const;
			[[nodiscard]] inline const std::map<uint32_t, tson::Tile *> &getTileMap() const;

			inline Layer * getLayer(const std::string &name);
			inline Tileset * getTileset(const std::string &name);

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property * getProp(const std::string &name);

			//v1.2.0
			[[nodiscard]] inline int getCompressionLevel() const;
			inline DecompressorContainer *getDecompressors();
			inline Project * getProject();
			inline Tileset * getTilesetByGid(uint32_t gid);

		private:
			inline bool createTilesetData(IJson &json);
			inline void processData();

			Colori                                 m_backgroundColor;   /*! 'backgroundcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) (optional)*/;
			Vector2i                               m_size;              /*! 'width' and 'height' of a Tiled map */
			int                                    m_hexsideLength {};  /*! 'hexsidelength': Length of the side of a hex tile in pixels */
			bool                                   m_isInfinite {};     /*! 'infinite': Whether the map has infinite dimensions*/
			std::vector<tson::Layer>               m_layers; 	        /*! 'layers': Array of layers. group on */
			int                                    m_nextLayerId {};    /*! 'nextlayerid': Auto-increments for each layer */
			int                                    m_nextObjectId {};   /*! 'nextobjectid': Auto-increments for each placed object */
			std::string                            m_orientation;       /*! 'orientation': orthogonal, isometric, staggered or hexagonal */
			tson::PropertyCollection               m_properties; 	    /*! 'properties': A list of properties (name, value, type). */
			std::string                            m_renderOrder;       /*! 'renderorder': Rendering direction (orthogonal maps only) */
			std::string                            m_staggerAxis;       /*! 'staggeraxis': x or y (staggered / hexagonal maps only) */
			std::string                            m_staggerIndex;      /*! 'staggerindex': odd or even (staggered / hexagonal maps only) */
			std::string                            m_tiledVersion;      /*! 'tiledversion': The Tiled version used to save the file */
			Vector2i                               m_tileSize;          /*! 'tilewidth': and 'tileheight' of a map */
			std::vector<tson::Tileset>             m_tilesets;          /*! 'tilesets': Array of Tilesets */
			std::string                            m_type;              /*! 'type': map (since 1.0) */
			tson::Vector2f                         m_parallaxOrigin;    /*! Tiled v1.8: parallax origin in pixels. Defaults to 0. */
			//int                                    m_version{};       /*! 'version': The JSON format version - Removed in Tileson v1.3.0*/

			ParseStatus                            m_status {ParseStatus::OK};
			std::string                            m_statusMessage {"OK"};

			std::map<uint32_t, tson::Tile*>        m_tileMap{};           /*! key: Tile ID. Value: Pointer to Tile*/

			//v1.2.0
			int                                    m_compressionLevel {-1};  /*! 'compressionlevel': The compression level to use for tile layer
																			  *     data (defaults to -1, which means to use the algorithm default)
																			  *     Introduced in Tiled 1.3*/
			tson::DecompressorContainer *          m_decompressors {nullptr};
			tson::Project *                        m_project {nullptr};
			std::map<uint32_t, tson::Tile>         m_flaggedTileMap{};    /*! key: Tile ID. Value: Tile*/

			std::string                            m_classType{};              /*! 'class': The class of this map (since 1.9, defaults to “”). */
			std::shared_ptr<tson::TiledClass>      m_class {};
	};

	/*!
	 * A shortcut for getting a property. Alternative to getProperties().getValue<T>("<name>")
	 * @tparam T The template value
	 * @param name Name of the property
	 * @return The actual value, if it exists. Otherwise: The default value of the type.
	 */
	template<typename T>
	T tson::Map::get(const std::string &name)
	{
		return m_properties.getValue<T>(name);
	}
}

/*!
 * When errors have happened before the map starts parsing, just keep the statuses
 * @param status The status
 * @param description Description of the status
 */
tson::Map::Map(tson::ParseStatus status, std::string description) : m_status {status}, m_statusMessage { std::move(description) }
{

}

/*!
 * Parses a json of a Tiled map.
 * @param json A json object with the format of Map
 * @return true if all mandatory fields was found. false otherwise.
 */
tson::Map::Map(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project)
{
	parse(json, decompressors, project);
}

/*!
 * Parses a json of a Tiled map.
 * @param json A json object with the format of Map
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Map::parse(IJson &json, tson::DecompressorContainer *decompressors, tson::Project *project)
{
	m_decompressors = decompressors;
	m_project = project;

	bool allFound = true;
	if(json.count("compressionlevel") > 0)
		m_compressionLevel = json["compressionlevel"].get<int>(); //Tiled 1.3 - Optional

	if(json.count("backgroundcolor") > 0) m_backgroundColor = Colori(json["backgroundcolor"].get<std::string>()); //Optional
	if(json.count("width") > 0 && json.count("height") > 0 )
		m_size = {json["width"].get<int>(), json["height"].get<int>()}; else allFound = false;
	if(json.count("hexsidelength") > 0) m_hexsideLength = json["hexsidelength"].get<int>();         //Optional
	if(json.count("infinite") > 0) m_isInfinite = json["infinite"].get<bool>();                     //Optional
	if(json.count("nextlayerid") > 0) m_nextLayerId = json["nextlayerid"].get<int>();               //Optional
	if(json.count("nextobjectid") > 0) m_nextObjectId = json["nextobjectid"].get<int>(); else allFound = false;
	if(json.count("orientation") > 0) m_orientation = json["orientation"].get<std::string>(); else allFound = false;
	if(json.count("renderorder") > 0) m_renderOrder = json["renderorder"].get<std::string>();       //Optional
	if(json.count("staggeraxis") > 0) m_staggerAxis = json["staggeraxis"].get<std::string>();       //Optional
	if(json.count("staggerindex") > 0) m_staggerIndex = json["staggerindex"].get<std::string>();    //Optional
	if(json.count("tiledversion") > 0) m_tiledVersion = json["tiledversion"].get<std::string>(); else allFound = false;
	if(json.count("tilewidth") > 0 && json.count("tileheight") > 0 )
		m_tileSize = {json["tilewidth"].get<int>(), json["tileheight"].get<int>()}; else allFound = false;
	if(json.count("type") > 0) m_type = json["type"].get<std::string>();                            //Optional
	if(json.count("class") > 0) m_classType = json["class"].get<std::string>();                     //Optional

	//Removed - Changed from a float to string in Tiled v1.6, and old spec said int.
	//Reason for removal is that it seems to have no real use, as TiledVersion is stored in another variable.
	//if(json.count("version") > 0) m_version = json["version"].get<int>(); else allFound = false;

	//More advanced data
	if(json.count("layers") > 0 && json["layers"].isArray())
	{
		auto &array = json.array("layers");
		std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item)
		{
			m_layers.emplace_back(*item, this);
		});
	}

	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &array = json.array("properties");
		std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item)
		{
			m_properties.add(*item, m_project);
		});
	}

	tson::Vector2f parallaxOrigin {0.f, 0.f};
	if(json.count("parallaxoriginx") > 0)
		parallaxOrigin.x = json["parallaxoriginx"].get<float>();
	if(json.count("parallaxoriginy") > 0)
		parallaxOrigin.y = json["parallaxoriginy"].get<float>();

	m_parallaxOrigin = parallaxOrigin;

	if(!createTilesetData(json))
		allFound = false;

	processData();

	return allFound;
}

/*!
 * Tileset data must be created in two steps to prevent malformed tson::Tileset pointers inside tson::Tile
 */
bool tson::Map::createTilesetData(IJson &json)
{
	bool ok = true;
	if(json.count("tilesets") > 0 && json["tilesets"].isArray())
	{
		//First created tileset objects
		auto &tilesets = json.array("tilesets");
		std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr<IJson> &)
		{
			m_tilesets.emplace_back();
		});

		int i = 0;
		//Then do the parsing
		std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr<IJson> &item)
		{
			item->directory(json.directory());
			if(!m_tilesets[i].parse(*item, this))
				ok = false;

			++i;
		});
	}
	return ok;
}

/*!
 * Processes the parsed data and uses the data to create helpful objects, like tile maps.
 */
void tson::Map::processData()
{
	m_tileMap.clear();
	for(auto &tileset : m_tilesets)
	{
		  std::set<std::uint32_t> usedIds;
		  for(auto& tile : tileset.getTiles())
		  {
			  if (usedIds.count(tile.getGid()) != 0)
			  {
				  continue;
			  }
			  usedIds.insert(tile.getGid());
			  m_tileMap[tile.getGid()] = &tile;
		  }
	}
	std::for_each(m_layers.begin(), m_layers.end(), [&](tson::Layer &layer)
	{
		layer.assignTileMap(&m_tileMap);
		layer.createTileData(m_size, m_isInfinite);
		const std::set<uint32_t> &flaggedTiles = layer.getUniqueFlaggedTiles();
		for(uint32_t ftile : flaggedTiles)
		{
			tson::Tile tile {ftile, layer.getMap()};
			if(m_tileMap.count(tile.getGid()))
			{
				tson::Tile *originalTile = m_tileMap[tile.getGid()];
				tile.addTilesetAndPerformCalculations(originalTile->getTileset());
				tile.setProperties(originalTile->getProperties());
				m_flaggedTileMap[ftile] = tile;
				m_tileMap[ftile] = &m_flaggedTileMap[ftile];
			}
		}
		layer.resolveFlaggedTiles();
	});
}

/*!
 * 'backgroundcolor': Color created from a hex-formatted color string (#RRGGBB or #AARRGGBB) (optional)
 * @return string as color
 */
const tson::Colori &tson::Map::getBackgroundColor() const
{
	return m_backgroundColor;
}

/*!
 * 'width' and 'height' of a Tiled map
 * @return
 */
const tson::Vector2<int> &tson::Map::getSize() const
{
	return m_size;
}

/*!
 * 'hexsidelength': Length of the side of a hex tile in pixels
 * @return
 */
int tson::Map::getHexsideLength() const
{
	return m_hexsideLength;
}

/*!
 * 'infinite': Whether the map has infinite dimensions
 * @return
 */
bool tson::Map::isInfinite() const
{
	return m_isInfinite;
}

/*!
 * 'nextlayerid': Auto-increments for each layer
 * @return
 */
int tson::Map::getNextLayerId() const
{
	return m_nextLayerId;
}

/*!
 * 'nextobjectid': Auto-increments for each placed object
 * @return
 */
int tson::Map::getNextObjectId() const
{
	return m_nextObjectId;
}

/*!
 * 'orientation': orthogonal, isometric, staggered or hexagonal
 * @return
 */
const std::string &tson::Map::getOrientation() const
{
	return m_orientation;
}

/*!
 * 'renderorder': Rendering direction (orthogonal maps only)
 * @return
 */
const std::string &tson::Map::getRenderOrder() const
{
	return m_renderOrder;
}

/*!
 * 'staggeraxis': x or y (staggered / hexagonal maps only)
 * @return
 */
const std::string &tson::Map::getStaggerAxis() const
{
	return m_staggerAxis;
}

/*!
 * 'staggerindex': odd or even (staggered / hexagonal maps only)
 * @return
 */
const std::string &tson::Map::getStaggerIndex() const
{
	return m_staggerIndex;
}

/*!
 * 'tiledversion': The Tiled version used to save the file
 * @return
 */
const std::string &tson::Map::getTiledVersion() const
{
	return m_tiledVersion;
}

/*!
 * 'tilewidth': and 'tileheight' of a map
 * @return
 */
const tson::Vector2<int> &tson::Map::getTileSize() const
{
	return m_tileSize;
}

/*!
 * 'type': map (since 1.0)
 * @return
 */
const std::string &tson::Map::getType() const
{
	return m_type;
}

/*!
 * 'version': The JSON format version
 * @return
 */
//int tson::Map::getVersion() const
//{
//    return m_version;
//}

/*!
 * 'layers': Array of layers. group on
 * @return
 */
std::vector<tson::Layer> &tson::Map::getLayers()
{
	return m_layers;
}

/*!
 * 'properties': A list of properties (name, value, type).
 * @return
 */
tson::PropertyCollection &tson::Map::getProperties()
{
	return m_properties;
}

/*!
 * 'tilesets': Array of Tilesets
 * @return
 */
std::vector<tson::Tileset> &tson::Map::getTilesets()
{
	return m_tilesets;
}

tson::Layer *tson::Map::getLayer(const std::string &name)
{
	auto result = std::find_if(m_layers.begin(), m_layers.end(), [&](const tson::Layer &item) { return item.getName() == name; });
	if(result == m_layers.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Gets a tileset by name
 *
 * @param name Name of the tileset
 * @return tileset with the matching name
 */
tson::Tileset *tson::Map::getTileset(const std::string &name)
{
	auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &item) {return item.getName() == name; });
	if(result == m_tilesets.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Gets a tileset by gid (graphical ID of a tile). These are always unique, no matter how many tilesets you have
 *
 * @param gid Graphical ID of a tile
 * @return tileset related to the actual gid
 */
tson::Tileset *tson::Map::getTilesetByGid(uint32_t gid)
{
	auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &tileset)
	{
		auto const firstId = static_cast<uint32_t>(tileset.getFirstgid()); //First tile id of the tileset
		auto const lastId =  static_cast<uint32_t>((firstId + tileset.getTileCount()) - 1);

		return (gid >= firstId && gid <= lastId);
	});
	if(result == m_tilesets.end())
		return nullptr;

	return &result.operator*();
}

/*!
 * Shortcut for getting a property object. Alternative to getProperties().getProperty("<name>");
 * @param name Name of the property
 * @return
 */
tson::Property *tson::Map::getProp(const std::string &name)
{
	if(m_properties.hasProperty(name))
		return m_properties.getProperty(name);
	return nullptr;
}

tson::ParseStatus tson::Map::getStatus() const
{
	return m_status;
}

const std::string &tson::Map::getStatusMessage() const
{
	return m_statusMessage;
}

/*!
 * Get a tile map with pointers to every existing tile.
 * @return
 */
const std::map<uint32_t, tson::Tile *> &tson::Map::getTileMap() const
{
	return m_tileMap;
}

tson::DecompressorContainer *tson::Map::getDecompressors()
{
	return m_decompressors;
}

/*!
 * 'compressionlevel': The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default)
 *
 * @return The compression level
 */
int tson::Map::getCompressionLevel() const
{
	return m_compressionLevel;
}

/*!
 * New in Tiled v1.8
 * Gets the parallax origin in pixels. Defaults to 0.
 * @return A vector with the x and y values of the parallax origin.
 */
const tson::Vector2f &tson::Map::getParallaxOrigin() const
{
	return m_parallaxOrigin;
}

tson::Project *tson::Map::getProject()
{
	return m_project;
}

const std::string &tson::Map::getClassType() const
{
	return m_classType;
}

#endif //TILESON_MAP_HPP

/*** End of inlined file: Map.hpp ***/


/*** Start of inlined file: TiledEnum.hpp ***/
//
// Created by robin on 06.06.22.
//

#ifndef TILESON_TILEDENUM_HPP
#define TILESON_TILEDENUM_HPP

namespace tson
{
	class EnumDefinition
	{
		public:
			inline explicit EnumDefinition(IJson &json);
			inline uint32_t getValue(const std::string &str);
			inline std::string getValue(uint32_t num);
			inline std::vector<std::string> getValues(uint32_t num);
			inline bool exists(const std::string &str);
			inline bool exists(uint32_t num);

			[[nodiscard]] inline uint32_t getId() const;
			[[nodiscard]] inline uint32_t getMaxValue() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline EnumStorageType getStorageType() const;
			[[nodiscard]] inline bool hasValuesAsFlags() const;

		private:
			inline bool hasFlag(uint32_t value, uint32_t flag) const;
			uint32_t m_id {};
			uint32_t m_maxValue {};
			std::string m_name {};
			std::map<uint32_t, std::string> m_values {};
			bool m_valuesAsFlags {false};
			EnumStorageType m_storageType { EnumStorageType::Unspecified };
	};

	EnumDefinition::EnumDefinition(IJson &json)
	{
		m_id = json.get<uint32_t>("id");
		m_name = json.get<std::string>("name");
		std::string type = json.get<std::string>("storageType");
		m_storageType = (type == "int") ? EnumStorageType::Int : (type == "string") ? EnumStorageType::String : EnumStorageType::Unspecified;
		m_valuesAsFlags = json.get<bool>("valuesAsFlags");

		if(json.count("values") > 0 && json["values"].isArray())
		{
			m_values[0] = "None";
			uint32_t valueCounter = (m_valuesAsFlags) ? 1 : 0;
			uint8_t flagBit = 1;
			auto &array = json.array("values");
			std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item)
			{
				std::string v = item->get<std::string>();
				m_values[valueCounter] = v;
				if(m_valuesAsFlags)
				{
					valueCounter = 1 << flagBit;
					++flagBit;
				}
				else
				{
					++valueCounter;
				}
			});

			m_maxValue = valueCounter;
		}
	}

	uint32_t EnumDefinition::getValue(const std::string &str)
	{
		auto result = std::find_if(m_values.begin(), m_values.end(), [&](const std::pair<uint32_t, std::string> &pair)
		{
			return pair.second == str;
		});

		if(result != m_values.end())
			return result->first;

		return 0;
	}

	std::string EnumDefinition::getValue(uint32_t num)
	{
		return (m_values.count(num) > 0) ? m_values[num] : "";
	}

	bool EnumDefinition::hasValuesAsFlags() const
	{
		return m_valuesAsFlags;
	}

	bool EnumDefinition::exists(const std::string &str)
	{
		auto result = std::find_if(m_values.begin(), m_values.end(), [&](const std::pair<uint32_t, std::string> &pair)
		{
			return pair.second == str;
		});

		if(result != m_values.end())
			return true;

		return false;
	}

	bool EnumDefinition::exists(uint32_t num) { return (m_values.count(num) > 0); }

	uint32_t EnumDefinition::getId() const
	{
		return m_id;
	}

	const std::string &EnumDefinition::getName() const
	{
		return m_name;
	}

	EnumStorageType EnumDefinition::getStorageType() const
	{
		return m_storageType;
	}

	uint32_t EnumDefinition::getMaxValue() const
	{
		return m_maxValue;
	}

	std::vector<std::string> EnumDefinition::getValues(uint32_t num)
	{
		std::vector<std::string> values;
		if(m_valuesAsFlags)
		{
			uint32_t flag = 0;
			uint32_t i = 0;
			while(flag < m_maxValue)
			{
				flag = 1 << i;
				++i;
				if(m_values.count(flag) > 0 && hasFlag(num, flag))
				{
					values.emplace_back(m_values[flag]);
				}
			}
		}
		else
		{
			std::string v = getValue(num);
			if(!v.empty())
				values.emplace_back();
		}

		return values;
	}

	bool EnumDefinition::hasFlag(uint32_t value, uint32_t flag) const
	{
		return ((value & flag) == flag);
	}

	class EnumValue
	{
		public:
			inline EnumValue() = default;
			inline EnumValue(uint32_t value, EnumDefinition *definition);
			inline EnumValue(const std::string &value, EnumDefinition *definition);

			[[nodiscard]] inline uint32_t getValue() const;
			inline std::string getValueName() const;
			[[nodiscard]] inline std::vector<std::string> getValueNames() const;
			[[nodiscard]] inline EnumDefinition *getDefinition() const;

			inline bool hasFlagValue(uint32_t flag) const;
			template <typename T>
			inline bool hasFlag(T flags) const;
			inline bool hasAnyFlagValue(uint32_t flags) const;
			template <typename T>
			inline bool hasAnyFlag(T flags) const;
			[[nodiscard]] inline bool containsValueName(const std::string &value) const;

		private:
			uint32_t m_value {0};
			EnumDefinition *m_definition = nullptr;
	};

	EnumValue::EnumValue(uint32_t value, EnumDefinition *definition) : m_value {value}, m_definition {definition}
	{

	}

	EnumValue::EnumValue(const std::string &value, EnumDefinition *definition) : m_definition {definition}
	{
		if(!value.empty() && definition != nullptr)
		{
			std::vector<std::string> values = Tools::SplitString(value, ',');
			for(auto &item : values)
			{
				uint32_t v = definition->getValue(item);
				m_value |= v;
			}
		}
	}

	/*!
	 * Checks if uint32 value contains a single flag. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done
	 * instead.
	 * @param flags The uint32 value of the flag you want to check
	 * @return 'true' if EnumValue has the requested bit activated. 'false' otherwise.
	 */
	bool EnumValue::hasFlagValue(uint32_t flags) const
	{
		if(m_definition->hasValuesAsFlags())
			return ((m_value & flags) == flags) ? true : false;

		return m_value == flags;
	}

	/*!
	 * Checks if uint32 value contains one of several possible flags. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done
	 * instead.
	 * @param flags The uint32 values of the flags you want to check
	 * @return 'true' if EnumValue has the requested bits activated. 'false' otherwise.
	 */
	bool EnumValue::hasAnyFlagValue(uint32_t flags) const
	{
		if(m_definition->hasValuesAsFlags())
			return ((m_value & flags) != 0);

		return m_value == flags;
	}

	uint32_t EnumValue::getValue() const
	{
		return m_value;
	}

	/*!
	 * Gets the single name of a value.
	 * This function is intended for enums that can only have one value (non-flag).
	 * If you want to get several value names (flags), use getValueNames() instead.
	 * @return A single name for a value
	 */
	std::string EnumValue::getValueName() const
	{
		return (m_definition == nullptr) ? "" : m_definition->getValue(m_value);
	}

	/*!
	 * Checks if T value contains a single flag. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done
	 * instead.
	 * @tparam T A uint32_t compatible type
	 * @param flags One or more flags you want to verify is included.
	 * @return true is all flags presented are set. false otherwise.
	 */
	template<typename T>
	bool EnumValue::hasFlag(T flags) const
	{
		return hasFlagValue(static_cast<uint32_t>(flags));
	}

	/*!
	 * Checks if T value contains one of several possible flags. If 'valuesAsFlags' is not a part of the EnumDefinition, a simple equality comparison will be done
	 * instead.
	 * @tparam T A uint32_t compatible type
	 * @param flags One or more flags you want to verify is included.
	 * @return true is all flags presented are set. false otherwise.
	 */
	template<typename T>
	bool EnumValue::hasAnyFlag(T flags) const
	{
		return hasAnyFlagValue(static_cast<uint32_t>(flags));
	}

	EnumDefinition *EnumValue::getDefinition() const
	{
		return m_definition;
	}

	std::vector<std::string> EnumValue::getValueNames() const
	{
		return (m_definition == nullptr) ? std::vector<std::string>() : m_definition->getValues(m_value);
	}

	/*!
	 *
	 * @param value
	 * @return
	 */
	bool EnumValue::containsValueName(const std::string &value) const
	{
		if(m_definition != nullptr)
		{
			if(m_definition->hasValuesAsFlags())
			{
				std::vector<std::string> values = m_definition->getValues(m_value);
				auto it = std::find(values.begin(), values.end(), value);
				return it != values.end();
			}
			return m_definition->getValue(value) == m_value;
		}
		return false;
	}
}

#endif //TILESON_TILEDENUM_HPP

/*** End of inlined file: TiledEnum.hpp ***/


/*** Start of inlined file: TiledClass.hpp ***/
//
// Created by robin on 06.06.22.
//

#ifndef TILESON_TILEDCLASS_HPP
#define TILESON_TILEDCLASS_HPP

namespace tson
{
	class TiledClass
	{
		public:
			inline explicit TiledClass() = default;
			inline explicit TiledClass(IJson &json, tson::Project *project = nullptr);

			[[nodiscard]] inline uint32_t getId() const;
			[[nodiscard]] inline const std::string &getName() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const PropertyCollection &getMembers() const;
			inline void update(IJson &json);
			inline void update(PropertyCollection &properties);

			template <typename T>
			inline T get(const std::string &name);
			inline tson::Property *getMember(const std::string &name);

		private:
			uint32_t m_id {};
			std::string m_name {};
			std::string m_type {};
			PropertyCollection m_members {};

	};

	TiledClass::TiledClass(IJson &json, tson::Project *project)
	{
		if(json.count("id") > 0)
			m_id = json["id"].get<uint32_t>();

		if(json.count("name") > 0)
			m_name = json["name"].get<std::string>();
		if(json.count("type") > 0)
			m_type = json["type"].get<std::string>();

		if(json.count("members") > 0 && json["members"].isArray())
		{
			auto &array = json.array("members");
			std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item)
			{
				m_members.add(*item, project);
			});
		}
	}

	uint32_t TiledClass::getId() const
	{
		return m_id;
	}

	const std::string &TiledClass::getName() const
	{
		return m_name;
	}

	const std::string &TiledClass::getType() const
	{
		return m_type;
	}

	const PropertyCollection &TiledClass::getMembers() const
	{
		return m_members;
	}

	template<typename T>
	T TiledClass::get(const std::string &name)
	{
		return m_members.getValue<T>(name);
	}

	tson::Property *TiledClass::getMember(const std::string &name)
	{
		if(m_members.hasProperty(name))
			return m_members.getProperty(name);
		return nullptr;
	}

	/*!
	 * Takes a json object from a particular map top update values if they differ from the original values of the class
	 * @param json
	 */
	void TiledClass::update(IJson &json)
	{
		for(Property *property : m_members.get())
		{
			if(json.any(property->getName()))
			{
				property->setValueByType(json[property->getName()]);
			}
		}
	}

	void TiledClass::update(PropertyCollection &properties)
	{
		std::vector<Property *> toUpdate;
		for(Property *member : m_members.get())
		{
			if(properties.hasProperty(member->getName()))
			{
				Property *property = properties.getProperty(member->getName());
				if(member->getType() == property->getType())
				{
					toUpdate.push_back(property);
				}
			}
		}

		std::for_each(toUpdate.begin(), toUpdate.end(), [&](Property *p)
		{
		   m_members.setProperty(p->getName(), *p);
		});
	}
}

#endif //TILESON_TILEDCLASS_HPP

/*** End of inlined file: TiledClass.hpp ***/


/*** Start of inlined file: Project.hpp ***/
//
// Created by robin on 01.08.2020.
//

#ifndef TILESON_PROJECT_HPP
#define TILESON_PROJECT_HPP

#include <fstream>
#include <sstream>
#include <memory>

/*** Start of inlined file: World.hpp ***/
//
// Created by robin on 01.08.2020.
//

#ifndef TILESON_WORLD_HPP
#define TILESON_WORLD_HPP


/*** Start of inlined file: WorldMapData.hpp ***/
//
// Created by robin on 01.08.2020.
//

#ifndef TILESON_WORLDMAPDATA_HPP
#define TILESON_WORLDMAPDATA_HPP

namespace tson
{
	class WorldMapData
	{
		public:
			inline WorldMapData(const fs::path &folder_, IJson &json);
			inline void parse(const fs::path &folder_, IJson &json);
			//inline WorldMapData(fs::path folder_, std::string fileName_) : folder {std::move(folder_)}, fileName {fileName_}
			//{
			//    path = folder / fileName;
			//}

			fs::path folder;
			fs::path path;
			std::string fileName;
			tson::Vector2i size;
			tson::Vector2i position;
	};

	WorldMapData::WorldMapData(const fs::path &folder_, IJson &json)
	{
		parse(folder_, json);
	}

	void WorldMapData::parse(const fs::path &folder_, IJson &json)
	{
		folder = folder_;
		if(json.count("fileName") > 0) fileName = json["fileName"].get<std::string>();
		if(json.count("height") > 0) size = {json["width"].get<int>(), json["height"].get<int>()};
		if(json.count("x") > 0) position = {json["x"].get<int>(), json["y"].get<int>()};

		path = (!fileName.empty()) ? folder / fileName : folder;
	}
}

#endif //TILESON_WORLDMAPDATA_HPP
/*** End of inlined file: WorldMapData.hpp ***/

#include <memory>
namespace tson
{
	class Tileson;
	class World
	{
		public:
			#ifdef JSON11_IS_DEFINED
			inline explicit World(std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>()) : m_json {std::move(jsonParser)}
			{
			}

			inline explicit World(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>());
			#else
			inline explicit World(std::unique_ptr<tson::IJson> jsonParser) : m_json {std::move(jsonParser)}
			{
			}

			inline explicit World(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser);
			#endif
			inline bool parse(const fs::path &path);
			inline std::size_t loadMaps(tson::Tileson *parser); //tileson_forward.hpp
			inline bool contains(std::string_view filename);
			inline const WorldMapData *get(std::string_view filename) const;

			[[nodiscard]] inline const fs::path &getPath() const;
			[[nodiscard]] inline const fs::path &getFolder() const;
			[[nodiscard]] inline const std::vector<WorldMapData> &getMapData() const;
			[[nodiscard]] inline bool onlyShowAdjacentMaps() const;
			[[nodiscard]] inline const std::string &getType() const;
			[[nodiscard]] inline const std::vector<std::unique_ptr<tson::Map>> &getMaps() const;

		private:
			inline void parseJson(IJson &json);

			std::unique_ptr<IJson> m_json = nullptr;
			fs::path m_path;
			fs::path m_folder;
			std::vector<WorldMapData> m_mapData;
			std::vector<std::unique_ptr<tson::Map>> m_maps;
			bool m_onlyShowAdjacentMaps;
			std::string m_type;
	};

	World::World(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser) : m_json {std::move(jsonParser)}
	{
		parse(path);
	}

	bool World::parse(const fs::path &path)
	{
		m_path = path;
		m_folder = m_path.parent_path();

		if(!m_json->parse(path))
			return false;

		parseJson(*m_json);
		return true;
	}

	const fs::path &World::getPath() const
	{
		return m_path;
	}

	const std::vector<WorldMapData> &World::getMapData() const
	{
		return m_mapData;
	}

	bool World::onlyShowAdjacentMaps() const
	{
		return m_onlyShowAdjacentMaps;
	}

	const std::string &World::getType() const
	{
		return m_type;
	}

	void World::parseJson(IJson &json)
	{
		if(json.count("onlyShowAdjacentMaps") > 0) m_onlyShowAdjacentMaps = json["onlyShowAdjacentMaps"].get<bool>();
		if(json.count("type") > 0) m_type = json["type"].get<std::string>();

		if(json["maps"].isArray())
		{
			auto &maps = json.array("maps");
			std::for_each(maps.begin(), maps.end(), [&](std::unique_ptr<IJson> &item) { m_mapData.emplace_back(m_folder, *item); });
		}
	}

	const fs::path &World::getFolder() const
	{
		return m_folder;
	}

	/*!
	 * Check if there is WorldMapData in the world that contains the current filename.
	 * Filename = <file>.<extension>
	 * @param filename
	 * @return
	 */
	bool World::contains(std::string_view filename)
	{
		//Note: might be moved to std::ranges from C++20.
		return std::any_of(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; });
	}

	/*!
	 * Get a map by its filename
	 * @param filename Filename (including extension) - (example: file.json)
	 * @return pointer to WorldMapData or nullptr if not exists
	 */
	const WorldMapData * World::get(std::string_view filename) const
	{
		auto iter = std::find_if(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; });
		return (iter == m_mapData.end()) ? nullptr : iter.operator->();
	}

	/*!
	 * Get all maps that have been loaded by loadMaps().
	 * NOTE: This is untested, and was a last second addition to Tileson 1.2.0, as I had forgot about the loadMaps() functionality (also untested)
	 * If you find anything malfunctioning - please report.
	 * @return All maps loaded by loadMaps()
	 */
	const std::vector<std::unique_ptr<tson::Map>> &World::getMaps() const
	{
		return m_maps;
	}

}

#endif //TILESON_WORLD_HPP

/*** End of inlined file: World.hpp ***/



/*** Start of inlined file: ProjectPropertyTypes.hpp ***/
//
// Created by robin on 01.08.22.
//

#ifndef TILESON_PROJECTPROPERTYTYPES_HPP
#define TILESON_PROJECTPROPERTYTYPES_HPP

namespace tson
{
	class ProjectPropertyTypes
	{
		public:
			inline ProjectPropertyTypes() = default;
			inline bool parse(IJson &json, tson::Project *project);

			inline const std::vector<tson::EnumDefinition> &getEnums() const;
			inline const std::vector<tson::TiledClass> &getClasses() const;
			[[nodiscard]] inline tson::EnumDefinition* getEnumDefinition(std::string_view name);
			[[nodiscard]] inline tson::TiledClass* getClass(std::string_view name);
			inline bool isUnhandledContentFound() const;

		private:
			std::vector<tson::EnumDefinition> m_enums;
			std::vector<tson::TiledClass> m_classes;
			bool m_unhandledContentFound {false};

	};

	bool ProjectPropertyTypes::parse(IJson &json, tson::Project *project)
	{
		m_enums.clear();
		m_classes.clear();
		m_unhandledContentFound = false;

		if(json.count("propertyTypes") > 0 && json["propertyTypes"].isArray())
		{
			auto &array = json.array("propertyTypes");
			std::vector<tson::IJson*> classes; //Classes must be handled after enums
			std::vector<tson::IJson*> other; //Unhandled stuff - just to keep track if something is missing...
			std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item)
			{
				IJson &j = *item;
				if(j.count("type") > 0)
				{
					std::string t = j["type"].get<std::string>();
					if(t == "enum")
					{
						m_enums.emplace_back(j); //Can be resolved directly
					}
					else if(t == "class")
					{
						classes.push_back(item.get()); //Must be resolved later
					}
					else
						other.push_back(item.get()); //Only used to set flag for whether unhandled content was found.
				}
			});

			std::for_each(classes.begin(), classes.end(), [&](IJson *item)
			{
				m_classes.emplace_back(*item, project);
			});

			if(!other.empty())
				m_unhandledContentFound = true;

		}
		return false;
	}

	const std::vector<tson::EnumDefinition> &ProjectPropertyTypes::getEnums() const
	{
		return m_enums;
	}

	const std::vector<tson::TiledClass> &ProjectPropertyTypes::getClasses() const
	{
		return m_classes;
	}

	bool ProjectPropertyTypes::isUnhandledContentFound() const
	{
		return m_unhandledContentFound;
	}

	tson::EnumDefinition *ProjectPropertyTypes::getEnumDefinition(std::string_view name)
	{
		auto it = std::find_if(m_enums.begin(), m_enums.end(), [&](const EnumDefinition &def)
		{
			return def.getName() == name;
		});

		if(it != m_enums.end())
			return &it.operator*();

		return nullptr;
	}

	tson::TiledClass *ProjectPropertyTypes::getClass(std::string_view name)
	{
		auto it = std::find_if(m_classes.begin(), m_classes.end(), [&](const TiledClass &def)
		{
			return def.getName() == name;
		});

		if(it != m_classes.end())
			return &it.operator*();

		return nullptr;
	}
}

#endif //TILESON_PROJECTPROPERTYTYPES_HPP

/*** End of inlined file: ProjectPropertyTypes.hpp ***/


/*** Start of inlined file: ProjectFolder.hpp ***/
//
// Created by robin on 01.08.2020.
//

#ifndef TILESON_PROJECTFOLDER_HPP
#define TILESON_PROJECTFOLDER_HPP

namespace tson
{
	class ProjectFolder
	{
		public:
			inline ProjectFolder(const fs::path &path);

			inline const fs::path &getPath() const;
			inline bool hasWorldFile() const;
			inline const std::vector<ProjectFolder> &getSubFolders() const;
			inline const std::vector<fs::path> &getFiles() const;
			inline const World &getWorld() const;

		private:
			inline void loadData();
			fs::path                    m_path;
			bool                        m_hasWorldFile;
			tson::World                 m_world;
			std::vector<ProjectFolder>  m_subFolders;
			std::vector<fs::path>       m_files;

	};

	ProjectFolder::ProjectFolder(const fs::path &path) : m_path {path}
	{
		loadData();
	}

	void ProjectFolder::loadData()
	{
		m_hasWorldFile = false;
		m_subFolders.clear();
		m_files.clear();
		//Search and see if there is a World file .world file
		fs::path worldPath;
		for (const auto & entry : fs::directory_iterator(m_path))
		{
			if(fs::is_regular_file(entry.path()))
			{
				if(entry.path().extension() == ".world")
				{
					m_hasWorldFile = true;
					worldPath = entry.path();
				}
			}
		}

		if(m_hasWorldFile)
			m_world.parse(worldPath);

		for (const auto & entry : fs::directory_iterator(m_path))
		{
			if (fs::is_directory(entry.path()))
				m_subFolders.emplace_back(entry.path());//.loadData(); - loadData() is called in the constructor, so don't call again.
			else if (fs::is_regular_file(entry.path()))
			{
				if(m_hasWorldFile && m_world.contains(entry.path().filename().generic_string()))
					m_files.emplace_back(entry.path());
				else if(!m_hasWorldFile)
					m_files.emplace_back(entry.path());
			}
		}

	}

	const fs::path &ProjectFolder::getPath() const
	{
		return m_path;
	}

	bool ProjectFolder::hasWorldFile() const
	{
		return m_hasWorldFile;
	}

	const std::vector<ProjectFolder> &ProjectFolder::getSubFolders() const
	{
		return m_subFolders;
	}

	const std::vector<fs::path> &ProjectFolder::getFiles() const
	{
		return m_files;
	}

	/*!
	 * Only gives useful data if hasWorldFile() is true!
	 * @return
	 */
	const World &ProjectFolder::getWorld() const
	{
		return m_world;
	}
}

#endif //TILESON_PROJECTFOLDER_HPP
/*** End of inlined file: ProjectFolder.hpp ***/


/*** Start of inlined file: ProjectData.hpp ***/
//
// Created by robin on 01.08.2020.
//

#ifndef TILESON_PROJECTDATA_HPP
#define TILESON_PROJECTDATA_HPP

namespace tson
{
	class ProjectData
	{
		public:
			ProjectData() = default;
			std::string automappingRulesFile;
			std::vector<std::string> commands;
			std::string extensionsPath;
			std::vector<std::string> folders;
			std::string objectTypesFile;
			ProjectPropertyTypes projectPropertyTypes;

			//Tileson specific
			fs::path basePath;
			std::vector<tson::ProjectFolder> folderPaths;
	};
}

#endif //TILESON_PROJECTDATA_HPP
/*** End of inlined file: ProjectData.hpp ***/

namespace tson
{
	class Project
	{
		public:
			#ifdef JSON11_IS_DEFINED
			inline explicit Project(std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>()) : m_json {std::move(jsonParser)}
			{

			}
			inline explicit Project(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>());
			#else
			inline explicit Project(std::unique_ptr<tson::IJson> jsonParser) : m_json {std::move(jsonParser)}
			{

			}
			inline explicit Project(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser);
			#endif
			inline bool parse(const fs::path &path);
			inline void parse();

			[[nodiscard]] inline const ProjectData &getData() const;
			[[nodiscard]] inline const fs::path &getPath() const;
			[[nodiscard]] inline const std::vector<ProjectFolder> &getFolders() const;
			[[nodiscard]] inline tson::EnumDefinition* getEnumDefinition(std::string_view name);
			[[nodiscard]] inline tson::TiledClass* getClass(std::string_view name);

		private:
			inline void parseJson(IJson &json);
			fs::path m_path;
			std::vector<ProjectFolder> m_folders;
			ProjectData m_data;
			std::unique_ptr<IJson> m_json = nullptr;
	};

	Project::Project(const fs::path &path, std::unique_ptr<tson::IJson> jsonParser) : m_json {std::move(jsonParser)}
	{
		parse(path);
	}

	bool Project::parse(const fs::path &path)
	{
		m_path = path;
		std::ifstream i(m_path.generic_string());

		try
		{
			if(!m_json->parse(path))
				return false;
		}
		catch(const std::exception &error)
		{
			std::string message = "Parse error: ";
			message += std::string(error.what());
			message += std::string("\n");
			return false;
		}
		parseJson(*m_json);
		return true;
	}

	const ProjectData &Project::getData() const
	{
		return m_data;
	}

	void Project::parseJson(IJson &json)
	{
		m_data.basePath = (m_path.empty()) ? fs::path() : m_path.parent_path(); //The directory of the project file

		//Make sure these property types are read before any map is, so they can be resolved.
		if(json.count("propertyTypes") > 0)
		{
			m_data.projectPropertyTypes.parse(json, this);
		}

		if(json.count("automappingRulesFile") > 0) m_data.automappingRulesFile = json["automappingRulesFile"].get<std::string>();
		if(json.count("commands") > 0)
		{
			m_data.commands.clear();
			auto &commands = json.array("commands");
			std::for_each(commands.begin(), commands.end(), [&](std::unique_ptr<IJson> &item)
			{
				m_data.commands.emplace_back(item->get<std::string>());
			});
		}
		if(json.count("extensionsPath") > 0) m_data.extensionsPath = json["extensionsPath"].get<std::string>();
		if(json.count("folders") > 0)
		{
			m_data.folders.clear();
			m_data.folderPaths.clear();
			auto &folders = json.array("folders");
			std::for_each(folders.begin(), folders.end(), [&](std::unique_ptr<IJson> &item)
			{
				std::string folder = item->get<std::string>();
				m_data.folders.emplace_back(folder);
				m_data.folderPaths.emplace_back(m_data.basePath / folder);
				m_folders.emplace_back(m_data.basePath / folder);
			});
		}
		if(json.count("objectTypesFile") > 0) m_data.objectTypesFile = json["objectTypesFile"].get<std::string>();
	}

	const fs::path &Project::getPath() const
	{
		return m_path;
	}

	const std::vector<ProjectFolder> &Project::getFolders() const
	{
		return m_folders;
	}

	tson::EnumDefinition *Project::getEnumDefinition(std::string_view name)
	{
		return m_data.projectPropertyTypes.getEnumDefinition(name);
	}

	tson::TiledClass *Project::getClass(std::string_view name)
	{
		return m_data.projectPropertyTypes.getClass(name);
	}

	/*!
	 * Parses preloaded json data. Only used during tests involving project jsons not actually read from files
	 * @return
	 */
	void Project::parse()
	{
		parseJson(*m_json);
	}

}

#endif //TILESON_PROJECT_HPP

/*** End of inlined file: Project.hpp ***/

namespace tson
{
	class Tileson
	{
		public:
			#ifdef JSON11_IS_DEFINED
			inline explicit Tileson(std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>(), bool includeBase64Decoder = true);
			inline explicit Tileson(tson::Project *project, std::unique_ptr<tson::IJson> jsonParser = std::make_unique<tson::Json11>(), bool includeBase64Decoder = true);
			#else
			inline explicit Tileson(std::unique_ptr<tson::IJson> jsonParser, bool includeBase64Decoder = true);
			inline explicit Tileson(tson::Project *project, std::unique_ptr<tson::IJson> jsonParser, bool includeBase64Decoder = true);
			#endif

			inline std::unique_ptr<tson::Map> parse(const fs::path &path, std::unique_ptr<IDecompressor<std::vector<uint8_t>, std::vector<uint8_t>>> decompressor = nullptr);
			inline std::unique_ptr<tson::Map> parse(const void * data, size_t size, std::unique_ptr<IDecompressor<std::vector<uint8_t>, std::vector<uint8_t>>> decompressor = nullptr);
			inline tson::DecompressorContainer *decompressors();

		private:
			inline std::unique_ptr<tson::Map> parseJson();
			std::unique_ptr<tson::IJson> m_json;
			tson::DecompressorContainer m_decompressors;
			tson::Project *m_project {nullptr};
	};
}

/*!
 *
 * @param includeBase64Decoder Includes the base64-decoder from "Base64Decompressor.hpp" if true.
 * Otherwise no other decompressors/decoders than whatever the user itself have added will be used.
 */
tson::Tileson::Tileson(std::unique_ptr<tson::IJson> jsonParser, bool includeBase64Decoder) : m_json {std::move(jsonParser)}
{
	if(includeBase64Decoder)
		m_decompressors.add<Base64Decompressor>();
}

tson::Tileson::Tileson(tson::Project *project, std::unique_ptr<tson::IJson> jsonParser, bool includeBase64Decoder) : m_json {std::move(jsonParser)}
{
	m_project = project;
	if(includeBase64Decoder)
		m_decompressors.add<Base64Decompressor>();
}

/*!
 * Parses Tiled json map data by file
 * @param path path to file
 * @return parsed data as Map
 */
std::unique_ptr<tson::Map> tson::Tileson::parse(const fs::path &path, std::unique_ptr<IDecompressor<std::vector<uint8_t>, std::vector<uint8_t>>> decompressor)
{

	bool result = false;

	if(decompressor != nullptr)
	{
		std::vector<uint8_t> decompressed = decompressor->decompressFile(path);
		result = (decompressed.empty()) ? false : true;
		if(!result)
			return std::make_unique<tson::Map>(tson::ParseStatus::DecompressionError, "Error during decompression");

		result = m_json->parse(&decompressed[0], decompressed.size());

		if(result)
			return parseJson();
	}
	else if(m_json->parse(path))
	{
		return parseJson();
	}

	std::string msg = "File not found: ";
	msg += std::string(path.generic_string());
	return std::make_unique<tson::Map>(tson::ParseStatus::FileNotFound, msg);
}

/*!
 * Parses Tiled json map data by memory
 * @param data The data to parse
 * @param size The size of the data to parse
 * @return parsed data as Map
 */
std::unique_ptr<tson::Map> tson::Tileson::parse(const void *data, size_t size, std::unique_ptr<IDecompressor<std::vector<uint8_t>, std::vector<uint8_t>>> decompressor)
{
	bool result = false;

	if(decompressor != nullptr)
	{
		std::vector<uint8_t> decompressed = decompressor->decompress(data, size);
		result = (decompressed.empty()) ? false : true;
		if(!result)
			return std::make_unique<tson::Map>(tson::ParseStatus::DecompressionError, "Error during decompression");
		result = m_json->parse(&decompressed[0], decompressed.size());
	}
	else
		result = m_json->parse(data, size);

	if(!result)
		return std::make_unique<tson::Map>(tson::ParseStatus::ParseError, "Memory error");

	return parseJson();
}

/*!
 * Common parsing functionality for doing the json parsing
 * @param json Tiled json to parse
 * @return parsed data as Map
 */
std::unique_ptr<tson::Map> tson::Tileson::parseJson()
{
	std::unique_ptr<tson::Map> map = std::make_unique<tson::Map>();

	if(map->parse(*m_json, &m_decompressors, m_project))
		return map;

	return std::make_unique<tson::Map> (tson::ParseStatus::MissingData, "Missing map data...");
}

/*!
 * Gets the decompressor container used when something is either encoded or compressed (regardless: IDecompressor is used as base).
 * These are used specifically for tile layers, and are connected by checking the name of the IDecompressor. If the name of a decompressor
 * matches with an encoding or a compression, its decompress() function will be used.
 *
 * @return The container including all decompressors.
 */
tson::DecompressorContainer *tson::Tileson::decompressors()
{
	return &m_decompressors;
}

#endif //TILESON_TILESON_PARSER_HPP

/*** End of inlined file: tileson_parser.hpp ***/


/*** Start of inlined file: tileson_forward.hpp ***/
//
// Created by robin on 25.07.2020.
//

#ifndef TILESON_TILESON_FORWARD_HPP
#define TILESON_TILESON_FORWARD_HPP
/*!
 * T I L E S O N   F O R W A R D   D E C L A R A T I O N S
 * -------------------------------------------------------
 *
 * Due to cross-references we have forward declarations that cannot be resolved during the
 * implementation, thus the implementations must be done later when the class definition itself is known.
 *
 * All those forward declarations can be found below.
 */

// M a p . h p p
// ----------------

tson::TiledClass *tson::Map::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_project != nullptr) ? m_project->getClass(m_classType) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// T i l e . h p p
// ---------------------

/*!
 * Really just a shortcut to retrieve the tile size from the map.
 * @return TileSize based on the map property for tile size.
 */
const tson::Vector2i tson::Tile::getTileSize() const
{
	if(m_map != nullptr)
		return m_map->getTileSize();
	else
		return {0,0};
}

bool tson::Tile::parseId(IJson &json)
{
	if(json.count("id") > 0)
	{
		m_id = json["id"].get<uint32_t>() + 1;
		if (m_tileset != nullptr)
			m_gid = m_tileset->getFirstgid() + m_id - 1;
		else
			m_gid = m_id;
		manageFlipFlagsByIdThenRemoveFlags(m_gid);
		return true;
	}
	return false;
}

/*!
 * Uses tson::Tileset and tson::Map data to calculate related values for tson::Tile.
 * Added in v1.2.0
 */
void tson::Tile::performDataCalculations()
{
	if(m_tileset == nullptr || m_map == nullptr)
		return;

	int firstId = m_tileset->getFirstgid(); //First tile id of the tileset
	int columns = m_tileset->getColumns();
	int rows = m_tileset->getTileCount() / columns;
	int lastId = (m_tileset->getFirstgid() + m_tileset->getTileCount()) - 1;

	int const gid = static_cast<int>(getGid());
	if (gid >= firstId && gid <= lastId)
	{
		int const baseTilePosition = (gid - firstId);

		int const tileModX = (baseTilePosition % columns);
		int const currentRow = (baseTilePosition / columns);
		int const offsetX = (tileModX != 0) ? ((tileModX) * m_map->getTileSize().x) : (0 * m_map->getTileSize().x);
		int const offsetY =  (currentRow < rows-1) ? (currentRow * m_map->getTileSize().y) : ((rows-1) * m_map->getTileSize().y);

		tson::Vector2i spacing = m_tileset->getMarginSpacingOffset({tileModX, currentRow});
		m_drawingRect = { offsetX + spacing.x, offsetY + spacing.y, m_map->getTileSize().x, m_map->getTileSize().y };
	}
	else
		m_drawingRect = {0, 0, 0, 0};
}

/*!
 * Get the position of the tile in pixels based on the tile data position from the current layer.
 * @return The position of the tile in Pixels
 */
const tson::Vector2f tson::Tile::getPosition(const std::tuple<int, int> &tileDataPos)
{
	return {((float) std::get<0>(tileDataPos)) * m_drawingRect.width, ((float) std::get<1>(tileDataPos)) * m_drawingRect.height};
}

/*!
 * Gets the class information for the 'type'/'class'
 * This may only give a valid result if the map is loaded through a tson::Project
 * @return a tson::TiledClass object if related map was loaded through tson::Project
 */
tson::TiledClass *tson::Tile::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_type) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// T i l e s e t . h p p
// ------------------------

tson::TiledClass *tson::Tileset::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// T i l e O b j e c t . h p p
// ---------------------

/*!
 * In cases where the empty constructor is called, this must be called manually
 * for this class to make sense
 * @param posInTileUnits
 * @param tile
 */
void tson::TileObject::initialize(const std::tuple<int, int> &posInTileUnits, tson::Tile *tile)
{
	m_tile = tile;
	m_posInTileUnits = tile->getPositionInTileUnits(posInTileUnits);
	m_position = tile->getPosition(posInTileUnits);
}

const tson::Rect &tson::TileObject::getDrawingRect() const
{
	return m_tile->getDrawingRect();
}

// L a y e r . h p p
// -------------------

/*!
 * Decompresses data if there are matching decompressors
 */
void tson::Layer::decompressData()
{

	tson::DecompressorContainer *container = m_map->getDecompressors();
	if(container->empty())
		return;

	if(m_encoding.empty() && m_compression.empty())
		return;

	std::string data = m_base64Data;
	bool hasBeenDecoded = false;
	if(!m_encoding.empty() && container->contains(m_encoding))
	{
		data = container->get(m_encoding)->decompress(data);
		hasBeenDecoded = true;
	}

	if(!m_compression.empty() && container->contains(m_compression))
	{
		data = container->get(m_compression)->decompress(data);
	}

	if(hasBeenDecoded)
	{
		std::vector<uint8_t> bytes = tson::Tools::Base64DecodedStringToBytes(data);
		m_data = tson::Tools::BytesToUnsignedInts(bytes);
	}
}

/*!
 * Parses a Tiled layer from json
 * @param json
 * @return true if all mandatory fields was found. false otherwise.
 */
bool tson::Layer::parse(IJson &json, tson::Map *map)
{
	m_map = map;

	bool allFound = true;
	if(json.count("tintcolor") > 0) m_tintColor = tson::Colori(json["tintcolor"].get<std::string>()); //Optional
	if(json.count("compression") > 0) m_compression = json["compression"].get<std::string>(); //Optional
	if(json.count("draworder") > 0) m_drawOrder = json["draworder"].get<std::string>(); //Optional
	if(json.count("encoding") > 0) m_encoding = json["encoding"].get<std::string>(); //Optional
	if(json.count("id") > 0) m_id = json["id"].get<int>(); //Optional
	if(json.count("image") > 0) m_image = json["image"].get<std::string>(); //Optional
	if(json.count("name") > 0) m_name = json["name"].get<std::string>(); else allFound = false;
	if(json.count("offsetx") > 0 && json.count("offsety") > 0)
		m_offset = {json["offsetx"].get<float>(), json["offsety"].get<float>()}; //Optional
	if(json.count("opacity") > 0) m_opacity = json["opacity"].get<float>(); else allFound = false;
	if(json.count("width") > 0 && json.count("height") > 0)
		m_size = {json["width"].get<int>(), json["height"].get<int>()}; //else allFound = false; - Not mandatory for all layers!
	if(json.count("transparentcolor") > 0) m_transparentColor = tson::Colori(json["transparentcolor"].get<std::string>()); //Optional
	if(json.count("type") > 0) m_typeStr = json["type"].get<std::string>(); else allFound = false;
	if(json.count("class") > 0) m_classType = json["class"].get<std::string>();                     //Optional
	if(json.count("visible") > 0) m_visible = json["visible"].get<bool>(); else allFound = false;
	if(json.count("x") > 0) m_x = json["x"].get<int>(); else allFound = false;
	if(json.count("y") > 0) m_y = json["y"].get<int>(); else allFound = false;
	if(json.count("repeatx") > 0) m_repeatX = json["repeatx"].get<bool>(); //Optional
	if(json.count("repeaty") > 0) m_repeatY = json["repeaty"].get<bool>(); //Optional

	tson::Vector2f parallax {1.f, 1.f};
	if(json.count("parallaxx") > 0)
		parallax.x = json["parallaxx"].get<float>();
	if(json.count("parallaxy") > 0)
		parallax.y = json["parallaxy"].get<float>();

	m_parallax = parallax;

	//Handle DATA (Optional)
	if(json.count("data") > 0)
	{
		if(json["data"].isArray())
		{
			auto &array = json.array("data");
			std::for_each(array.begin(), array.end(), [&](std::unique_ptr<IJson> &item) { m_data.push_back(item->get<uint32_t>()); });
		}
		else
		{
			m_base64Data = json["data"].get<std::string>();
			decompressData();
		}
	}

	//More advanced data
	if(json.count("chunks") > 0 && json["chunks"].isArray())
	{
		auto &chunks = json.array("chunks");
		std::for_each(chunks.begin(), chunks.end(), [&](std::unique_ptr<IJson> &item) { m_chunks.emplace_back(*item); });
	}
	if(json.count("layers") > 0 && json["layers"].isArray())
	{
		auto &layers = json.array("layers");
		std::for_each(layers.begin(), layers.end(), [&](std::unique_ptr<IJson> &item) { m_layers.emplace_back(*item, m_map); });
	}
	if(json.count("objects") > 0 && json["objects"].isArray())
	{
		auto &objects = json.array("objects");
		std::for_each(objects.begin(), objects.end(), [&](std::unique_ptr<IJson> &item) { m_objects.emplace_back(*item, m_map); });
	}
	if(json.count("properties") > 0 && json["properties"].isArray())
	{
		auto &properties = json.array("properties");
		tson::Project *project = (m_map != nullptr) ? m_map->getProject() : nullptr;
		std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr<IJson> &item) { m_properties.add(*item, project); });
	}

	setTypeByString();

	return allFound;
}

tson::TiledClass *tson::Layer::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// O b j e c t . h p p
// --------------------

// W a n g s e t . h p p
// ----------------------
tson::TiledClass *tson::WangSet::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// W a n g c o l o r . h p p
// ----------------------
tson::TiledClass *tson::WangColor::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_classType) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

/*!
 * Gets the class information for the 'type'/'class'
 * This may only give a valid result if the map is loaded through a tson::Project
 * @return a tson::TiledClass object if related map was loaded through tson::Project
 */
tson::TiledClass *tson::Object::getClass()
{
	if(m_class == nullptr)
	{
		TiledClass* baseClass = (m_map != nullptr && m_map->getProject() != nullptr) ? m_map->getProject()->getClass(m_type) : nullptr;
		if(baseClass != nullptr)
		{
			m_class = std::make_shared<TiledClass>(*baseClass);
			m_class->update(m_properties);
		}
	}
	return (m_class != nullptr) ? m_class.get() : nullptr;
}

// W o r l d . h p p
// ------------------

/*!
 * Loads the actual maps based on the world data.
 * @param parser A Tileson object used for parsing the maps of the world.
 * @return How many maps who were parsed. Remember to call getStatus() for the actual map to find out if everything went okay.
 */

std::size_t tson::World::loadMaps(tson::Tileson *parser)
{
	m_maps.clear();
	std::for_each(m_mapData.begin(), m_mapData.end(), [&](const tson::WorldMapData &data)
	{
		if(fs::exists(data.path))
		{
			std::unique_ptr<tson::Map> map = parser->parse(data.path);
			m_maps.push_back(std::move(map));
		}
	});

	return m_maps.size();
}

// P r o p e r t y . h p p
// ------------------
void tson::Property::setValueByType(IJson &json)
{
	switch(m_type)
	{
		case Type::Color:
			m_value = Colori(json.get<std::string>());
			break;

		case Type::File:
			m_value = fs::path(json.get<std::string>());
			break;

		case Type::Int:
			if(!m_propertyType.empty())
			{
				m_type = Type::Enum;
				tson::EnumDefinition *def = (m_project != nullptr) ? m_project->getEnumDefinition(m_propertyType) : nullptr;
				if(def != nullptr)
				{
					uint32_t v = json.get<uint32_t>();
					m_value = tson::EnumValue(v, def);
				}
				else
					m_value = tson::EnumValue();
			}
			else
				m_value = json.get<int>();

			break;

		case Type::Boolean:
			m_value = json.get<bool>();
			break;

		case Type::Float:
			m_value = json.get<float>();
			break;

		case Type::String:
			if(!m_propertyType.empty())
			{
				m_type = Type::Enum;
				tson::EnumDefinition *def = (m_project != nullptr) ? m_project->getEnumDefinition(m_propertyType) : nullptr;
				if(def != nullptr)
				{
					std::string v = json.get<std::string>();
					m_value = tson::EnumValue(v, def);
				}
				else
					m_value = tson::EnumValue();
			}
			else
				setStrValue(json.get<std::string>());

			break;

		case Type::Class:
		{
			tson::TiledClass *baseClass = (m_project != nullptr) ? m_project->getClass(m_propertyType) : nullptr;
			if (baseClass != nullptr)
			{
				tson::TiledClass c = *baseClass;
				c.update(json);
				m_value = c;
			}
		}
		break;

		case Type::Object:
			m_value = json.get<uint32_t>();
			break;
		default:
			setStrValue(json.get<std::string>());
			break;
	}
}

#endif //TILESON_TILESON_FORWARD_HPP

/*** End of inlined file: tileson_forward.hpp ***/

#endif //TILESON_TILESON_H

