// mmap.h

/*    Copyright 2009 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#pragma once

namespace mongo {
    
    /* the administrative-ish stuff here */
    class MongoFile : boost::noncopyable { 
        
    public:
        /** Flushable has to fail nicely if the underlying object gets killed */
        class Flushable {
        public:
            virtual ~Flushable(){}
            virtual void flush() = 0;
        };
        
    protected:
        virtual void close() = 0;
        virtual void flush(bool sync) = 0;
        /**
         * returns a thread safe object that you can call flush on
         * Flushable has to fail nicely if the underlying object gets killed
         */
        virtual Flushable * prepareFlush() = 0;
        
        void created(); /* subclass must call after create */
        void destroyed(); /* subclass must call in destructor */

        // only supporting on posix mmap
        virtual void _lock() {}
        virtual void _unlock() {}

    public:
        virtual ~MongoFile() {}
        virtual long length() = 0;

        enum Options {
            SEQUENTIAL = 1 // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on windows
        };

        static int flushAll( bool sync ); // returns n flushed
        static long long totalMappedLength();
        static void closeAllFiles( stringstream &message );

        // Locking allows writes. Reads are always allowed
        static void lockAll();
        static void unlockAll();

        /* can be "overriden" if necessary */
        static bool exists(boost::filesystem::path p) {
            return boost::filesystem::exists(p);
        }
    };

#ifndef _DEBUG
    // no-ops in production
    inline void MongoFile::lockAll() {}
    inline void MongoFile::unlockAll() {}

#endif

    struct MongoFileAllowWrites {
        MongoFileAllowWrites(){
            MongoFile::lockAll();
        }
        ~MongoFileAllowWrites(){
            MongoFile::unlockAll();
        }
    };

    /** template for what a new storage engine's class definition must implement 
        PRELIMINARY - subject to change.
    */
    class StorageContainerTemplate : public MongoFile {
    protected:
        virtual void close();
        virtual void flush(bool sync);
    public:
        virtual long length();

        /** pointer to a range of space in this storage unit */
        class Pointer {
        public:
            /** retried address of buffer at offset 'offset' withing the storage unit. returned range is a contiguous 
                buffer reflecting what is in storage.  caller will not read or write past 'len'.

                note calls may be received that are at different points in a range and different lengths. however 
                for now assume that on writes, if a call is made, previously returned addresses are no longer valid. i.e.
                  p = at(10000, 500);
                  q = at(10000, 600);
                after the second call it is ok if p is invalid.
            */
            void* at(int offset, int len);

            /** indicate that we wrote to the range (from a previous at() call) and that it needs 
                flushing to disk.
                */
            void written(int offset, int len);

            bool isNull() const;
        };

        /** commit written() calls from above. */
        void commit();
        
        Pointer open(const char *filename);
        Pointer open(const char *_filename, long &length, int options=0);
    };

    class MemoryMappedFile : public MongoFile {
    public:
        class Pointer {
            char *_base;
        public:
            Pointer() : _base(0) { }
            Pointer(void *p) : _base((char*) p) { }
            void* at(int offset, int maxLen) { return _base + offset; } 
			void grow(int offset, int len) { /* no action required with mem mapped file */ }
            bool isNull() const { return _base == 0; }
        };

        MemoryMappedFile();
        ~MemoryMappedFile() {
            destroyed();
            close();
        }
        void close();
        
        // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?)
        void* map( const char *filename );

        /*To replace map():
        
          Pointer open( const char *filename ) {
            void *p = map(filename);
            uassert(13077, "couldn't open/map file", p);
            return Pointer(p);
        }*/

        /* Creates with length if DNE, otherwise uses existing file length,
           passed length.
        */
        void* map(const char *filename, long &length, int options = 0 );

        void flush(bool sync);
        virtual Flushable * prepareFlush();

        /*void* viewOfs() {
            return view;
        }*/

        long length() {
            return len;
        }
        
    private:
        static void updateLength( const char *filename, long &length );
        
        HANDLE fd;
        HANDLE maphandle;
        void *view;
        long len;
        string _filename;

    protected:
        // only posix mmap implementations will support this
        virtual void _lock();
        virtual void _unlock();

    };

    void printMemInfo( const char * where );    

#include "ramstore.h"

//#define _RAMSTORE
#if defined(_RAMSTORE)
    typedef RamStoreFile MMF;
#else
    typedef MemoryMappedFile MMF;
#endif

} // namespace mongo
