#include "fs/tmpfs/FileSystem.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/tmpfs/Inode.h"
#include <luna/Alloc.h>
#include <luna/CString.h>
#include <luna/Ignore.h>

namespace TmpFS
{
    Result<SharedPtr<VFS::FileSystem>> FileSystem::create()
    {
        SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
        SharedPtr<VFS::Inode> root = TRY(fs->create_dir_inode({}));
        root->chmod(0755);
        fs->set_root(root);
        return (SharedPtr<VFS::FileSystem>)fs;
    }

    Result<SharedPtr<VFS::Inode>> FileSystem::create_file_inode()
    {
        SharedPtr<FileInode> inode = TRY(make_shared<FileInode>());
        inode->set_fs(*this, {});
        inode->set_inode_number(m_next_inode_number++, {});
        return (SharedPtr<VFS::Inode>)inode;
    }

    Result<SharedPtr<VFS::Inode>> FileSystem::create_symlink_inode(StringView link)
    {
        SharedPtr<SymlinkInode> inode = TRY(make_shared<SymlinkInode>());
        inode->set_fs(*this, {});
        TRY(inode->set_link(link, {}));
        inode->set_inode_number(m_next_inode_number++, {});
        return (SharedPtr<VFS::Inode>)inode;
    }

    Result<SharedPtr<VFS::Inode>> FileSystem::create_dir_inode(SharedPtr<VFS::Inode> parent)
    {
        SharedPtr<DirInode> inode = TRY(make_shared<DirInode>());

        TRY(inode->add_entry(inode, "."));
        TRY(inode->add_entry(parent ? parent : (SharedPtr<VFS::Inode>)inode, ".."));

        inode->set_self(inode, {});
        inode->set_fs(*this, {});
        inode->set_inode_number(m_next_inode_number++, {});

        return (SharedPtr<VFS::Inode>)inode;
    }

    Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
    {
        SharedPtr<Device> device = TRY(DeviceRegistry::fetch_special_device(major, minor));

        SharedPtr<DeviceInode> inode = TRY(make_shared<DeviceInode>());
        inode->set_fs(*this, {});
        inode->set_inode_number(m_next_inode_number++, {});
        inode->set_device(device, {});
        // FIXME: This should be queried from Device directly, but Device doesn't have an API to store and retrieve its
        // device ID atm.
        inode->set_device_id(luna_dev_makedev(major, minor), {});

        return (SharedPtr<VFS::Inode>)inode;
    }

    FileSystem::FileSystem()
    {
        m_host_device_id = DeviceRegistry::next_null_device_id();
    }

    Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
    {
        return m_root_inode->replace_entry(parent, "..");
    }

    void FileSystem::set_root(SharedPtr<VFS::Inode> root)
    {
        m_root_inode = root;
    }
}