#ifndef INCLUDED_BOBCAT_FLOCK_
#define INCLUDED_BOBCAT_FLOCK_

#include <sys/file.h>
#include <chrono>
#include <thread>
#include <bobcat/exception>
#include <bobcat/semaphore>

namespace FBB
{

class Flock
{
    std::string d_filename;
    int d_fd;
    bool d_ok;              // set to true if the wait-members got the lock
    Semaphore d_sem;

    public:
        Flock();                                                // 1.cc

        // copy/move constructors not avaiable

                            // construct an object using 'lockname' as file
                            // if lockname doesn't exist it is created with
                            // access mode 'mode'
        Flock(std::string const &lockname, mode_t mode = 0600);         // 2.cc

        ~Flock();           // merely unlocks but does not remove 'd_filename'

        void shared() const;    // returns once a shared lock is obtained
                                // throws an exception if the lock file 
                                // doesn't exist

        template <typename Amount, typename Resolution>                 // .f
        bool shared(std::chrono::duration<Amount, Resolution> const &time);
                            // returns true when a shared lock was 
                            // obtained within 'time' time units
                            // throws an exception if d_fd is -1

        void exclusive() const; // returns once an exclusive lock is obtained
                                // throws an exception if the lock file 
                                // doesn't exist

        template <typename Amount, typename Resolution>                 // .f
        bool exclusive(std::chrono::duration<Amount, Resolution> const &wait);
                            // returns true when an exclusive lock was 
                            // obtained within 'wait' time units
                            // throws an exception if d_fd is -1

                            // throws an exception if d_fd is -1
        void unlock() const;// releases the lock

        int fd() const;                                                 // .f
        std::string const &filename() const;                            // .f

        void setFilename(std::string const &filename, mode_t mode = 0600);

    private:
        template <typename Amount, typename Resolution>                 // .f
        bool lockThread(std::chrono::duration<Amount, Resolution> const &wait,
                        int type);

        void reset();
        void requireFD() const;

        void notifyLock(int lockType);                  
        static void tryLock(Flock *ptr, int lockType);                  // .f

};

//inline  Flock::()
//{
//}

inline int Flock::fd() const
{
    return d_fd;
}

inline  std::string const &Flock::filename() const
{
    return d_filename;
}

template <typename Amount, typename Resolution>
inline bool Flock::exclusive(std::chrono::duration<Amount, Resolution> 
                                                                const &time) 
{
    return lockThread(time, LOCK_EX);
}

template <typename Amount, typename Resolution>
inline bool Flock::shared(std::chrono::duration<Amount, Resolution> 
                                                                const &time) 
{
    return lockThread(time, LOCK_SH);
}

// static
inline void Flock::tryLock(Flock *ptr, int lockType)
{
    ptr->notifyLock(lockType);
}

template <typename Amount, typename Resolution>
bool Flock::lockThread(std::chrono::duration<Amount, Resolution> const &time,
                       int flockType)
{
    requireFD();

    std::thread thr{ tryLock, this, flockType };     // try to flock
    thr.detach();

    d_sem.wait_for(time);

    return d_ok;
}


} // FBB        

#endif
