=encoding utf8

=head1 NAME

Sidef::Types::Glob::DirHandle - Directory handle object for iterating and manipulating directories.

=head1 DESCRIPTION

This class implements a directory handle for reading and navigating directory contents in Sidef. A DirHandle provides an object-oriented interface to directory operations, allowing you to read entries, iterate through files, and manipulate the read position within a directory stream.

DirHandle objects are typically created by calling the C<open> method on a Dir object, which opens the directory for reading and returns a DirHandle instance.

=head1 SYNOPSIS

    # Open a directory and get a DirHandle
    var dir = Dir('/path/to/directory')
    var dh = dir.open

    # Read entries one by one
    while (var entry = dh.read) {
        say entry
    }
    dh.close

    # Or iterate through raw entry names with a block
    Dir('/path/to/directory').open.each { |name|
        say "Found: #{name}"
    }

    # Get all entries (as Dir/File objects) as an array
    var entries = Dir('.').open.entries
    say entries

=head1 INHERITS

Inherits methods from L<Sidef::Object::Object>.

=head1 CONSTRUCTION

=head2 new

    Sidef::Types::Glob::DirHandle.new(dh, dir)

Constructs a new DirHandle object that wraps a raw directory handle (as produced internally by C<opendir>) together with the directory it belongs to. This is a low-level constructor: DirHandle objects are normally obtained by calling C<open> on a L<Sidef::Types::Glob::Dir> object rather than being built directly.

Aliases: I<call>

=cut

=head1 DIRECTORY INFORMATION

=head2 dir

    self.dir

Returns the directory this handle was opened from.

    var dh = Dir('/home/user').open
    var dir = dh.dir        # returns the associated Dir object
    say dir                 # prints: /home/user

Aliases: I<parent>

=cut

=head1 READING ENTRIES

=head2 read

    self.read

Reads and returns the next entry from the directory as a L<Sidef::Types::Glob::Dir> or L<Sidef::Types::Glob::File> object, depending on the entry's type. Returns C<nil> when there are no more entries. Each call advances the directory position to the next entry.

The special entries C<.> and C<..> are automatically skipped, and symbolic links are silently ignored (they are not returned at all).

    var dh = Dir('.').open

    loop {
        var entry = dh.read
        entry || break
        say entry
    }

    dh.close

=cut

=head2 entries

    self.entries

Returns an array containing all the entries in the directory, as L<Sidef::Types::Glob::Dir> and L<Sidef::Types::Glob::File> objects. Internally this rewinds the handle and then repeatedly calls C<read>, so, like C<read>, it excludes the C<.> and C<..> entries as well as symbolic links.

    var dh = Dir('/etc').open
    var all_entries = dh.entries
    say "Found #{all_entries.len} entries"
    say all_entries

This is convenient when you need to work with all entries at once or want to apply array operations like sorting or filtering.

=cut

=head2 files

    self.files

Returns an array containing only the regular files within the directory (i.e., the result of C<entries> filtered down to entries where C<is_file> is true). Subdirectories, symbolic links, and the C<.> / C<..> entries are excluded.

    var dh = Dir('.').open
    var files = dh.files
    files.each { |file|
        say "File: #{file}"
    }

=cut

=head2 dirs

    self.dirs

Returns an array containing only the subdirectories within the directory (i.e., the result of C<entries> filtered down to entries where C<is_dir> is true). Regular files, symbolic links, and the C<.> / C<..> entries are excluded.

    var dh = Dir('.').open
    var subdirs = dh.dirs
    subdirs.each { |subdir|
        say "Subdirectory: #{subdir}"
    }

=cut

=head2 each

    self.each(code)

Iterates through each raw entry in the directory, calling the provided code block once per entry with the entry's name as a plain string. Returns the DirHandle object.

Unlike C<read>, C<entries>, C<files>, and C<dirs>, this method reads directly from the underlying directory handle: it does B<not> skip the C<.> and C<..> entries, and it does B<not> filter out symbolic links.

    Dir('.').open.each { |name|
        say "Entry: #{name}"
    }

=cut

=head2 iter

    self.iter

Returns a callable iterator (a Block) that lazily yields the next raw entry name (as a string) each time it's invoked, returning C<nil> once the directory is exhausted. This is useful for processing large directories without loading all entries into memory at once.

Like C<each>, and unlike C<read>, this reads directly from the underlying directory handle, so it does B<not> skip the C<.> and C<..> entries and does B<not> filter out symbolic links.

    var dh = Dir('.').open
    var iterator = dh.iter

    # Get entries one at a time
    while (var entry = iterator.next) {
        say entry
    }

=cut

=head1 POSITION CONTROL

=head2 tell

    self.tell

Returns the current position within the directory stream as an integer. This position can be used later with C<seek> to return to the same point in the directory.

    var dh = Dir('.').open

    dh.read  # read first entry
    var pos = dh.tell  # get current position
    say "Current position: #{pos}"

The position value is opaque and should only be used with C<seek> on the same directory handle.

=cut

=head2 seek

    self.seek(pos)

Sets the position within the directory stream to the specified position. The position value should be obtained from a previous C<tell> call. Returns a boolean indicating success.

    var dh = Dir('.').open

    dh.read  # read first entry
    var pos = dh.tell  # save position
    dh.read  # read second entry
    dh.read  # read third entry

    dh.seek(pos)  # return to saved position
    say dh.read   # reads second entry again

=cut

=head2 rewind

    self.rewind

Resets the directory position back to the beginning, allowing you to read through the directory entries again from the start. This is useful when you need to make multiple passes through a directory.

    var dh = Dir('.').open

    # First pass
    while (var entry = dh.read) {
        say "First pass: #{entry}"
    }

    # Reset to beginning
    dh.rewind

    # Second pass
    while (var entry = dh.read) {
        say "Second pass: #{entry}"
    }

    dh.close

=cut

=head1 HANDLE MANAGEMENT

=head2 close

    self.close

Closes the directory handle, releasing system resources. After closing, the DirHandle can no longer be used to read directory entries. Returns a boolean indicating success.

    var dh = Dir('.').open
    # ... read some entries ...
    dh.close  # close the handle when done

It's good practice to close directory handles when you're finished with them, although Sidef will automatically close them when they go out of scope.

=cut

=head2 chdir

    self.chdir

Changes the current working directory to the directory represented by this DirHandle. Returns a boolean indicating success or failure.

    var dh = Dir('/tmp').open
    dh.chdir  # changes current directory to /tmp
    say Dir.cwd  # prints: /tmp

=cut

=head1 FILE STATUS

=head2 stat

    self.stat

Returns a L<Sidef::Types::Glob::Stat> object containing filesystem metadata about the directory represented by this handle.

    var dh = Dir('.').open
    var stat = dh.stat
    say "Directory size: #{stat.size}"
    say "Last modified: #{stat.mtime}"
    say "Owner UID: #{stat.uid}"

=cut

=head2 lstat

    self.lstat

Returns a L<Sidef::Types::Glob::Stat> object containing filesystem metadata about the directory represented by this handle, following C<lstat> semantics.

    var dh = Dir('/tmp').open
    var stat = dh.lstat
    say "Directory inode: #{stat.ino}"
    say "Permissions: #{stat.mode.as_oct}"

=cut

=head1 LOW-LEVEL ACCESS

=head2 get_value

    self.get_value

Returns the raw underlying directory handle wrapped by this object. This is a low-level accessor, mainly intended for internal use or interoperability rather than everyday scripting.

=cut

=head1 EXAMPLES

=head2 Basic Directory Reading

    # Read all files in the current directory
    var dh = Dir('.').open
    while (var file = dh.read) {
        say file
    }
    dh.close

=head2 Filtering Directory Contents

    # Find all .txt files
    Dir('.').open.each { |name|
        if (name ~~ /\.txt$/) {
            say "Text file: #{name}"
        }
    }

=head2 Working with Subdirectories

    # List all subdirectories
    var dh = Dir('/home').open
    var subdirs = dh.dirs
    say "Subdirectories: #{subdirs.join(', ')}"

=head2 Recursive Directory Traversal

    func traverse_dir(dir_path) {
        var dh = Dir(dir_path).open

        dh.each { |name|
            next if (name ~~ /^\.\.?$/)  # skip . and ..

            var full_path = Dir(dir_path) + name

            if (full_path.is_dir) {
                say "Directory: #{full_path}"
                traverse_dir(full_path)  # recurse
            } else {
                say "File: #{full_path}"
            }
        }

        dh.close
    }

    traverse_dir('/path/to/start')

=head2 Using Iterator for Large Directories

    # Process large directory lazily
    var iter = Dir('/var/log').open.iter

    while (var entry = iter.next) {
        if (entry ~~ /\.log$/) {
            say "Log file: #{entry}"
        }
    }

=head1 SEE ALSO

L<Sidef::Types::Glob::Dir>, L<Sidef::Types::Glob::File>, L<Sidef::Types::Glob::FileHandle>, L<Sidef::Types::Glob::Stat>

=head1 NOTES

The order in which directory entries are returned is not guaranteed and may vary depending on the underlying filesystem. If you need sorted entries, use the C<entries> method and sort the resulting array.

C<read>, C<entries>, C<files>, and C<dirs> all skip the special C<.> and C<..> entries and silently ignore symbolic links. In contrast, C<each> and C<iter> read directly from the underlying directory handle and include C<.>, C<..>, and symbolic links, returning plain entry-name strings rather than Dir/File objects.

=cut
