#ifndef INCLUDED_BOBCAT_MEMORYACCESS_
#define INCLUDED_BOBCAT_MEMORYACCESS_

#include <iosfwd>
#include <vector>
#include <algorithm>            // min()

#include <bobcat/exception>

// MemoryAccess's constructor is called by create and refresh to initialize 
// its d_segmentPtr to point to a MemoryAccess which is constructed in shared
// memory.

namespace FBB
{

class MemoryAccess
{
    static size_t const s_extend = 5;       // # blocks added by extend.cc
    static size_t const s_minimum = 10;     // minimum # of blocks
    static size_t const s_pageSize = 1 << 12;   // = 4096, std. page size
    static size_t const s_noBlock = -1;     // cf. activate.cc, disconnect1.cc

    size_t      d_idx;              // construction sequence idx

    size_t      d_blockStep;        // # blocks to add by extend
    size_t      d_blockSize;        // size of the shared data blocks

    size_t      d_id;               // this object's own shmat-ID
    size_t      d_accessMode;       // chmod access value (e.g., 0600)

        // positions relative to the beginning of the segement's data
    size_t d_blockBegin;            // begin abs. offset of d_currentBlock
    size_t d_blockEnd;              // abs. offset beyond the last avail. byte
                                    // in the current block

    size_t d_writtenUntil;           // offset of the last ever written byte
                                    // (maybe set by truncate)

    size_t d_maxEnd;                // max. abs size updated by write/truncate
                                    // never exceeds nBlocks * dataSize
    char       *d_data;             // points to -> the data of d_currentBlock
    size_t      d_nBlocks;

    int      d_block[1];         // IDs of shared data blocks

    // int d_block[1];          // IDs of shared data blocks
        // this is not a standard array: when the shared segment is created by 
        // create() a block of 'segmentSize' bytes is made available. This
        // block is 'raw meory' and a MemoryAccess is installed in there
        // using placement new. When create() is called the number of blocks
        // are specified, and the area from d_block to d_block + nBlocks is
        // initialized with nBlocks MemoryID objects, each containing an
        // id, initialized to -1. Once a block is requested a new shared block
        // is created 

// getId returns the ID of a shared segment containing a specifiied #bytes
// connect returns a pointer to the data prepared by getID
// disconnect 

    static std::vector<size_t>  s_loaded;   // by activate members
    static size_t s_idx;                    // idx used by the next object

    public:
        MemoryAccess(MemoryAccess const &other) = delete;
        ~MemoryAccess();

        char *beginPtr();                                               // .f

        size_t blockBegin() const;                                      // .f
        size_t blockEnd() const;                                        // .f

        size_t bufLimits(size_t offset);    // returns current block idx

        char *endPtr();                                                 // .f
        char *endReadPtr(size_t offset);                                // .f

        int id() const;                                                 // .f

        void info(std::ostream &out) const;

        bool load(size_t offset);

        size_t maxEnd() const;                                          // .f

        char *offsetPtr(size_t offset);                                 // .f

                                                    // returns # of 
                                                    // read bytes.
        size_t read(char *src, size_t len, size_t offset);  

        void truncate(size_t offset);               // may update 
                                                    // d_writtenUntil

                                                    // returns # of 
                                                    // written bytes.
        size_t write(char const *src, size_t len, size_t offset);  

        size_t writtenUntil() const;     // offset of last written byte // .f
        void writtenUntil(size_t offset);// update d_writtenUntil       // .f


                                                    // ptr to new MemoryAccess
        static MemoryAccess *create(std::string const &bufSize, 
                                    size_t access);

        static void drop(MemoryAccess *ptr, bool erase);

        static MemoryAccess *extend(size_t offset, MemoryAccess *ptr);

        static MemoryAccess *loadID(int id);        // throws on failure

    private:
        MemoryAccess(int id, size_t access, size_t nBlocks,             // 1.
                      size_t blockSize);

        void activate(size_t idx, int id);  // disconnects a connected block
        size_t activated() const;                              // activate.c

        void disconnect(size_t blockIdx);                               // 1.
        static void disconnect(MemoryAccess const *ptr);                // 2.

        void get(size_t blockIdx);

        void cpFrom(MemoryAccess &old);

            // the 'mem' parameter is const_cast to char * by memRead
        size_t io(char const *mem, size_t len, 
                  void (*copy)(char const *, char const *, size_t),
                  size_t offset);

        void killBlocks();

        char *ptr(size_t offset);   // ptr to the byte at offset        // .ih

        MemoryAccess *refresh(size_t offset);

        void setDataPtr(int id);

        void setZero(size_t from);              // set 0 from 'from' to
                                                // d_writtenUntil

    // statics below

        static void *connect(int id);           // return a ptr to segment 
                                                // id's data or 0

        static void cptSizes(size_t *nBlocks, size_t *blockSize,
                             std::string const &bufSize);

        static size_t divPlus(size_t value, size_t unit);               // .ih

        static void gigaBytes(size_t *nBlocks, size_t *blockSize, 
                              size_t factor);

        static int getID(size_t requestedSize, size_t access);

        static size_t getSegmentSize(int id);   // size of the shm block

        static void killID(int id);

        static void kiloBytes(size_t *nBlocks, size_t *blockSize, 
                              size_t factor);

        static void megaBytes(size_t *nBlocks, size_t *blockSize, 
                              size_t factor);

        static size_t memoryAccessSize(size_t nBlocks);

            // the 'dest' parameters are const_cast to char *
        static void memRead(char const *dest, char const *src, size_t nChars);
        static void memWrite(char const *src, char const *dest, 
                                                               size_t nChars);
};

#include "memoryaccess.f"

} // FBB

#endif
