/******************************************************************************
 * hiperiso_json.h
 *
 * Copyright (c) 2021, longpanda <admin@hiperiso.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 */
#ifndef __HIPERISO_JSON_H__
#define __HIPERISO_JSON_H__

#define JSON_NEW_ITEM(pstJson, ret) \
{ \
    (pstJson) = (HISO_JSON *)zalloc(sizeof(HISO_JSON)); \
    if (NULL == (pstJson)) \
    { \
        vdebug("Failed to alloc memory for json."); \
        return (ret); \
    } \
}

#if defined(_MSC_VER) || defined(WIN32)
#define ssprintf(curpos, buf, len, fmt, ...) \
    curpos += scnprintf(buf + curpos, len - curpos, fmt, ##__VA_ARGS__)

#define HISO_JSON_IS_SKIPABLE(c) (((c) <= 32) ? 1 : 0)

#define HISO_JSON_PRINT_PREFIX(uiDepth, ...) \
{ \
    uint32_t _uiLoop = 0; \
    for (_uiLoop = 0; _uiLoop < (uiDepth); _uiLoop++) \
    { \
        ssprintf(uiCurPos, pcBuf, uiBufLen, "    "); \
    } \
    ssprintf(uiCurPos, pcBuf, uiBufLen, ##__VA_ARGS__); \
}
#else

#define ssprintf(curpos, buf, len, fmt, args...) \
    curpos += scnprintf(buf + curpos, len - curpos, fmt, ##args)

#define HISO_JSON_IS_SKIPABLE(c) (((c) <= 32) ? 1 : 0)

#define HISO_JSON_PRINT_PREFIX(uiDepth, args...) \
{ \
    uint32_t _uiLoop = 0; \
    for (_uiLoop = 0; _uiLoop < (uiDepth); _uiLoop++) \
    { \
        ssprintf(uiCurPos, pcBuf, uiBufLen, "    "); \
    } \
    ssprintf(uiCurPos, pcBuf, uiBufLen, ##args); \
}
#endif


#define HISO_JSON_SUCCESS_RET      "{ \"result\" : \"success\" }"
#define HISO_JSON_FAILED_RET       "{ \"result\" : \"failed\" }"
#define HISO_JSON_INVALID_RET      "{ \"result\" : \"invalidfmt\" }"
#define HISO_JSON_TOKEN_ERR_RET    "{ \"result\" : \"tokenerror\" }"
#define HISO_JSON_EXIST_RET        "{ \"result\" : \"exist\" }"
#define HISO_JSON_TIMEOUT_RET      "{ \"result\" : \"timeout\" }"
#define HISO_JSON_DUPLICATE        "{ \"result\" : \"duplicate\" }"
#define HISO_JSON_BUSY_RET         "{ \"result\" : \"busy\" }"
#define HISO_JSON_INUSE_RET        "{ \"result\" : \"inuse\" }"
#define HISO_JSON_NOTFOUND_RET     "{ \"result\" : \"notfound\" }"
#define HISO_JSON_NOTRUNNING_RET   "{ \"result\" : \"notrunning\" }"
#define HISO_JSON_NOT_READY_RET    "{ \"result\" : \"notready\" }"
#define HISO_JSON_NOT_SUPPORT_RET  "{ \"result\" : \"notsupport\" }"
#define HISO_JSON_MBR_2TB_RET      "{ \"result\" : \"mbr2tb\" }"
#define HISO_JSON_INVALID_RSV_RET      "{ \"result\" : \"reserve_invalid\" }"
#define HISO_JSON_FILE_NOT_FOUND_RET     "{ \"result\" : \"file_not_found\" }"

typedef enum tagJSON_TYPE
{
    JSON_TYPE_NUMBER = 0,
    JSON_TYPE_STRING,
    JSON_TYPE_BOOL,
    JSON_TYPE_ARRAY,
    JSON_TYPE_OBJECT,
    JSON_TYPE_NULL,
    JSON_TYPE_BUTT
}JSON_TYPE;

typedef struct tagHISO_JSON
{
    struct tagHISO_JSON *pstPrev;
    struct tagHISO_JSON *pstNext;
    struct tagHISO_JSON *pstChild;

    JSON_TYPE enDataType;
    union 
    {
        char  *pcStrVal;
        int    iNumVal;
        uint64_t lValue;
    }unData;

    char *pcName;
}HISO_JSON;

#define HISO_JSON_FMT_BEGIN(uiCurPos, pcBuf, uiBufLen) \
{\
    uint32_t __uiCurPos = (uiCurPos);\
    uint32_t __uiBufLen = (uiBufLen);\
    char *__pcBuf = (pcBuf);
    
#define HISO_JSON_FMT_END(uiCurPos) \
    (uiCurPos) = __uiCurPos;\
}
    
#define HISO_JSON_FMT_OBJ_BEGIN()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "{")
#define HISO_JSON_FMT_OBJ_BEGIN_L(P)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{", P)
#define HISO_JSON_FMT_OBJ_BEGIN_N()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "{\n")
#define HISO_JSON_FMT_OBJ_BEGIN_LN(P)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{\n", P)
    
#define HISO_JSON_FMT_OBJ_END()  \
{\
    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
    {\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
}

#define HISO_JSON_FMT_OBJ_ENDEX()  \
{\
    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
    {\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "},");\
}


    
#define HISO_JSON_FMT_KEY(Key)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\":", (Key))
#define HISO_JSON_FMT_KEY_L(P, Key)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\":", P, (Key))


#define HISO_JSON_FMT_ITEM(Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\",", (Item))
#define HISO_JSON_FMT_ITEM_L(P, Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\",", P, (Item))
#define HISO_JSON_FMT_ITEM_LN(P, Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\",\n", P, (Item))
#define HISO_JSON_FMT_ITEM_PATH_LN(P, Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\",\n", P, hiperiso_real_path(Item))

#define HISO_JSON_FMT_ITEM_INT(Item)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%d,", (Item))


#define HISO_JSON_FMT_COMA()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",")
#define HISO_JSON_FMT_COMA_N(cnt)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",\n")
#define HISO_JSON_FMT_COMA_N_CNT(cnt) if ((cnt) > 0)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",\n")


#define HISO_JSON_FMT_APPEND_BEGIN() \
{ \
    if ('}' == *(__pcBuf + (__uiCurPos - 1)))\
    {\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, ",");\
}

#define HISO_JSON_FMT_APPEND_END() \
{ \
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "}");\
}

#define HISO_JSON_FMT_ARY_BEGIN()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "[")
#define HISO_JSON_FMT_ARY_BEGIN_N()  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "[\n")
    
#define HISO_JSON_FMT_ARY_END()    \
{\
    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
    {\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "]");\
}
#define HISO_JSON_FMT_ARY_ENDEX()    \
{\
    if (',' == *(__pcBuf + (__uiCurPos - 1)))\
    {\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "],");\
}



#define HISO_JSON_FMT_OBJ_END_L(P)  \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s}%s", P);\
}
#define HISO_JSON_FMT_OBJ_ENDEX_L(P)  \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s},%s", P);\
}

#define HISO_JSON_FMT_OBJ_END_LN(P)  \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s}\n", P);\
}
#define HISO_JSON_FMT_OBJ_ENDEX_LN(P)  \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s},\n", P);\
}




#define HISO_JSON_FMT_ARY_END_L(P)    \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s]", P);\
}

#define HISO_JSON_FMT_ARY_END_LN(P)    \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s]\n", P);\
}

#define HISO_JSON_FMT_ARY_ENDEX_L(P)    \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s],", P);\
}
    

#define HISO_JSON_FMT_ARY_ENDEX_LN(P)    \
{\
    if ('\n' == *(__pcBuf + (__uiCurPos - 1)) && ',' == *(__pcBuf + (__uiCurPos - 2)))\
    {\
        *(__pcBuf + (__uiCurPos - 2)) = '\n';\
        __uiCurPos -= 1;\
    }\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s],\n", P);\
}



    
#define HISO_JSON_FMT_UINT64(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %llu,", Key, (_ull)Val)

#define HISO_JSON_FMT_ULONG(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %lu,", Key, Val)
#define HISO_JSON_FMT_LONG(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %ld,", Key, Val)

#define HISO_JSON_FMT_UINT(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %u,", Key, Val)
#define HISO_JSON_FMT_UINT_N(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %u,\n", Key, Val)
#define HISO_JSON_FMT_UINT_L(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": %u,", P, Key, Val)
#define HISO_JSON_FMT_UINT_LN(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": %u,\n", P, Key, Val)


#define HISO_JSON_FMT_STRUINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%u\",", Key, Val)
#define HISO_JSON_FMT_STRUINT_N(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%u\",\n", Key, Val)
#define HISO_JSON_FMT_STRUINT_L(P, Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%u\",", P, Key, Val)
#define HISO_JSON_FMT_STRUINT_LN(P, Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%u\",\n", P, Key, Val)
    
#define HISO_JSON_FMT_STRSINT(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%d\",", Key, Val)
#define HISO_JSON_FMT_STRSINT_N(Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%d\",\n", Key, Val)
#define HISO_JSON_FMT_STRSINT_L(P, Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%d\",", P, Key, Val)
#define HISO_JSON_FMT_STRSINT_LN(P, Key, Val) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%d\",\n", P, Key, Val)

#define HISO_JSON_FMT_CTRL_INT(Prefix, Key, Field)  \
    if (def->Field != data->Field) \
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{ \"%s\": \"%d\" },\n", Prefix, Key, data->Field)

#define HISO_JSON_FMT_CTRL_STRN(P, Key, Field) \
    if (strcmp(def->Field, data->Field)) \
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{ \"%s\": \"%s\" },\n", P, Key, data->Field) 
        
#define HISO_JSON_FMT_CTRL_STRN_STR(P, Key, ptr) \
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{ \"%s\": \"%s\" },\n", P, Key, ptr)
        
#define HISO_JSON_FMT_CTRL_PUB_STRN(P, Key, Field) \
    if (strcmp(def->Field, g_pub_path)) \
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s{ \"%s\": \"%s\" },\n", P, Key, g_pub_path) 


#define HISO_JSON_FMT_DIFF_STRN(P, Key, Field) \
    if (strcmp(def->Field, data->Field)) \
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%s\",\n", P, Key, data->Field) 


#define HISO_JSON_FMT_STRINT64(Key, Val)  \
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%llu\",", Key, Val)

#define HISO_JSON_FMT_SINT(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %d,", Key, Val)
#define HISO_JSON_FMT_SINT_N(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %d,\n", Key, Val)
#define HISO_JSON_FMT_SINT_L(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": %d,", P, Key, Val)
#define HISO_JSON_FMT_SINT_LN(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": %d,\n", P, Key, Val)
    
#define HISO_JSON_FMT_DUBL(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %.1lf,", Key, Val)
#define HISO_JSON_FMT_DUBL2(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": %10.02lf,", Key, Val)

#define HISO_JSON_FMT_STRN(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%s\",", Key, Val) 
#define HISO_JSON_FMT_STRN_N(Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": \"%s\",\n", Key, Val) 
#define HISO_JSON_FMT_STRN_L(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%s\",", P, Key, Val) 
#define HISO_JSON_FMT_STRN_LN(P, Key, Val)  ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%s\",\n", P, Key, Val) 
#define HISO_JSON_FMT_STRN_PATH_LN(P, Key, Val)  \
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": \"%s\",\n", P, Key, hiperiso_real_path(Val))

int hiso_json_escape_string(char *buf, int buflen, const char *str, int newline);

#define HISO_JSON_FMT_STRN_EX_LN(P, Key, Val)  \
{\
    ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "%s\"%s\": ", P, Key);\
    __uiCurPos += hiso_json_escape_string(__pcBuf + __uiCurPos, __uiBufLen - __uiCurPos, Val, 1);\
}

    
#define HISO_JSON_FMT_NULL(Key)       ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": null,", Key)

#define HISO_JSON_FMT_TRUE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": true,", (Key))
#define HISO_JSON_FMT_FALSE(Key) ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": false,", (Key))

#define HISO_JSON_FMT_BOOL(Key, Val)  \
{\
    if (0 == (Val))\
    {\
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": false,", (Key));\
    }\
    else \
    {\
        ssprintf(__uiCurPos, __pcBuf, __uiBufLen, "\"%s\": true,", (Key));\
    }\
}

typedef struct tagHISO_JSON_PARSE
{
    char *pcKey;
    void *pDataBuf;
    uint32_t  uiBufSize;
}HISO_JSON_PARSE_S;

#define JSON_SUCCESS    0
#define JSON_FAILED     1
#define JSON_NOT_FOUND  2

int hiso_json_parse_value
(
    char *pcNewStart,
    char *pcRawStart,
    HISO_JSON *pstJson, 
    const char *pcData,
    const char **ppcEnd
);
HISO_JSON * hiso_json_create(void);
int hiso_json_parse(HISO_JSON *pstJson, const char *szJsonData);
int hiso_json_parse_ex(HISO_JSON *pstJson, const char *szJsonData, int szLen);
int hiso_json_destroy(HISO_JSON *pstJson);
HISO_JSON *hiso_json_find_item
(
    HISO_JSON *pstJson,
    JSON_TYPE  enDataType,
    const char *szKey
);
int hiso_json_scan_parse
(
    const HISO_JSON    *pstJson,
    uint32_t            uiParseNum,
    HISO_JSON_PARSE_S  *pstJsonParse
);
int hiso_json_get_int
(
    HISO_JSON *pstJson, 
    const char *szKey, 
    int *piValue
);
int hiso_json_get_uint
(
    HISO_JSON *pstJson, 
    const char *szKey, 
    uint32_t *puiValue
);
int hiso_json_get_uint64
(
    HISO_JSON *pstJson, 
    const char *szKey, 
    uint64_t *pui64Value
);
int hiso_json_get_bool
(
    HISO_JSON *pstJson,
    const char *szKey, 
    uint8_t *pbValue
);
int hiso_json_get_string
(
     HISO_JSON *pstJson, 
     const char *szKey, 
     uint32_t  uiBufLen,
     char *pcBuf
);
const char * hiso_json_get_string_ex(HISO_JSON *pstJson,  const char *szKey);

#endif /* __HIPERISO_JSON_H__ */

