#ifndef INCLUDED_BOBCAT_SHAREDMEMORY_
#define INCLUDED_BOBCAT_SHAREDMEMORY_

#include <ios>

#include <bobcat/fswap>
#include <bobcat/sharedpos>
#include <bobcat/exception>

namespace FBB
{

class SharedSegment;

struct SharedEnum__
{
    enum SizeUnit
    {
        kB = 10,
        MB = 20,
        GB = 30
    };
};

class SharedMemory: public virtual SharedEnum__
{
    friend std::ostream &operator<<(std::ostream &out, 
                                    SharedMemory const &mem);

    static size_t const s_pageSize = 1 << 12;

        // updated by the non-default constructors.
    int d_id = -1;                      // id of SharedSegment
    SharedSegment *d_sharedSegment = 0; // points to the attached shared
                                        //  memory. This is NOT a pointer
                                        // to dynamically allocated memory
                                        // but a static_cast pointer to
                                        // the attached shared data segment
    SharedPos d_pos;

    size_t d_lockCount = 0;             // # of nested locks
    char *d_data = 0;                   // pointer to shared memory data

    public:
        SharedMemory() = default;

        SharedMemory(SharedMemory const &other) = delete;

        SharedMemory(size_t maxSize, SizeUnit sizeUnit,
                     size_t access = 0600);             // 2: creation mode;

        SharedMemory(int id);                           // 3

        ~SharedMemory();

        SharedMemory &operator=(SharedMemory &&tmp);


        std::streamsize blockOffset() const;// offset matching offset(),
                                            // relative to the current data 
                                            // block.

        void clear();                   // clear all existing data and reduce
                                        // until only the segment at 
                                        // d_sharedSegment  

        size_t dataSegmentSize() const;

        int get();                      // get char from d_segmentData 
                                        // locks), or EOF   

        int id() const;                 // id of the d_sharedSegment segment


                // after kill/remove: shared segment is unusable
        void kill();                    // delete all shared segments w/o locks

        std::streamsize maxOffset() const;  

        std::streamsize nReadable() const;  // beyond last readable byte
        std::streamsize offset() const;     // read/write offset relative to
                                            // ios::beg

        char *ptr();                        // 0 if at maxOffset

        int put(int ch);                // put char at d_offset (locks),
                                        // ch == EOF immediately returns EOF 

                                        // read len chars, return nRead or -1, 
                                        // locks
        int read(char *data, std::streamsize len);    

        template <typename Type>
        int read(Type *value);              // 1.f
     
        template <typename Type>            // 2.f
        int read(std::ios::off_type offset, Type *value,
                  std::ios::seekdir origin = std::ios::beg);

                // after kill/remove: shared segment is unusable
        void remove();                  // delete all shared segments.

                                        // returns -1 if inaccessible
        std::ios::pos_type seek(std::ios::off_type offset, 
                                std::ios::seekdir origin = std::ios::beg);

        std::streamsize showmanyc();

        void swap(SharedMemory &other);

        bool truncate(std::streamsize offset);  // nReadable is reduced to 
                                            // offset


                                        // write len bytes at d_offset
                                        // locks, returns #written or -1
        int write(char const *data, std::streamsize len);

        template <typename Type>
        int write(Type const *value);       // 1.f
     
        template <typename Type>            // 2.f
        int write(std::ios::off_type offset, Type const *value,
                  std::ios::seekdir origin = std::ios::beg);

        template <typename SharedType, typename ... Params>
        SharedType *install(std::streamsize *offset, Params &&...params);

    private:
        std::ostream &insert(std::ostream &out) const;


                                        // lockAll: d_lockCount should be 0.
        void lockAll();                 // lock all block[] mutexes
        void unlockAll();               // unlock all block[] mutexes

        void lock(size_t idx);          // (recursively) lock block idx
        void unlock(size_t idx);        // unlock block idx

        void clearAll();                // clear without locking

        bool blockAvailable(size_t idx);

        void map();                     // 1    maps shared data to d_data
        void map(size_t idx);           // 2    only called by load


        int writeBlock(char const *data, size_t len);   // locks, returns 
                                                        // #written or -1
        int readBlock(char *data, size_t len);          // same, but now reads
                                                        // both update offset

        static size_t computeSegmentSize(
                            size_t *nBlocks, 
                            long long maxMemory, SizeUnit sizeUnit);

        void validate() const;
};

#include "blockoffset.f"
#include "datasegmentsize.f"
#include "id.f"
#include "install.f"
#include "maxoffset.f"
#include "nreadable.f"
#include "offset.f"
#include "operatorassign.f"
#include "operatorinsert.f"
#include "read1.f"
#include "read2.f"
#include "swap.f"
#include "write1.f"
#include "write2.f"

} // FBB        
#endif


