/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-05-05     Bernard      Implement vnode in dfs v2.0
 */

#include <dfs_file.h>
#include <dfs_mnt.h>
#ifdef RT_USING_PAGECACHE
#include "dfs_pcache.h"
#endif

#define DBG_TAG    "DFS.vnode"
#define DBG_LVL    DBG_WARNING
#include <rtdbg.h>

int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
{
    if (vnode)
    {
        rt_memset(vnode, 0, sizeof(struct dfs_vnode));

        vnode->type = type;
        rt_atomic_store(&(vnode->ref_count), 1);
        vnode->mnt = RT_NULL;
        vnode->fops = fops;
    }

    return 0;
}

struct dfs_vnode *dfs_vnode_create(void)
{
    struct dfs_vnode *vnode = rt_calloc(1, sizeof(struct dfs_vnode));
    if (!vnode)
    {
        LOG_E("create a vnode failed.");
        return RT_NULL;
    }

    rt_atomic_store(&(vnode->ref_count), 1);

    LOG_I("create a vnode: %p", vnode);

    return vnode;
}

int dfs_vnode_destroy(struct dfs_vnode* vnode)
{
    rt_err_t ret = RT_EOK;

    if (vnode)
    {
        ret = dfs_file_lock();
        if (ret == RT_EOK)
        {
            if (rt_atomic_load(&(vnode->ref_count)) == 1)
            {
                LOG_I("free a vnode: %p", vnode);
#ifdef RT_USING_PAGECACHE
                if (vnode->aspace)
                {
                    dfs_aspace_destroy(vnode->aspace);
                }
#endif
                if (vnode->mnt)
                {
                    DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
                    vnode->mnt->fs_ops->free_vnode(vnode);
                }
                else
                {
                    DLOG(msg, "vnode", "vnode", DLOG_MSG, "destroy vnode(mnt=NULL)");
                }

                dfs_file_unlock();

                rt_free(vnode);
            }
            else
            {
                dfs_file_unlock();
            }
        }
    }

    return 0;
}

struct dfs_vnode *dfs_vnode_ref(struct dfs_vnode *vnode)
{
    if (vnode)
    {
        rt_atomic_add(&(vnode->ref_count), 1);

        DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
    }

    return vnode;
}

void dfs_vnode_unref(struct dfs_vnode *vnode)
{
    rt_err_t ret = RT_EOK;

    if (vnode)
    {
        ret = dfs_file_lock();
        if (ret == RT_EOK)
        {
            rt_atomic_sub(&(vnode->ref_count), 1);
            DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
#ifdef RT_USING_PAGECACHE
            if (vnode->aspace)
            {
                dfs_aspace_destroy(vnode->aspace);
            }
#endif
            if (rt_atomic_load(&(vnode->ref_count)) == 0)
            {
                LOG_I("free a vnode: %p", vnode);
                DLOG(msg, "vnode", "vnode", DLOG_MSG, "free vnode, ref_count=0");

                if (vnode->mnt)
                {
                    DLOG(msg, "vnode", vnode->mnt->fs_ops->name, DLOG_MSG, "fs_ops->free_vnode");
                    vnode->mnt->fs_ops->free_vnode(vnode);
                }

                dfs_file_unlock();

                rt_free(vnode);
            }
            else
            {
                dfs_file_unlock();
                DLOG(note, "vnode", "vnode ref_count=%d", rt_atomic_load(&(vnode->ref_count)));
            }
        }
    }

    return;
}
