/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC 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.
 * -------------------------------------------------------------------------
 *
 * pl_common.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/pl/common/pl_common.c
 *
 * -------------------------------------------------------------------------
 */
#include "pl_common.h"
#include "srv_instance.h"
#include "pl_memory.h"

static inline text_t *plm_get_udo_owner(var_udo_t *udo_obj)
{
    if (udo_obj->pack.len == 0) {
        return &udo_obj->user;
    } else {
        return &udo_obj->pack;
    }
}

status_t pl_unfound_error(sql_stmt_t *stmt, var_udo_t *udo_obj, src_loc_t *loc, uint32 type)
{
    text_t *owner = NULL;
    switch (type) {
        case PL_FUNCTION:
            owner = plm_get_udo_owner(udo_obj);
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "function", T2S(owner), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_PROCEDURE:
            owner = plm_get_udo_owner(udo_obj);
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "procedure", T2S(owner), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_TRIGGER:
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "trigger",
                                   T2S(&udo_obj->user), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_PACKAGE_SPEC:
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "package",
                                   T2S(&udo_obj->user), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_PACKAGE_BODY:
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "package body", T2S(&udo_obj->user),
                T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_TYPE_SPEC:
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "type",
                                   T2S(&udo_obj->user), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        case PL_TYPE_BODY:
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "type body",
                                   T2S(&udo_obj->user), T2S_EX(&udo_obj->name));
            return OG_ERROR;
        default:
            owner = plm_get_udo_owner(udo_obj);
            OG_THROW_ERROR_TRY_SRC(loc, ERR_USER_OBJECT_NOT_EXISTS, "object", T2S(owner), T2S_EX(&udo_obj->name));
            return OG_ERROR;
    }
}

status_t pl_copy_text(void *context, text_t *src, text_t *dst)
{
    if (pl_alloc_mem((void *)context, src->len, (void **)&dst->str) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (src->len != 0) {
        MEMS_RETURN_IFERR(memcpy_s(dst->str, src->len, src->str, src->len));
    }

    dst->len = src->len;

    return OG_SUCCESS;
}

status_t pl_copy_str(void *context, char *src, text_t *dst)
{
    text_t src_text;
    cm_str2text_safe(src, (uint32)strlen(src), &src_text);
    return pl_copy_text(context, &src_text, dst);
}

status_t pl_copy_name(void *context, text_t *src, text_t *dst)
{
    uint32 i;

    if (src->len > OG_MAX_NAME_LEN) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "'%s' is too long to as name", T2S(src));
        return OG_ERROR;
    }

    if (src->len == 0) {
        dst->len = 0;
        return OG_SUCCESS;
    }

    if (pl_alloc_mem(context, src->len, (void **)&dst->str) != OG_SUCCESS) {
        return OG_ERROR;
    }

    dst->len = src->len;

    for (i = 0; i < dst->len; i++) {
        dst->str[i] = UPPER(src->str[i]);
    }

    return OG_SUCCESS;
}

status_t pl_copy_name_cs(void *entity, text_t *src, text_t *dst, bool32 sensitive)
{
    pl_entity_t *context = (pl_entity_t *)entity;

    if (IS_CASE_INSENSITIVE && !sensitive) {
        return pl_copy_name(context, src, dst);
    }

    if (src->len > OG_MAX_NAME_LEN) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "'%s' is too long to as name", T2S(src));
        return OG_ERROR;
    }

    if (src->len == 0) {
        dst->len = 0;
        return OG_SUCCESS;
    }

    return pl_copy_text(context, src, dst);
}

status_t pl_copy_object_name(void *context, word_type_t type, text_t *src, text_t *dst)
{
    if (IS_DQ_STRING(type)) {
        return pl_copy_text(context, src, dst);
    }

    if (IS_CASE_INSENSITIVE) {
        return pl_copy_name(context, src, dst);
    } else {
        return pl_copy_name_cs(context, src, dst, OG_FALSE);
    }
}

static status_t pl_copy_object_name_loc(void *context, word_type_t type, sql_text_t *src, sql_text_t *dst)
{
    dst->loc = src->loc;
    return pl_copy_object_name(context, type, &src->value, &dst->value);
}

status_t pl_copy_object_name_ci(void *context, word_type_t type, text_t *src, text_t *dst)
{
    if (IS_DQ_STRING(type)) {
        return pl_copy_text(context, src, dst);
    }
    return pl_copy_name(context, src, dst);
}

status_t pl_copy_prefix_tenant(void *stmt_in, text_t *src, text_t *dst, pl_copy_func_t pl_copy_func)
{
    text_t name;
    char buf[OG_NAME_BUFFER_SIZE];
    sql_stmt_t *stmt = stmt_in;

    if (IS_CASE_INSENSITIVE) {
        cm_text2str_with_upper(src, buf, OG_NAME_BUFFER_SIZE);
    } else {
        if (cm_text2str(src, buf, OG_NAME_BUFFER_SIZE) != OG_SUCCESS) {
            return OG_ERROR;
        }
    }

    if (sql_user_prefix_tenant(stmt->session, buf) != OG_SUCCESS) {
        return OG_ERROR;
    }

    cm_str2text(buf, &name);
    if (pl_copy_func(stmt->pl_context, &name, dst) != OG_SUCCESS) {
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t pl_copy_name_prefix_tenant_loc(void *stmt_in, sql_text_t *src, sql_text_t *dst)
{
    sql_stmt_t *stmt = (sql_stmt_t *)stmt_in;

    dst->loc = src->loc;
    return pl_copy_prefix_tenant(stmt, &src->value, &dst->value, pl_copy_name);
}

status_t pl_word_as_table(void *stmt_in, word_t *word, var_word_t *var)
{
    sql_stmt_t *stmt = (sql_stmt_t *)stmt_in;
    if (word->ex_count == 0) {
        if (pl_copy_object_name_loc(stmt->pl_context, word->type, &word->text, &var->table.name) != OG_SUCCESS) {
            return OG_ERROR;
        }
        var->table.user.loc = word->text.loc;
        var->table.user.implicit = OG_TRUE;
        text_t user_name = { stmt->session->curr_schema, (uint32)strlen(stmt->session->curr_schema) };
        if (IS_DUAL_TABLE_NAME(&var->table.name.value)) {
            cm_text_upper(&var->table.name.value);
        }

        if (pl_copy_name(stmt->pl_context, &user_name, (text_t *)&var->table.user) != OG_SUCCESS) {
            return OG_ERROR;
        }
    } else if (word->ex_count == 1) {
        if (pl_copy_object_name_loc(stmt->pl_context, word->ex_words[0].type, &word->ex_words[0].text,
            &var->table.name) != OG_SUCCESS) {
            return OG_ERROR;
        }

        if (pl_copy_name_prefix_tenant_loc(stmt, &word->text, &var->table.user) != OG_SUCCESS) {
            return OG_ERROR;
        }
        var->table.user.implicit = OG_FALSE;
        if (cm_text_str_equal_ins(&var->table.user.value, "SYS") && IS_DUAL_TABLE_NAME(&var->table.name.value)) {
            cm_text_upper(&var->table.name.value);
        }
    } else {
        OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR, "invalid table name");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}


status_t pl_decode_object_name(sql_stmt_t *stmt, word_t *word, sql_text_t *user, sql_text_t *name)
{
    var_word_t var_word;

    if (pl_word_as_table(stmt, word, &var_word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    *user = var_word.table.user;
    *name = var_word.table.name;

    return OG_SUCCESS;
}


int g_pl2obj_type_map[] = {
    [PL_PROCEDURE] = OBJ_TYPE_PROCEDURE,
    [PL_FUNCTION] = OBJ_TYPE_FUNCTION,
    [PL_TRIGGER] = OBJ_TYPE_TRIGGER,
    [PL_PACKAGE_SPEC] = OBJ_TYPE_PACKAGE_SPEC,
    [PL_PACKAGE_BODY] = OBJ_TYPE_PACKAGE_BODY,
    [PL_TYPE_SPEC] = OBJ_TYPE_TYPE_SPEC,
    [PL_TYPE_BODY] = OBJ_TYPE_TYPE_BODY,
    [PL_SYNONYM] = OBJ_TYPE_PL_SYNONYM,
    [PL_SYS_PACKAGE] = OBJ_TYPE_SYS_PACKAGE,
};

int g_obj2pl_type_map[] = {
    [OBJ_TYPE_PROCEDURE] = PL_PROCEDURE,
    [OBJ_TYPE_FUNCTION] = PL_FUNCTION,
    [OBJ_TYPE_TRIGGER] = PL_TRIGGER,
    [OBJ_TYPE_PACKAGE_SPEC] = PL_PACKAGE_SPEC,
    [OBJ_TYPE_PACKAGE_BODY] = PL_PACKAGE_BODY,
    [OBJ_TYPE_TYPE_SPEC] = PL_TYPE_SPEC,
    [OBJ_TYPE_TYPE_BODY] = PL_TYPE_BODY,
    [OBJ_TYPE_PL_SYNONYM] = PL_SYNONYM,
    [OBJ_TYPE_SYS_PACKAGE] = PL_SYS_PACKAGE,
};


uint32 pl_get_obj_type(object_type_t obj_type)
{
    return g_obj2pl_type_map[obj_type];
}

object_type_t pltype_to_objtype(uint32 obj_type)
{
    return g_pl2obj_type_map[obj_type];
}