/*! \file pijson.h
 * \ingroup Serialization
 * \brief
 * \~english JSON class
 * \~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 class.
//! \~russian Класс JSON.
class PIP_EXPORT PIJSON {
	friend PICout operator <<(PICout s, const PIJSON & v);
public:

	//! \~english
	//! Type of JSON tree element
	//! \~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
	//! Generate JSON variant
	//! \~russian
	//! Вариант генерации JSON
	enum PrintType {
		Compact /*! \~english Without spaces, minimum size \~russian Без пробелов, минимальный размер */,
		Tree    /*! \~english With spaces and new-lines, human-readable \~russian С пробелами и новыми строками, читаемый человеком */
	};

	//! \~english Contructs invalid %PIJSON.
	//! \~russian Создает недействительный %PIJSON.
	PIJSON() {}
	PIJSON(const PIJSON & o) = default;

	//! \~english Returns name of element, or empty string if it doesn`t have name.
	//! \~russian Возвращает имя элемента, либо пустую строку, если имени нет.
	const PIString & name() const {return c_name;}

	//! \~english Returns elements array of this element, or empty array if element is not \a PIJSON::Array.
	//! \~russian Возвращает массив элементов этого элемента, либо пустой массив, если тип элемента не \a PIJSON::Array.
	const PIVector<PIJSON> & array() const;

	//! \~english Returns elements map of this element, or empty map if element is not \a PIJSON::Object.
	//! \~russian Возвращает словарь элементов этого элемента, либо пустой словарь, если тип элемента не \a PIJSON::Object.
	const PIMap<PIString, PIJSON> & object() const;

	//! \~english Returns element value.
	//! \~russian Возвращает значение элемента.
	const PIVariant & value() const {return c_value;}

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

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

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

	//! \~english Returns element value as string, valid for all types.
	//! \~russian Возвращает значение элемента как строка, действительно для всех типов.
	PIString toString() const {return c_value.toString();}


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

	//! \~english Returns if element is valid.
	//! \~russian Возвращает действителен ли элемент.
	bool isValid() const {return c_type != Invalid;}

	//! \~english Returns if element is \a PIJSON::Object.
	//! \~russian Возвращает является ли элемент \a PIJSON::Object.
	bool isObject() const {return c_type == Object;}

	//! \~english Returns if element is \a PIJSON::Array.
	//! \~russian Возвращает является ли элемент \a PIJSON::Array.
	bool isArray() const {return c_type == Array;}

	//! \~english Set value and type of element from "v".
	//! \~russian Устанавливает значение и тип элемента из "v".
	void setValue(const PIVariant & v);

	//! \~english Clear element and set it to \a PIJSON::Invalid.
	//! \~russian Очищает элемент и устанавливает его в \a PIJSON::Invalid.
	void clear();

	//! \~english Returns size of elements array if type is \a PIJSON::Array, size of elements map if type is \a PIJSON::Object, otherwise returns 0.
	//! \~russian Возвращает размер массива элементов если тип \a PIJSON::Array, размер словаря элементов если тип \a PIJSON::Object, иначе возвращает 0.
	int size() const;

	//! \~english Returns if elements map contains key "key" if type is \a PIJSON::Object, otherwise returns \b false.
	//! \~russian Возвращает содержит ли словарь элементов ключ "key" если тип \a PIJSON::Object, иначе возвращает \b false.
	bool contains(const PIString & key) const;

	//! \~english Set element type to \a PIJSON::Array and resize elements array to "new_size".
	//! \~russian Устанавливает тип элемента в \a PIJSON::Array и изменяет размер массива элементов на "new_size".
	void resize(int new_size);


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

	PIJSON & operator=(const PIJSON & v);


	//! \~english Returns element from array with index "index" if type is \a PIJSON::Array, otherwise returns invalid %PIJSON.
	//! \~russian Возвращает элемент из массива по индексу "index" если тип \a PIJSON::Array, иначе возвращает недействительный %PIJSON.
	const PIJSON & operator[](int index) const;

	//! \~english Set element type to \a PIJSON::Array, resize if necessary and returns element from array with index "index".
	//! \~russian Устанавливает тип элемента в \a PIJSON::Array, изменяет размер массива при неоходимости и возвращает элемент из массива по индексу "index".
	PIJSON & operator[](int index);

	//! \~english Set element type to \a PIJSON::Array and add element to the end of array.
	//! \~russian Устанавливает тип элемента в \a PIJSON::Array и добавляет элемент в массив.
	PIJSON & operator <<(const PIJSON & element);


	//! \~english Returns element from map with key "key" if type is \a PIJSON::Object, otherwise returns invalid %PIJSON.
	//! \~russian Возвращает элемент из словаря по ключу "key" если тип \a PIJSON::Object, иначе возвращает недействительный %PIJSON.
	PIJSON operator[](const PIString & key) const;

	//! \~english Set element type to \a PIJSON::Object and returns element from map with key "key". If element with this key doesn`t exists, it will be created.
	//! \~russian Устанавливает тип элемента в \a PIJSON::Object и возвращает элемент из словаря по ключу "key". Если элемента с таким ключом не существует, он будет создан.
	PIJSON & operator[](const PIString & key);

	PIJSON operator[](const char * key) const {return (*this)[PIString::fromUTF8(key)];}
	PIJSON & operator[](const char * key) {return (*this)[PIString::fromUTF8(key)];}


	//! \~english Returns text representation of JSON tree.
	//! \~russian Возвращает текстовое представление дерева JSON.
	PIString toJSON(PrintType print_type = Compact) const;

	//! \~english Parse text representation of JSON "str" and returns it root element.
	//! \~russian Разбирает текстовое представление JSON "str" и возвращает его корневой элемент.
	static PIJSON fromJSON(PIString str);

	static PIJSON newObject();
	static PIJSON newArray();
	static PIJSON newString(const PIString & v = PIString());

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);
	static PIString stringUnmask(const PIString & s);
	static void print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform = false, bool comma = false);

	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 // PICONSTCHARS_H
