#ifndef INCLUDED_BOBCAT_FILESYSTEM_
#define INCLUDED_BOBCAT_FILESYSTEM_

#include <istream>
#include <ostream>
#include <compare>
#include <chrono>

#include <bobcat/ranger>
#include <bobcat/fsoptions>

namespace FBB
{

class DateTime;

template <typename Iterator>
class Ranger;

struct FileSystem: public FS
{
                                    // sets d_ec = 0
    friend std::istream &operator>>(std::istream &in,           // opextract
                                    FileSystem &rhs); 

    friend std::ostream &operator<<(std::ostream &out,                  // .f
                                    FileSystem const &rhs);

        // note: both must exists or an exception is thrown
    friend bool operator==(FileSystem const &lhs,                       // .f
                           FileSystem const &rhs);   

    using EC            = std::error_code;
    using FileStatus    = std::filesystem::file_status;
    using Path          = std::filesystem::path;
    using Iter          = Path::iterator;
    using ConstIter     = Path::const_iterator;
    using DirEntry      = std::filesystem::directory_entry;
    using DirIter       = std::filesystem::directory_iterator;
    using RecursiveIter = std::filesystem::recursive_directory_iterator;
    using CopyOpts      = std::filesystem::copy_options;
    using Perms         = std::filesystem::perms;
    using PermOpts      = std::filesystem::perm_options;
    using FileType      = std::filesystem::file_type;
    using SystemClock   = std::chrono::system_clock;
    using FileClock     = std::chrono::file_clock;

    private:
        mutable EC *d_ec = 0;                   // 0 if no EC is used.
        Path d_path;
    
        static EC s_errorCode;
    
    public:
        FileSystem() = default;

        FileSystem(Path const &path, bool useEC = true);            // 1
        FileSystem(Path const &path, EC &ec);                       // 2

        FileSystem(Path &&tmp, bool useEC = true);                  // 3
        FileSystem(Path &&tmp, EC &ec);                             // 4

        void swap(FileSystem &other);

        void clear();                                               // .f

        template <typename Type>                                    // .f
        FileSystem &operator/=(Type const &arg);
    
        FileSystem &operator/=(FileSystem const &arg);              // .f

        template <typename Type>                                    // .f
        FileSystem &operator+=(Type const &arg);

        FileSystem &operator+=(FileSystem const &arg);              // .f


                                        // if (not fs().absolute())
                                        // or if (not fs(ec).absolute())...
        FileSystem &operator()(EC &ec = s_errorCode);               // .f
        FileSystem const &operator()(EC &ec = s_errorCode) const;   // .f
        FileSystem &noEC();                                         // .f
        FileSystem const &noEC() const;                             // .f

        static std::error_code &errorCode();                        // .f
    
        char const *c_str() const;                                  // .f
        Path const &path() const;                                   // .f
        std::string string() const;                                 // .f

        std::string extension() const;                              // .f
        FileSystem &setExtension(std::string const &ext);           // .f
        bool hasExtension() const;                                  // .f
    
        std::string filename() const;                               // .f
        FileSystem &setFilename(std::string const &newName);        // .f
        bool hasFilename() const;                                   // .f
    
        FileSystem stem() const;                                    // .f
        FileSystem parent() const;                                  // .f
                                        
        FileSystem relative() const;    // drops a starting '/'     // .f
    
        Ranger<Iter> range();                                       // .f
        Ranger<ConstIter> range() const;                            // .f
    
        FileSystem absolute() const;                                // .f
        FileSystem canonical() const;                               // .f
    
        bool isAbsolute() const;                                    // .f
        bool isRelative() const;                                    // .f
    
                                                                    // .f
        bool copy(FileSystem const &dest, FSOptions cpOptions = DEFAULT);
        bool copy(Path const &dest, FSOptions cpOptions = DEFAULT);
    
        bool mkDir() const;     // the parent must exist            // .f
        bool mkDir(Path const &reference) const;                    // .f  
        bool mkDir(FileSystem const &reference) const;              // .f
    
        bool mkDirs() const;    // constructs all subdirs           // .f
    
        static FileSystem cwd();                                    // .f
        static FileSystem cwd(EC &ec);                              // .f

        FileSystem &setCwd();                                       // 1
        static FileSystem setCwd(Path const &path);                 // 2
        static FileSystem setCwd(Path const &path, EC &ec);         // 3
    
        bool sameAs(FileSystem const &other) const; // equivalent   // .f
        bool sameAs(Path const &other) const;                       // .f
    
        bool exists() const;                                        // .f
        static bool exists(FileStatus status);                          // .f

                //throws if not existing
        uintmax_t size() const;                                     // .f
        uintmax_t count() const;       // # hard link count         // .f
    
                                                // last_write_time      
        FileClock::time_point fcModification() const;               // .f
        SystemClock::time_point modification() const;               // .f
    
        bool setModification(SystemClock::time_point const &time);  // 1.cc
        bool setModification(FBB::DateTime const &time);            // 2.cc

            // the calling object must refer to a simlink    
        FileSystem destination() const; // symlink's destination    // .f
    
        bool remove() const;                                        // .f
        std::uintmax_t removeAll() const;                           // .f
    
        bool rename(FileSystem const &newName) const;               // .f
        bool rename(Path const &newName) const;                     // .f
    
        bool resize(std::uintmax_t size) const;                     // .f
    
        static FileSystem tmpDir(bool ec = true);                   // tmpdir
        static FileSystem tmpDir(EC &ec);                           // .f
    
            // space, system_complete: maybe?
    
        FileType type(bool destination = true) const;               // .f

                                      // true/false: cf. status()
            // unclear how this works: returns fallse if the type doesn't
            // match, but nothing is changed
        bool setType(FileType type, bool destination = true);
    
        FileStatus status(bool destination = true) const;

            // Only returns false with file_status{}.
            // More useful: check FileStatus for file_type::not_found or
            //              file_type::unknown
        bool knownStatus() const;  
                // Koenig: no ns spec needed for is_*
                // bool is_WHATEVER(file_status status)
                // bool is_WHATEVER(path const path &entry [, error_code &ec])
    
                            // retrieve permissions
        Perms permissions() const;                                  // .f
    
        template <typename PermType>
        FileSystem const &setPermissions(PermType perms,            // .f
                                         FSOptions opt = RESET) const;

        template <typename PermType>
        FileSystem &setPermissions(PermType perms,                  // .f
                                   FSOptions opt = RESET);
    
        DirEntry directory() const;                                 // .f
    
            // returns directory_entry objects, only the current dir
        Ranger<DirIter> dirRange() const;                           // 1.cc

            // returns directory_iterators to all (recursive) entries
        Ranger<RecursiveIter> recursiveRange() const;               // 2.cc
        
    private:
        template <typename IterType>
        Ranger<IterType> dirRange() const;

        FileSystem &set(EC *ptr) const;     // backdoor (for op()() members)

        template <typename ReturnType>
        ReturnType optEC1(ReturnType (*fun1)(Path const &),         // .f
                          ReturnType (*fun2)(Path const &, EC &) ) const;

        bool optEC2(Path const &dest,
                    bool (*fun1)(Path const &, Path const &),       // 2.cc
                    bool (*fun2)(Path const &, Path const &, EC &) ) const;

        bool optEC3(Path const &dest,
                    void (*fun1)(Path const &, Path const &),       // 3.cc
                    void (*fun2)(Path const &, Path const &, EC &) ) const;

            // private backdoor for setPermissions
        FileSystem &setPerms(Perms perms, FSOptions opt) const;

        bool hardLink(Path const &dest) const;                      // .ih
        bool symLink(Path const &dest) const;                       // .ih
        bool cpSymLink(Path const &dest) const;                     // .ih
        bool copyFile(Path const &dest, unsigned options) const;
        bool fsCopy(Path const &dest, unsigned options) const;
};

#include "filesystem.f"

} // FBB        


#endif


