#pragma once
#include "fs/VFS.h"
#include <luna/LinkedList.h>

class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
{
  public:
    static Result<SharedPtr<VFS::Inode>> create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs);

    void set_fs(SharedPtr<VFS::FileSystem> fs)
    {
        m_mountee = fs;
    }

    Result<SharedPtr<VFS::Inode>> find(const char* name) const override
    {
        return m_mount_root_inode->find(name);
    }

    Option<VFS::DirectoryEntry> get(usize index) const override
    {
        return m_mount_root_inode->get(index);
    }

    Result<usize> read(u8*, usize, usize) const override
    {
        return err(EISDIR);
    }

    Result<usize> write(const u8*, usize, usize) override
    {
        return err(EISDIR);
    }

    Result<void> truncate(usize) override
    {
        return err(EISDIR);
    }

    bool will_block_if_read() const override
    {
        return false;
    }

    const VFS::InodeMetadata& metadata() const override
    {
        return m_mount_root_inode->metadata();
    }

    Result<void> set_metadata(const VFS::InodeMetadata& metadata) override
    {
        return m_mount_root_inode->set_metadata(metadata);
    }

    VFS::FileSystem* fs() const override
    {
        return m_mountee.ptr();
    }

    VFS::InodeType type() const override
    {
        return VFS::InodeType::Directory;
    }

    void did_link() override
    {
        m_mount_root_inode->did_link();
    }

    void did_unlink() override
    {
        m_mount_root_inode->did_unlink();
    }

    usize entries() const override
    {
        return m_mount_root_inode->entries();
    }

    bool is_mountpoint() const override
    {
        return true;
    }

    SharedPtr<VFS::Inode> source() const
    {
        return m_source;
    }

    Result<void> remove_entry(const char* name) override
    {
        return m_mount_root_inode->remove_entry(name);
    }

    Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override
    {
        return m_mount_root_inode->create_file(name, mode);
    }

    Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override
    {
        return m_mount_root_inode->create_subdirectory(name, mode);
    }

    Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override
    {
        return m_mount_root_inode->add_entry(inode, name);
    }

    Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name) override
    {
        return m_mount_root_inode->replace_entry(inode, name);
    }

    Result<void> set_source(SharedPtr<VFS::Inode> source)
    {
        if (m_source) m_source->remove_handle();

        m_source = source;

        if (source)
        {
            auto parent = TRY(source->find(".."));
            TRY(m_mountee->set_mount_dir(parent));

            source->add_handle();
        }

        return {};
    }

    virtual ~MountInode();

  private:
    SharedPtr<VFS::Inode> m_source {};
    SharedPtr<VFS::FileSystem> m_mountee;
    SharedPtr<VFS::Inode> m_mount_root_inode;

    MountInode() = default;
};

extern LinkedList<MountInode> g_mounts;