/* osgEarth
 * Copyright 2016 Pelican Mapping
 * MIT License
 */
#ifndef OSGEARTH_DRIVER_CACHE_LEVELDB_BIN
#define OSGEARTH_DRIVER_CACHE_LEVELDB_BIN 1

#include "Tracker"
#include <osgEarth/Common>
#include <osgEarth/Cache>
#include <string>
#include <leveldb/db.h>

#define LEVELDB_CACHE_VERSION 1

namespace osgEarth { namespace Drivers { namespace LevelDBCache
{
    using namespace osgEarth;

    /** 
     * Cache bin implementation for a LevelDBCache.
    */
    class LevelDBCacheBin : public osgEarth::CacheBin
    {
    public:
        LevelDBCacheBin(const std::string& name, leveldb::DB* db, Tracker* tracker);

        virtual ~LevelDBCacheBin();

    public: // CacheBin interface

        ReadResult readObject(const std::string& key, const osgDB::Options*);

        ReadResult readImage(const std::string& key, const osgDB::Options*);

        ReadResult readNode(const std::string& key, const osgDB::Options*);

        ReadResult readString(const std::string& key, const osgDB::Options*);

        bool write(const std::string& key, const osg::Object* object, const Config& meta, const osgDB::Options*);

        bool remove(const std::string& key);

        bool touch(const std::string& key);

        RecordStatus getRecordStatus(const std::string& key);

        bool clear();

        bool compact();
        
        unsigned getStorageSize();

        Config readMetadata();

        bool writeMetadata( const Config& meta );

        std::string getHashedKey(const std::string& key) const;

        bool purgeOldest(unsigned maxnum);
        
    protected:

        bool binValidForReading(bool silent =true);

        bool binValidForWriting(bool silent =false);

        bool                              _ok;
        bool                              _binPathExists;
        std::string                       _metaPath;       // full path to the bin's metadata file
        std::string                       _binPath;        // full path to the bin's root folder
        osg::ref_ptr<osgDB::ReaderWriter> _rw;
        osg::ref_ptr<osgDB::Options>      _rwOptions;
        Threading::Mutex                  _rwMutex;
        leveldb::DB*                      _db;
        osg::ref_ptr<Tracker>             _tracker;
        bool                              _debug;
        
        // adapter base for all the osg read functions...
        struct Reader {
            osgDB::ReaderWriter*   _rw;
            const osgDB::Options*  _op;
            Reader(osgDB::ReaderWriter* rw, const osgDB::Options* op) : _rw(rw), _op(op) { }
            virtual osgDB::ReaderWriter::ReadResult read(std::istream& in) const = 0;
            virtual std::string name() const = 0;
        };

        struct ImageReader : public Reader {
            ImageReader(osgDB::ReaderWriter* rw, const osgDB::Options* op) : Reader(rw, op) { }
            osgDB::ReaderWriter::ReadResult read(std::istream& in) const { return _rw->readImage(in, _op); }
            std::string name() const { return "ImageReader"; }
        };
        struct NodeReader : public Reader {
            NodeReader(osgDB::ReaderWriter* rw, const osgDB::Options* op) : Reader(rw, op) { }
            osgDB::ReaderWriter::ReadResult read(std::istream& in) const { return _rw->readNode(in, _op); }
            std::string name() const { return "NodeReader"; }
        };
        struct ObjectReader : public Reader {
            ObjectReader(osgDB::ReaderWriter* rw, const osgDB::Options* op) : Reader(rw, op) { }
            osgDB::ReaderWriter::ReadResult read(std::istream& in) const { return _rw->readObject(in, _op); }
            std::string name() const { return "ObjectReader"; }
        };

        ReadResult read(const std::string& key, const Reader& reader);

        void postWrite();

        // key generators
        std::string binDataKeyTuple(const std::string& key) const;
        std::string binPhrase() const;
        std::string dataKey(const std::string& key) const;
        std::string dataKeyFromTuple(const std::string& tuple) const;
        std::string dataBegin() const;
        std::string dataEnd() const;
        std::string metaKey(const std::string& key) const;
        std::string metaKeyFromTuple(const std::string& tuple) const;
        std::string metaBegin() const;
        std::string metaEnd() const;
        std::string timeKey(const DateTime& t, const std::string& key) const;
        std::string timeBegin() const;
        std::string timeEnd() const;
        std::string binKey() const;
        std::string timeBeginGlobal() const;
        std::string timeEndGlobal() const;
    };


} } } // namespace osgEarth::Drivers::LevelDBCache

#endif // OSGEARTH_DRIVER_CACHE_LEVELDB_BIN
