#ifndef INCLUDED_BOBCAT_STAT_
#define INCLUDED_BOBCAT_STAT_

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

#include <bobcat/datetime>

namespace FBB
{

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

    public:
        typedef struct stat 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,

           RWX =  0777,
        };
        enum Type
        {
            BLOCK_DEVICE        =  0060000,
            CHARACTER_DEVICE    =  0020000,   
            DIRECTORY           =  0040000,
            FIFO                =  0010000,
            REGULAR_FILE        =  0100000,
            SOCKET              =  0140000,
            SYMBOLIC_LINK       =  0120000,
        };

        Stat();
        explicit Stat(std::string const &name);
        Stat(std::string const &name, std::string const &pathlist);
        Stat(Stat &&tmp);

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

        DateTime lastAccess() const;
        DateTime lastChange() const;
        DateTime lastModification() const;
        Type type() const;
        bool isType(Type probe);
        bool mode(size_t mode, Combine combi = ALL) const;
        bool set(std::string const &name);
        bool set(std::string const &name, std::string const &pathlist);
        bool specialMode(size_t specialMode, Combine combi = ALL) const;
        off_t size() const;
        operator bool() const;
        size_t blockSize() const;
        size_t device() const;
        size_t deviceType() const;
        size_t error() const;
        size_t gid() const;
        size_t inode() const;
        size_t mode() const;
        size_t nBlocks() const;
        size_t nLinks() const;
        size_t uid() const;
        stat const &statStruct() const;
        std::string const &name();
        std::string modeStr() const;
        std::string path();
        std::string typeStr() const;

    private:
        void init();
};

inline Stat::Stat(std::string const &name, std::string const &pathlist)
{
    set(name, pathlist);
}
inline Stat::operator bool() const
{
    return d_errno == 0;
}
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 bool Stat::isType(Type probe)
{
    return type() == probe;
}
inline size_t Stat::gid() const
{
    return d_stat.st_gid;
}        
inline size_t Stat::inode() const
{
    return d_stat.st_ino;
}        
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 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
