//! \~\file pijson.h
//! \~\ingroup Serialization
//! \~\brief
//! \~english JSON tree node type
//! \~russian Тип узла дерева JSON
/*
    PIP - Platform Independent Primitives
    JSON class
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIJSON_H
#define PIJSON_H

#include "pivariant.h"


//! \~\ingroup Serialization
//! \~\brief
//! \~english JSON tree node.
//! \~russian Узел дерева JSON.
//! \~\details
//! \~english %PIJSON class provides a tree structure for JSON data representation.
//! Each element can be a value (with name), an array, or an object.
//! \~russian Класс %PIJSON предоставляет древовидную структуру для представления данных JSON.
//! Каждый элемент может быть значением (с именем), массивом или объектом.
class PIP_EXPORT PIJSON {
	friend PICout operator<<(PICout s, const PIJSON & v);

public:
	//! \~english Type of JSON node.
	//! \~russian Тип узла JSON.
	enum Type {
		Invalid /*! \~english Invalid type \~russian Недействительный тип */,
		Null /*! \~english Without value, null \~russian Без значения, null */,
		Boolean /*! \~english Boolean, \b true or \b false \~russian Логическое, \b true или \b false */,
		Number /*! \~english Integer or floating-point number \~russian Целое либо число с плавающей точкой */,
		String /*! \~english Text \~russian Текст */,
		Object /*! \~english Object, {} \~russian Объект, {} */,
		Array /*! \~english Array, [] \~russian Массив, [] */
	};

	//! \~english Formatting mode for JSON text output.
	//! \~russian Режим форматирования JSON-текста.
	enum PrintType {
		Compact /*! \~english Without spaces, minimum size \~russian Без пробелов, минимальный размер */,
		Tree /*! \~english With spaces and new-lines, human-readable \~russian С пробелами и новыми строками, читаемый человеком */
	};


	//! \~english Constructs invalid %PIJSON.
	//! \~russian Создает недействительный %PIJSON.
	PIJSON() {}

	//! \~english Copy constructor.
	//! \~russian Конструктор копирования.
	PIJSON(const PIJSON & o) = default;


	//! \~english Returns node name, or empty string for unnamed nodes.
	//! \~russian Возвращает имя узла, либо пустую строку для безымянных узлов.
	const PIString & name() const { return c_name; }

	//! \~english Returns array children, or an empty array when the node is not \a PIJSON::Array.
	//! \~russian Возвращает дочерние элементы массива, либо пустой массив, если узел не имеет тип \a PIJSON::Array.
	const PIVector<PIJSON> & array() const;

	//! \~english Returns object members, or an empty map when the node is not \a PIJSON::Object.
	//! \~russian Возвращает поля объекта, либо пустой словарь, если узел не имеет тип \a PIJSON::Object.
	const PIMap<PIString, PIJSON> & object() const;

	//! \~english Returns scalar node value.
	//! \~russian Возвращает скалярное значение узла.
	const PIVariant & value() const { return c_value; }


	//! \~english Returns scalar value as bool.
	//! \~russian Возвращает скалярное значение как логическое.
	bool toBool() const { return c_value.toBool(); }

	//! \~english Returns scalar value as integer.
	//! \~russian Возвращает скалярное значение как целое число.
	int toInt() const { return c_value.toInt(); }

	//! \~english Returns scalar value as floating-point number.
	//! \~russian Возвращает скалярное значение как число с плавающей точкой.
	double toDouble() const { return c_value.toDouble(); }

	//! \~english Returns scalar value as string.
	//! \~russian Возвращает скалярное значение как строку.
	PIString toString() const { return c_value.toString(); }


	//! \~english Returns node type.
	//! \~russian Возвращает тип узла.
	Type type() const { return c_type; }

	//! \~english Returns whether the node is valid.
	//! \~russian Возвращает, является ли узел действительным.
	bool isValid() const { return c_type != Invalid; }

	//! \~english Returns whether the node is \a PIJSON::Object.
	//! \~russian Возвращает, является ли узел \a PIJSON::Object.
	bool isObject() const { return c_type == Object; }

	//! \~english Returns whether the node is \a PIJSON::Array.
	//! \~russian Возвращает, является ли узел \a PIJSON::Array.
	bool isArray() const { return c_type == Array; }


	//! \~english Sets scalar value and updates node type for bool, numeric and string variants.
	//! \~russian Устанавливает скалярное значение и обновляет тип узла для логических, числовых и строковых вариантов.
	void setValue(const PIVariant & v);

	//! \~english Clears node content and sets type to \a PIJSON::Invalid.
	//! \~russian Очищает содержимое узла и устанавливает тип \a PIJSON::Invalid.
	void clear();


	//! \~english Returns array length or object member count. Other node types return 0.
	//! \~russian Возвращает длину массива или число полей объекта. Для остальных типов возвращает 0.
	int size() const;

	//! \~english Returns whether an object node contains key \a key.
	//! \~russian Возвращает, содержит ли узел-объект ключ \a key.
	bool contains(const PIString & key) const;

	//! \~english Converts node to array and resizes it. New entries become empty strings.
	//! \~russian Преобразует узел в массив и изменяет его размер. Новые элементы становятся пустыми строками.
	void resize(int new_size);


	//! \~english Synonym of \a setValue().
	//! \~russian Аналог \a setValue().
	PIJSON & operator=(const PIVariant & v) {
		setValue(v);
		return *this;
	}

	//! \~english Copy assignment operator.
	//! \~russian Оператор копирующего присваивания.
	PIJSON & operator=(const PIJSON & v);


	//! \~english Returns array element by index, or invalid %PIJSON when the node is not an array.
	//! \~russian Возвращает элемент массива по индексу, либо недействительный %PIJSON, если узел не является массивом.
	const PIJSON & operator[](int index) const;

	//! \~english Converts node to array, grows it when needed and returns element by index.
	//! \~russian Преобразует узел в массив, при необходимости увеличивает его и возвращает элемент по индексу.
	PIJSON & operator[](int index);

	//! \~english Converts node to array and appends another JSON node.
	//! \~russian Преобразует узел в массив и добавляет другой узел JSON.
	PIJSON & operator<<(const PIJSON & element);

	//! \~english Converts node to array and appends a scalar value.
	//! \~russian Преобразует узел в массив и добавляет скалярное значение.
	PIJSON & operator<<(const PIVariant & value);


	//! \~english Returns object member by key, or invalid %PIJSON when the node is not an object or the key is absent.
	//! \~russian Возвращает поле объекта по ключу, либо недействительный %PIJSON, если узел не является объектом или ключ отсутствует.
	PIJSON operator[](const PIString & key) const;

	//! \~english Converts node to object and returns member by key, creating it when needed.
	//! \~russian Преобразует узел в объект и возвращает поле по ключу, создавая его при необходимости.
	PIJSON & operator[](const PIString & key);

	//! \~english Convenience overload for UTF-8 key access.
	//! \~russian Вспомогательная перегрузка для доступа по UTF-8 ключу.
	PIJSON operator[](const char * key) const { return (*this)[PIString::fromUTF8(key)]; }

	//! \~english Convenience overload for UTF-8 key access.
	//! \~russian Вспомогательная перегрузка для доступа по UTF-8 ключу.
	PIJSON & operator[](const char * key) { return (*this)[PIString::fromUTF8(key)]; }


	//! \~english Returns JSON text representation of the node tree.
	//! \~russian Возвращает JSON-текст для дерева узлов.
	PIString toJSON(PrintType print_type = Compact, bool mask_unicode = true) const;

	//! \~english Parses JSON text and returns the root node.
	//! \~russian Разбирает JSON-текст и возвращает корневой узел.
	static PIJSON fromJSON(PIString str);


	//! \~english Creates an object node initialized from variant fields.
	//! \~russian Создает узел-объект, инициализированный из полей вариативного словаря.
	static PIJSON newObject(const PIVariantMap & fields = {});

	//! \~english Creates an array node initialized from variant elements.
	//! \~russian Создает узел-массив, инициализированный из вариативных элементов.
	static PIJSON newArray(const PIVariantVector & fields = {});

	//! \~english Creates a string node.
	//! \~russian Создает строковый узел.
	static PIJSON newString(const PIString & v = PIString());


	//! \~english Serializes value \a v with the overload set from \a pijsonserialization.h.
	//! \~russian Сериализует значение \a v через набор перегрузок из \a pijsonserialization.h.
	template<typename T>
	static PIJSON serialize(const T & v);

	//! \~english Deserializes value of type \c T with the overload set from \a pijsonserialization.h.
	//! \~russian Десериализует значение типа \c T через набор перегрузок из \a pijsonserialization.h.
	template<typename T>
	static T deserialize(const PIJSON & json);


private:
	static PIJSON & nullEntry();
	static PIString parseName(PIString & s);
	static PIJSON parseValue(PIString & s);
	static PIJSON parseObject(PIString s);
	static PIJSON parseArray(PIString s);
	static PIString stringMask(const PIString & s, bool mask_unicode);
	static PIString stringUnmask(const PIString & s);
	static void
	print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform = false, bool comma = false, bool mask_unicode = true);

	PIString c_name;
	PIVariant c_value;
	PIVector<PIJSON> c_array;
	PIMap<PIString, PIJSON> c_object;
	Type c_type = Invalid;
};


//! \relatesalso PICout
//! \~english Output operator to \a PICout.
//! \~russian Оператор вывода в \a PICout.
inline PICout operator<<(PICout s, const PIJSON & v) {
	s.space();
	s.saveAndSetControls(0);
	PIString str;
	PIJSON::print(str, v, "", true);
	s << str;
	s.restoreControls();
	return s;
}


#endif // PIJSON_H
