/*
 * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
 *
 * Distributable under the terms of either the Apache License (Version 2.0) or 
 * the GNU Lesser General Public License, as specified in the COPYING file.
 *
 * Changes are Copyright (C) 2015 The Qt Company Ltd.
*/
#ifndef _lucene_store_FSDirectory_
#define _lucene_store_FSDirectory_

#if defined(_LUCENE_PRAGMA_ONCE)
#   pragma once
#endif

#include <QtCore/QFile>
#include <QtCore/QString>
#include <QtCore/QStringList>

#include "Directory.h"
#include "Lock.h"
#include "CLucene/util/VoidMap.h"
#include "CLucene/util/StringBuffer.h"

CL_NS_DEF(store)

/**
* Straightforward implementation of {@link Directory} as a directory of files.
* <p>If the system property 'disableLuceneLocks' has the String value of
* "true", lock creation will be disabled.
*
* @see Directory
*/
class FSDirectory : public Directory
{
public:
    // Destructor - only call this if you are sure the directory
    // is not being used anymore. Otherwise use the ref-counting
    // facilities of _CLDECDELETE
    ~FSDirectory();

    // Get a list of strings, one for each file in the directory. 
    QStringList list() const;

    // Returns true iff a file with the given name exists. 
    bool fileExists(const QString& name) const;

    // Returns the text name of the directory
    QString getDirName() const; ///<returns reference

    /**
    Returns the directory instance for the named location.

    Do not delete this instance, only use close, otherwise other instances
    will lose this instance.

    <p>Directories are cached, so that, for a given canonical path, the same
    FSDirectory instance will always be returned.  This permits
    synchronization on directories.

    @param file the path to the directory.
    @param create if true, create, or erase any existing contents.
    @return the FSDirectory for the named file.
    */
    static FSDirectory* getDirectory(const QString& file, const bool create);

    // Returns the time the named file was last modified.
    int64_t fileModified(const QString& name) const;

    //static
    // Returns the time the named file was last modified.
    static int64_t fileModified(const QString& dir, const QString& name);

    // static
    // Returns the length in bytes of a file in the directory. 
    int64_t fileLength(const QString& name) const;

    // Returns a stream reading an existing file. 
    IndexInput* openInput(const QString& name);
    IndexInput* openInput(const QString& name, int32_t bufferSize);

    // Renames an existing file in the directory. 
    void renameFile(const QString& from, const QString& to);

    // Set the modified time of an existing file to now.
    void touchFile(const QString& name);

    // Creates a new, empty file in the directory with the given name.
    // Returns a stream writing this file.
    IndexOutput* createOutput(const QString& name);

    // Construct a {@link Lock}.
    // @param name the name of the lock file
    LuceneLock* makeLock(const QString& name);

    // Decrease the ref-count to the directory by one. If the object is no
    // longer needed, then the object is removed from the directory pool. 
    void close();

    // If MMap is available, this can disable use of mmap reading.
    void setUseMMap(bool value) { useMMap = value; }

    // Gets whether the directory is using MMap for inputstreams.
    bool getUseMMap() const { return useMMap; }

    QString toString() const;

    static QString DirectoryType() { return QLatin1String("FS"); }
    QString getDirectoryType() const { return QLatin1String("FS"); }

    // Set whether Lucene's use of lock files is disabled. By default, 
    // lock files are enabled. They should only be disabled if the index
    // is on a read-only medium like a CD-ROM.
    static void setDisableLocks(bool doDisableLocks)
    { disableLocks = doDisableLocks; }

    // Returns whether Lucene's use of lock files is disabled.
    // @return true if locks are disabled, false if locks are enabled.
    static bool getDisableLocks() { return disableLocks; }

protected:
    FSDirectory(const QString& path, const bool createDir);
    // Removes an existing file in the directory. 
    bool doDeleteFile(const QString& name);

private:
    class FSLock : public LuceneLock {
    public:
        FSLock (const QString& lockDir, const QString& name);
        ~FSLock();

        bool obtain();
        void release();
        bool isLocked();
        QString toString() const;

        QString lockDir;
        QString lockFile;
    };
    friend class FSDirectory::FSLock;

    class FSIndexInput : public BufferedIndexInput {
    public:
        FSIndexInput(const QString& path, int32_t bufferSize =
            CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE);
        ~FSIndexInput();

        void close();
        IndexInput* clone() const;

        int64_t length()
        { return handle->_length; }

        QString getDirectoryType() const
        { return FSDirectory::DirectoryType(); }

    protected:
        FSIndexInput(const FSIndexInput& clone);
        // Random-access methods 
        void seekInternal(const int64_t position);
        // IndexInput methods 
        void readInternal(uint8_t* b, const int32_t len);

    private:
        // We used a shared handle between all the fsindexinput clones.
        // This reduces number of file handles we need, and it means
        // we dont have to use file tell (which is slow) before doing a read.
        class SharedHandle : LUCENE_REFBASE {
        public:
            SharedHandle();
            ~SharedHandle();

            int64_t _fpos;
            int64_t _length;
            
            QFile fhandle;
            DEFINE_MUTEX(*THIS_LOCK)
        };
        SharedHandle* handle;
        int64_t _pos;
    };
    friend class FSDirectory::FSIndexInput;

    class FSIndexOutput : public BufferedIndexOutput {
    public:
        FSIndexOutput(const QString& path);
        ~FSIndexOutput();

        void close();
        int64_t length();
        void seek(const int64_t pos);

    protected:
        void flushBuffer(const uint8_t* b, const int32_t size);

    private:
        QFile fhandle;
    };
    friend class FSDirectory::FSIndexOutput;

private:
    QString directory;
    int refCount;
    void create();

    QString lockDir;
    QString getLockPrefix() const;
    static bool disableLocks;

    void priv_getFN(QString& buffer, const QString& name) const;
    bool useMMap;
};

CL_NS_END

#endif
