#pragma once

#include <hilbert/kernel/storage.hpp>
#include <hilbert/kernel/utility.hpp>

//TODO: mounts points.
//maybe a two-way map between mount points and targets? one mount point per
//target and vice versa. only directories may be mount points, and only file
//system roots (which must be directories) may be targets.

namespace hilbert::kernel::vfile {

  //a canon path contains no . or empty directory names, and
  //contains no .. except for at the start of a relative path.
  struct canon_path {

    bool absolute;
    unsigned parent_count;
    utility::vector<utility::string> segments;

    canon_path() : absolute(false), parent_count(0), segments(8) {}

    void parent();
    void rel(const canon_path &r);

    utility::string to_string(bool trailing_slash) const;

  };

  void canonize_path(const utility::string &name, canon_path &out);

  struct vfile {

    //on followed symlinks, path is the source of the symlink and the
    //rest of these are the target. in any case, path should be absolute.
    canon_path path;
    storage::block_device *bd;
    storage::dir_entry dir_entry;

    storage::fs_result follow_symlinks(vfile &out) const;

    //dir_entry.type is assumed to be directory.
    storage::fs_result get_child(
      vfile &out, const utility::string &name) const;
    //dir_entry.type is assumed to be directory. out must be empty on entry.
    storage::fs_result get_children(utility::vector<vfile> &out) const;

    //assumes file is a regular file and [start, start + length)
    //is in bounds of file. start and length are in bytes.
    storage::fs_result read_file(
      uint64_t start, uint64_t length, void *into) const;

  };

  //TODO: see comment at top of file.
  void set_root(const vfile &root);

  //path must be absolute. follows symlinks on all but the last node
  //always, and on the last node when follow_final_symlink is true.
  storage::fs_result lookup_path(
    const canon_path &path, vfile &out, bool follow_final_symlink);

}
