/*
 * Copyright (c) 2022 Huawei Technologies Co.,Ltd.
 *
 * CM is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * cm_text.cpp
 *
 * IDENTIFICATION
 *    src/cm_common/cm_text.cpp
 *
 * -------------------------------------------------------------------------
 */
#include <ctype.h>
#include <climits>
#include "cm_text.h"

#include "securec.h"
#include "cm_debug.h"
#include "cm_error.h"

#include "cm_elog.h"
#include "cm_config.h"

bool8 IsCmBracketText(const text_t *text)
{
    bool8 inString = CM_FALSE;
    uint32 depth;
    const int minLen = 2;

    if (text->len < minLen) {
        return CM_FALSE;
    }

    bool8 flag = (bool8)(CM_TEXT_BEGIN(text) != '(' || CM_TEXT_END(text) != ')');
    if (flag) {
        return CM_FALSE;
    }

    depth = 1;
    for (uint32 i = 1; i < text->len; i++) {
        if (text->str[i] == '\'') {
            inString = (bool8)(!inString);
            continue;
        }

        if (inString) {
            continue;
        } else if (text->str[i] == '(') {
            depth++;
        } else if (text->str[i] == ')') {
            depth--;
            if (depth == 0) {
                return (bool8)(i == text->len - 1);
            }
        }
    }

    return CM_FALSE;
}

bool8 IsCmSquareBracketText(const text_t *text)
{
    bool8 inString = CM_FALSE;
    uint32 depth;
    const int minLen = 2;

    if (text->len < minLen) {
        return CM_FALSE;
    }

    bool8 flag = (bool8)(CM_TEXT_BEGIN(text) != '[' || CM_TEXT_END(text) != ']');
    if (flag) {
        return CM_FALSE;
    }

    depth = 1;
    for (uint32 i = 1; i < text->len; i++) {
        if (text->str[i] == '\'') {
            inString = (bool8)(!inString);
            continue;
        }

        if (inString) {
            continue;
        } else if (text->str[i] == '[') {
            depth++;
        } else if (text->str[i] == ']') {
            depth--;
            if (depth == 0) {
                return (bool8)(i == text->len - 1);
            }
        }
    }

    return CM_FALSE;
}

void CmRtrimText(text_t *text)
{
    int32 index;

    if (text->str == NULL) {
        text->len = 0;
        return;
    } else if (text->len == 0) {
        return;
    }

    index = (int32)text->len - 1;
    while (index >= 0) {
        if ((unsigned char)text->str[index] > (unsigned char)' ') {
            text->len = (uint32)(index + 1);
            return;
        }

        --index;
    }
}

void CmLtrimText(text_t *text)
{
    if (text->str == NULL) {
        text->len = 0;
        return;
    } else if (text->len == 0) {
        return;
    }

    while (text->len > 0) {
        if ((unsigned char)*text->str > ' ') {
            break;
        }
        text->str++;
        text->len--;
    }
}

void CmTrimText(text_t *text)
{
    CmLtrimText(text);
    CmRtrimText(text);
}

static void CmRTrimStr(char *str)
{
    int32 strLen = (int32)strlen(str);
    int32 i = strLen - 1;
    for (; i >= 0; --i) {
        if (!isspace(str[i])) {
            if (i < strLen - 1) {
                str[i + 1] = '\0';
            }
            break;
        }
    }
}

static void CmLTrimStr(char *str)
{
    uint32 index = 0;
    uint32 strLen = (uint32)strlen(str);
    for (uint32 i = 0; i < strLen; ++i) {
        if (isspace(str[i])) {
            ++index;
        } else {
            break;
        }
    }
    if (index == 0) {
        return;
    }
    char *tempStr = str + index;
    uint32 curLen = (uint32)strlen(tempStr);
    if (curLen == 0) {
        str[0] = '\0';
        return;
    }
    errno_t rc = memmove_s(str, strlen(str), tempStr, curLen);
    securec_check_errno(rc, (void)rc);
    str[curLen] = '\0';
}

void CmTrimStr(char *str)
{
    if (CM_IS_EMPTY_STR(str)) {
        return;
    }
    CmRTrimStr(str);
    CmLTrimStr(str);
}

void CmRemoveBrackets(text_t *text)
{
    const int lenReduce = 2;
    while (IsCmBracketText(text)) {
        text->str++;
        text->len -= lenReduce;
        CmTrimText(text);
    }
}

void CmRemoveSquareBrackets(text_t *text)
{
    const int lenReduce = 2;
    while (IsCmSquareBracketText(text)) {
        text->str++;
        text->len -= lenReduce;
        CmTrimText(text);
    }
}

void CmSplitText(const text_t *text, char splitChar, char encloseChar, text_t *left, text_t *right)
{
    uint32 i;
    bool8 isEnclosed = CM_FALSE;

    left->str = text->str;

    for (i = 0; i < text->len; i++) {
        if (encloseChar != 0 && text->str[i] == encloseChar) {
            isEnclosed = (bool8)(!isEnclosed);
            continue;
        }

        if (isEnclosed) {
            continue;
        }

        if (text->str[i] == splitChar) {
            left->len = i;
            right->str = text->str + i + 1;
            right->len = text->len - (i + 1);
            return;
        }
    }
    /* if the split_char is not found */
    left->len = text->len;
    right->len = 0;
    right->str = NULL;
}

bool8 CmFetchText(text_t *text, char splitChar, char encloseChar, text_t *sub)
{
    text_t remain;
    if (text->len == 0) {
        CM_TEXT_CLEAR(sub);
        return CM_FALSE;
    }

    CmSplitText(text, splitChar, encloseChar, sub, &remain);

    text->len = remain.len;
    text->str = remain.str;
    return CM_TRUE;
}

bool8 CmTextStrEqualIns(const text_t *text, const char *str)
{
    uint32 i;

    for (i = 0; i < text->len; i++) {
        if (UPPER(text->str[i]) != UPPER(str[i]) || str[i] == '\0') {
            return CM_FALSE;
        }
    }

    return (bool8)(str[text->len] == '\0');
}

void CmFetchFileName(text_t *files, text_t *name)
{
    if (!CmFetchText(files, ',', '\0', name)) {
        return;
    }

    CmTrimText(name);
    const uint32 quotaionLen = 2;
    if (name->str[0] == '\'') {
        name->str++;
        if (name->len >= quotaionLen) {
            name->len -= quotaionLen;
        } else {
            name->len = 0;
        }

        CmTrimText(name);
    }
}

status_t CmText2Str(const text_t *text, char *buf, uint32 bufSize)
{
    if (buf == NULL) {
        return CM_ERROR;
    }
    uint32 copy_size;
    CM_ASSERT(bufSize > 1);
    copy_size = (text->len >= bufSize) ? bufSize - 1 : text->len;
    if (copy_size > 0) {
        int res = memcpy_s(buf, bufSize, text->str, copy_size);
        if (res != 0) {
            return CM_ERROR;
        }
    }

    buf[copy_size] = '\0';
    return CM_SUCCESS;
}

status_t CmText2Uint16(const text_t *textSrc, uint16 *value)
{
    char buf[CM_MAX_NUMBER_LENGTH + 1] = {0};
    text_t text = *textSrc;

    CmTrimText(&text);

    if (text.len > CM_MAX_NUMBER_LENGTH) {
        write_runlog(ERROR,
            "[%s] Convert uint16 failed,the length of text %u can't be larger than %u.\n",
            __FUNCTION__,
            text.len,
            CM_MAX_NUMBER_LENGTH);
        return CM_ERROR;
    }
    CM_RETURN_IFERR(CmText2Str(&text, buf, CM_MAX_NUMBER_LENGTH + 1));

    return CmStr2Uint16(buf, value);
}

status_t CmStr2Uint16(const char *str, uint16 *value)
{
    char *err = NULL;
    int ret = CmCheckIsNumber(str);
    if (ret != CM_SUCCESS) {
        write_runlog(ERROR,
            "[%s] Convert uint16 failed, the text is not number, text = %s.\n", __FUNCTION__, str);
        return CM_ERROR;
    }

    int64_t valInt64 = strtol(str, &err, CM_DEFAULT_DIGIT_RADIX);
    if (CmIsErr(err)) {
        write_runlog(ERROR, "[%s] Convert uint32 failed, text = %s.\n", __FUNCTION__, str);
        return CM_ERROR;
    }

    if (valInt64 > UINT_MAX || valInt64 < 0) {
        write_runlog(ERROR,
            "[%s] Convert uint32 failed, the text is not in the range of uint32, text = %s.\n",
            __FUNCTION__, str);
        return CM_ERROR;
    }

    *value = (uint32)valInt64;
    return CM_SUCCESS;
}

status_t CmCheckIsNumber(const char *str)
{
    size_t len = strlen(str);
    if (len == 0) {
        return CM_ERROR;
    }

    for (size_t i = 0; i < len; i++) {
        if (!CM_IS_DIGITAL_LETER(str[i])) {
            return CM_ERROR;
        }
    }
    return CM_SUCCESS;
}

bool CmIsErr(const char *err)
{
    if (err == NULL) {
        return false;
    }

    while (*err != '\0') {
        if (*err != ' ') {
            return true;
        }
        err++;
    }
    return false;
}