/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*  openUBMC 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.
*/

#include <mtd/mtd-abi.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <secure/securec.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <glib-2.0/glib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "utils/file_securec.h"

#include "logging.h"

#define MAX_LENGTH (100 * 1024 * 1024)

static int l_open_mtd(lua_State *L)
{
    size_t file_length;
    const char *file_path = (const char *)luaL_checklstring(L, 1, &file_length);
    gint32 flags = luaL_checkinteger(L, 2);
    gint32 fd = open_s(file_path, flags, 0, NULL);
    if (fd < 0) {
        debug_log(DLOG_ERROR, "open mtd failed errno:%d", errno);
    }
    lua_pushinteger(L, fd);
    return 1;
}

static int l_close_mtd(lua_State *L)
{
    gint32 dev_fd = luaL_checkinteger(L, 1);
    close_s(dev_fd);
    return 0;
}

static int l_get_mtd_meminfo(lua_State *L)
{
    struct mtd_info_user mtd;
    gint32 dev_fd = luaL_checkinteger(L, 1);
    if (ioctl(dev_fd, MEMGETINFO, &mtd) < 0) {
        debug_log(DLOG_ERROR, "get mem info failed errno:%d", errno);
        lua_pushinteger(L, -1);
        return 1;
    }
    lua_pushinteger(L, mtd.size);
    return 1;
}


static int l_erase_mtd(lua_State *L)
{
    struct erase_info_user erase;
    gint32 res;
    gint32 dev_fd = luaL_checkinteger(L, 1);
    guint32 start = luaL_checkinteger(L, 2);
    guint32 length = luaL_checkinteger(L, 3);
    erase.start = start;
    erase.length = length;
    res = ioctl(dev_fd, MEMERASE, &erase);
    lua_pushinteger(L, res);
    return 1;
}

static int l_read_mtd(lua_State *L)
{
    gint32 dev_fd = luaL_checkinteger(L, 1);
    gint32 offset = luaL_checkinteger(L, 2);
    gint32 length = luaL_checkinteger(L, 3);
    lseek(dev_fd, offset, 0);
    if (length < 0 || length > MAX_LENGTH) {
        return 0;
    }
    unsigned char *buf = g_malloc0(length);
    ssize_t result;
    result = read(dev_fd, buf, length);
    if (result < 0) {
        debug_log(DLOG_ERROR, "read data from flash failed errno:%d", errno);
    }
    lua_pushlstring(L, (const char *)buf, length);
    lua_pushinteger(L, result);
    g_free(buf);
    return 2;  // lua接收2个返回值
}

static int l_write_mtd(lua_State *L)
{
    gint32 dev_fd = luaL_checkinteger(L, 1);
    gint32 offset = luaL_checkinteger(L, 2);
    size_t file_length;
    const char *data = (const char *)luaL_checklstring(L, 3, &file_length);
    gint32 length = luaL_checkinteger(L, 4);
    lseek(dev_fd, offset, 0);
    ssize_t result;
    result = write(dev_fd, data, length);
    lua_pushinteger(L, result);
    return 1;
}

LUAMOD_API int luaopen_mtd_drv(lua_State *L)
{
    luaL_checkversion(L);
    luaL_Reg mtd_methods[] = {
        {"close", l_close_mtd},
        {"open", l_open_mtd},
        {"read", l_read_mtd},
        {"write", l_write_mtd},
        {"get_meminfo", l_get_mtd_meminfo},
        {"erase", l_erase_mtd},
        {NULL, NULL}
    };
    luaL_newlib(L, mtd_methods);
    return 1;
}
