#ifndef INCLUDED_BOBCAT_STAT_
#define INCLUDED_BOBCAT_STAT_

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>

#include <bobcat/datetime>
#include <bobcat/gs>
#include <bobcat/exception>

namespace FBB
{

class User;

class Stat: public GS__
{
    struct stat d_stat;
    bool d_errno;
    std::string d_name;

    public:
        using stat =  struct stat;   // Defines Stat::stat for clients

        enum Combine
        {
          ALL,
          ANY,
        };

        enum SpecialMode
        {
          SUID = 04000,
          SGID = 02000,
            SB = 01000,
        };

        enum Mode
        {
            UR =  0400,
            UW =  0200,
            UX =  0100,

            GR =   040,
            GW =   020,
            GX =   010,

            OR =    04,
            OW =    02,
            OX =    01,

          READ =  UR | GR | OR,
         WRITE =  UW | GW | OW,
          EXEC =  UX | GX | OX,

           RWX =  0777,
        };

        enum Lstat
        {
            LStat
        };

        Stat();                                                     // 1.cc
        Stat(Stat const &other) = default;
        Stat(Stat &&tmp);                                           // 2.cc

        explicit Stat(std::string const &name);                     // 3.cc
        Stat(Lstat, std::string const &name);                       // 4.f
        Stat(std::string const &name, std::string const &pathlist); // 5.f
        Stat(Lstat, std::string const &name,                        // 6.f
             std::string const &pathlist);


        Stat &operator=(Stat const &other) = default;
        Stat &operator=(Stat &&tmp);

        bool access(User const &user, size_t mode,
                                       bool useEffective = true) const;
        DateTime lastAccess() const;                                    // .f
        DateTime lastChange() const;                                    // .f
        DateTime lastModification() const;                              // .f
        Type type() const;                                              // .f
        bool isType(Type probe);                                        // .f
        bool mode(size_t mode, Combine combi = ALL) const;

        bool set(std::string const &name);                              // 1.cc
        bool set(Lstat, std::string const &name);                       // 2.f

        bool set(std::string const &name, std::string const &pathlist); // 3.cc
        bool set(Lstat, std::string const &name,                        // 4.f
                 std::string const &pathlist);

        bool specialMode(size_t specialMode, Combine combi = ALL) const;
        off_t size() const;                                             // .f
        operator bool() const;                                          // .f
        size_t blockSize() const;                                       // .f
        size_t device() const;                                          // .f
        size_t deviceType() const;                                      // .f
        size_t error() const;                                           // .f
        size_t gid() const;                                             // .f
        size_t inode() const;                                           // .f
        size_t mode() const;                                            // .f
        size_t nBlocks() const;                                         // .f
        size_t nLinks() const;                                          // .f
        size_t uid() const;                                             // .f
        stat const &statStruct() const;                                 // .f
        std::string const &name();                                      // .f
        std::string modeStr() const;
        std::string path();
        std::string typeStr() const;

    private:
        void init(int (*statFun)(char const *, stat *));
        bool setPath(int (*statFun)(char const *, stat *),
                     std::string const &name, std::string const &pathlist);
        bool set(int (*statFun)(char const *, stat *),                  // 5.cc
                 std::string const &name);
};

inline Stat::Stat(Lstat, std::string const &name, std::string const &pathlist)
{
    setPath(lstat, name, pathlist);
}
inline Stat::Stat(Lstat, std::string const &name)
:
    d_name(name)
{
    init(lstat);
}
inline Stat::Stat(std::string const &name, std::string const &pathlist)
{
    setPath(::stat, name, pathlist);
}
inline size_t Stat::blockSize() const
{
    return d_stat.st_blksize;
}
inline size_t Stat::device() const
{
    return d_stat.st_dev;
}
inline size_t Stat::deviceType() const
{
    return d_stat.st_rdev;
}
inline size_t Stat::error() const
{
    return d_errno;
}
inline size_t Stat::gid() const
{
    return d_stat.st_gid;
}
inline size_t Stat::inode() const
{
    return d_stat.st_ino;
}
inline bool Stat::isType(Type probe)
{
    return type() == probe;
}
inline DateTime Stat::lastAccess() const
{
    return DateTime(d_stat.st_atime, DateTime::UTC);
}
inline DateTime Stat::lastChange() const
{
    return DateTime(d_stat.st_ctime, DateTime::UTC);
}
inline DateTime Stat::lastModification() const
{
    return DateTime(d_stat.st_mtime, DateTime::UTC);
}
inline size_t Stat::mode() const
{
    return d_stat.st_mode & RWX;
}
inline std::string const &Stat::name()
{
    return d_name;
}
inline size_t Stat::nBlocks() const
{
    return d_stat.st_blocks;
}
inline size_t Stat::nLinks() const
{
    return d_stat.st_nlink;
}
inline Stat::operator bool() const
{
    return d_errno == 0;
}
inline bool Stat::set(Lstat, std::string const &name)
{
    return set(lstat, name);
}
inline bool Stat::set(Lstat, std::string const &name,
                      std::string const &pathList)
{
    return setPath(lstat, name, pathList);
}
inline off_t Stat::size() const
{
    return d_stat.st_size;
}
inline Stat::stat const &Stat::statStruct() const
{
    return d_stat;
}
inline Stat::Type Stat::type() const
{
    return static_cast<Type>(d_stat.st_mode & S_IFMT);
}
inline size_t Stat::uid() const
{
    return d_stat.st_uid;
}

} // FBB

#endif
