/*
*******************************************************************************
\file json.h
\brief JSON (JavaScript Object Notation, https://json.org)
\project bee2 [cryptographic library]
\created 2025.05.07
\version 2025.05.13
\copyright The Bee2 authors
\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
*******************************************************************************
*/

/*!
*******************************************************************************
\file json.h
\brief JSON
*******************************************************************************
*/

#ifndef __BEE2_JSON_H
#define __BEE2_JSON_H

#include "bee2/defs.h"

#ifdef __cplusplus
extern "C" {
#endif

/*
*******************************************************************************
\file json.h

Элементы JSON -- это текстовые строки следующих типов.

1. Объект. Начинается открывающей фигурной скобкой (`{`) и заканчивается 
   закрывающей (`}`). Состоит из пар (имя, элемент). Имя представляет собой 
   строку. Имя и элемент внутри пары разделяются двоеточием (`:`). Пары 
   разделяются запятыми. Запятая перед закрывающей скобкой не допускается.
2. Массив. Начинается открывающей квадратной скобкой (`[`) и заканчивается 
   закрывающей (`]`). Состоит из элементов, разделенных запятыми. Запятая 
   перед закрывающей скобкой не допускается.
3. Строка. Начинается и заканчивается кавычками (`"`). Может содержать escape-
   последовательности. Escape-последовательность начинается символом `\`и 
   продолжается одним из символов строки `"\/bfnrtu`. Escape-последовательность
   `\u` дополнительно продолжается четырьмя шестнадцатеричными символами --
   кодом символа Unicode.
4. Число. Состоит из десятичных символов `0` -- `9`. Незначащие нули не 
   допускаются.
5. Лексема `true`.
6. Лексема `false`.
7. Лексема `null`.

\warning Сужение реализации: отрицательные числа и числа с плавающей точкой 
не поддерживаются. Число должно укладываться в size_t.

Объект и массив являются составными элементами, строка, число и лексемы ---
примитивными.

Тип элемента можно определить по его первому символу:
`{`, `[`, `"`, `0`..`9`, `t`, `f` или `n`.

Между элементами и разделителями (запятыми, двоеточиями, скобками) может 
размещаться любое число пробелов. Пробелом является как собственно пробел, так
и символы `\n`, `\r`, `\t`. 

JSON-код представляет собой элемент с любым числом предваряющих и завершающих 
пробелов. Обычно подразумевается составной элемент -- объект или массив.

Элемент описываются типом json_elem_t. При декодировании составного элемента 
возвращается массив вложенных элементов этого типа. В каждом подэлементе
поле json ссылается на фрагмент в родительском элементе, а поле count задает 
длину этого фрагмента. Таким образом, для вложенных элемента можно не выделять 
дополнительную память.

\remark Похожий подход реализован в https://github.com/zserge/jsmn.

\remark Элемент типа json_elem_t может быть и результатом декодирования, 
и объектом декодирования. В последнем случае в функцию декодирования 
передается не указатель на объект, а его поля по отдельности:
\code
	json_elem_t elem;
	size_t c;
	size_t size;
	...
	c = jsonArrDec(&elem, 1, json, count);
	...
	c = jsonSizeDec(&size, elem.json, elem.count);
\endcode

*******************************************************************************
*/

/*!	\brief Элемент JSON

	Описывается элемент JSON -- фрагмент [count]json JSON-кода.
*/
typedef struct
{
	const char* json;		/*!< начало элемента */
	size_t count;			/*!< длина элемента */
} json_elem_t;

/*!	\brief Корректный код?

	Проверяется корректность кода [count]json.
	\return Признак корректности. 
*/
bool_t jsonIsValid(
	const char json[],		/*!< [in] код */
	size_t count			/*!< [in] длина кода */
);

/*!	\brief Декодирование строки

	Декодируется строка JSON, заданная кодом [count]json. В результате 
	декодирования определяется подстрока [len]str.
	\pre Указатель json корректен.
	\pre Любой из указателей str и len может быть нулевым.
	\return Обработанная при декодировании длина JSON-кода (включая 
	предваряющие и завершающие пробелы) или SIZE_MAX в случае ошибки.
	\remark Декодирование состоит в исключении пробелов и окаймляющих 
	кавычек. 
*/
size_t jsonStrDec(
	const char** str,		/*!< [out] подстрока */
	size_t* len,			/*!< [out] длина подстроки */
	const char json[],		/*!< [in] код */
	size_t count			/*!< [in] длина кода */
);

/*!	\brief Декодирование строки

	Декодируется число size, заданное кодом [count]json.
	\pre Указатель json корректен.
	\pre Указатель size может быть нулевым и тогда декодированное
	число не возвращается.
	\return Обработанная при декодировании длина JSON-кода (включая 
	предваряющие и завершающие пробелы) или SIZE_MAX в случае ошибки.
	\remark Код SIZE_MAX будет вовзращен, если число не укладывается 
	в size_t. 
*/
size_t jsonSizeDec(
	size_t* size,			/*!< [out] число */
	const char json[],		/*!< [in] код */
	size_t count			/*!< [in] длина кода */
);

/*!	\brief Декодирование объекта

	Декодируется объект JSON, заданный кодом [count]json. В результате 
	декодирования определяются вложенные в объект элементы [names_count]elems
	с именами [names_count]names: elems[i] -- это элемент с именем names[i].
	\pre Указатели json, names и elems корректны.
	\return Обработанная при декодировании длина JSON-кода (включая 
	предваряющие и завершающие пробелы) или SIZE_MAX в случае ошибки.
	\remark При повторе имен в names, при отсутствии нужного элемента и при
	наличии лишних элементов будет возвращена ошибка. 
*/
size_t jsonObjDec(
	json_elem_t elems[],		/*!< [out] элементы */
	const char json[],			/*!< [in] код */
	size_t count,				/*!< [in] длина кода */
	const char* const names[],	/*!< [in] имена элементов */
	size_t names_count			/*!< [in] число элементов */
);

/*!	\brief Декодирование массива

	Декодируется массив JSON, заданный кодом [count]json. В результате
	декодирования определяются вложенные в массив элементы [elems_count?]elems.
	\pre Указатель json корректен.
	\pre Любой из указателей elems и count может быть нулевым.
	\return Обработанная при декодировании длина JSON-кода (включая	
	предваряющие и завершающие пробелы) или SIZE_MAX в случае ошибки.
*/
size_t jsonArrDec(
	json_elem_t elems[],	/*!< [out] элементы */
	size_t* elems_count,	/*!< [out] число элементов */
	const char json[],		/*!< [in] код */
	size_t count			/*!< [in] длина кода */
);

/*!	\brief Кодирование по форматной строке

	По форматной строке fmt и аргументам ... в буфере [size]json формируется
	JSON-код и возвращается его длина. Код размещается с начала буфера и 
	завершается символом '\0'. Завершающий '\0' не учитывается в возвращеемой 
	длине кода, но должен учитываться во входном размере буфера size. 
	При передаче нулевого json и (или) нулевого size код не формируется, но 
	его длина все равно возвращается.
	\pre Строка fmt корректна.
	\expect Строка fmt и аргументы дают корректный код.
	\return Длина кода или SIZE_MAX в случае ошибки.
	\remark Ошибкой считается ситуация, когда код не умещается в буфере,
	т.е. его длина count >= size.
	\remark Корректность кода можно проверить с помощью jsonIsValid().
*/
size_t jsonFmtEnc(
	char json[],			/*!< [out] буфер для кода */
	size_t size,			/*!< [out] объем буфера */
	const char* fmt,		/*!< [in] форматная строка */
	...						/*!< [in] аргументы */
);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* __BEE2_JSON_H */
