/*********
*
* This file is part of BibleTime's source code, http://www.bibletime.info/.
*
* Copyright 1999-2014 by the BibleTime developers.
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
*
**********/

#include "util/directory.h"

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFileInfoList>
#include <QLocale>


namespace util {
namespace directory {

namespace {

QScopedPointer<QDir> cachedIconDir;
QScopedPointer<QDir> cachedJavascriptDir;
QScopedPointer<QDir> cachedLicenseDir;
QScopedPointer<QDir> cachedPicsDir;
QScopedPointer<QDir> cachedLocaleDir;
QScopedPointer<QDir> cachedHandbookDir;
QScopedPointer<QDir> cachedHowtoDir;
QScopedPointer<QDir> cachedDisplayTemplatesDir;
QScopedPointer<QDir> cachedUserDisplayTemplatesDir;
QScopedPointer<QDir> cachedUserBaseDir;
QScopedPointer<QDir> cachedUserHomeDir;
QScopedPointer<QDir> cachedUserHomeSwordDir;
QScopedPointer<QDir> cachedUserHomeSwordModsDir;
QScopedPointer<QDir> cachedUserSessionsDir;
QScopedPointer<QDir> cachedUserCacheDir;
QScopedPointer<QDir> cachedUserIndexDir;
QScopedPointer<QDir> cachedSwordPathDir;
#ifdef Q_OS_WIN
QScopedPointer<QDir> cachedApplicationSwordDir; // Only Windows installs the sword directory which contains locales.d
QScopedPointer<QDir> cachedSharedSwordDir;
#endif

#ifdef Q_OS_MAC
QScopedPointer<QDir> cachedSwordLocalesDir;
#endif

#ifdef Q_OS_WIN
static const char BIBLETIME[] = "Bibletime";
static const char SWORD_DIR[] = "Sword";
#else
#ifdef Q_OS_MAC
static const char BIBLETIME[] = "Library/Application Support/BibleTime";
static const char SWORD_DIR[] = "Library/Application Support/Sword";
#else
static const char BIBLETIME[] = ".bibletime";
static const char SWORD_DIR[] = ".sword";
#endif
#endif
static const char SWORD_PATH[] = "SWORD_PATH";
} // anonymous namespace

bool initDirectoryCache() {
    QDir wDir(QCoreApplication::applicationDirPath());
    wDir.makeAbsolute();

    if (!wDir.cdUp()) { // Installation prefix
        qWarning() << "Cannot cd up from directory " << QCoreApplication::applicationDirPath();
        return false;
    }

#ifdef Q_OS_WIN

    cachedApplicationSwordDir.reset(new QDir(wDir)); // application sword dir for Windows only
    if (!cachedApplicationSwordDir->cd("share/sword") || !cachedApplicationSwordDir->isReadable()) {
        qWarning() << "Cannot find sword directory relative to" << QCoreApplication::applicationDirPath();
        return false;
    }

    cachedSharedSwordDir.reset(new QDir(qgetenv("ALLUSERSPROFILE"))); // sword dir for Windows only
    if (!cachedSharedSwordDir->cd("Application Data")) {
        qWarning() << "Cannot find ALLUSERSPROFILE\\Application Data";
        return false;
    }
    if (!cachedSharedSwordDir->cd(SWORD_DIR)) {
        if (!cachedSharedSwordDir->mkdir(SWORD_DIR) || !cachedSharedSwordDir->cd(SWORD_DIR)) {
            qWarning() << "Cannot find ALLUSERSPROFILE\\Application Data\\Sword";
            return false;
        }
    }
#endif

#ifdef Q_OS_MAC
    cachedSwordLocalesDir.reset(new QDir(wDir)); // application sword dir for Windows only
    if (!cachedSwordLocalesDir->cd("share/sword/locales.d") || !cachedSwordLocalesDir->isReadable()) {
        qWarning() << "Cannot find sword locales directory relative to" << QCoreApplication::applicationDirPath();
        return false;
    }
#endif

    cachedSwordPathDir.reset(new QDir(wDir));
    char* swordPath = qgetenv(SWORD_PATH).data();
    if (swordPath != 0) {
        cachedSwordPathDir.reset(new QDir(swordPath));
        // We unset the SWORD_PATH so libsword finds paths correctly
        qputenv(SWORD_PATH, "");
    }

    cachedIconDir.reset(new QDir(wDir)); // Icon dir
    if (!cachedIconDir->cd("share/bibletime/icons") || !cachedIconDir->isReadable()) {
        qWarning() << "Cannot find icon directory relative to" << wDir.absolutePath();
        return false;
    }

    cachedJavascriptDir.reset(new QDir(wDir));
    if (!cachedJavascriptDir->cd("share/bibletime/javascript") || !cachedJavascriptDir->isReadable()) {
        qWarning() << "Cannot find javascript directory relative to" << wDir.absolutePath();
        return false;
    }

    cachedLicenseDir.reset(new QDir(wDir));
    if (!cachedLicenseDir->cd("share/bibletime/license") || !cachedLicenseDir->isReadable()) {
        qWarning() << "Cannot find license directory relative to" << wDir.absolutePath();
        return false;
    }

    cachedPicsDir.reset(new QDir(wDir));
    if (!cachedPicsDir->cd("share/bibletime/pics") || !cachedPicsDir->isReadable()) {
        qWarning() << "Cannot find pics directory relative to" << wDir.absolutePath();
        return false;
    }

    cachedLocaleDir.reset(new QDir(wDir));
    if (!cachedLocaleDir->cd("share/bibletime/locale")) {
        qWarning() << "Cannot find locale directory relative to" << wDir.absolutePath();
        return false;
    }

    QString localeName(QLocale::system().name());
    QString langCode(localeName.section('_', 0, 0));

    cachedHandbookDir.reset(new QDir(wDir));
    if (!cachedHandbookDir->cd("share/bibletime/docs/handbook/" + localeName)) {
        if (!cachedHandbookDir->cd("share/bibletime/docs/handbook/" + langCode)) {
            if (!cachedHandbookDir->cd("share/bibletime/docs/handbook/en/")) {
                qWarning() << "Cannot find handbook directory relative to" << wDir.absolutePath();
                return false;
            }
        }
    }

    cachedHowtoDir.reset(new QDir(wDir));
    if (!cachedHowtoDir->cd("share/bibletime/docs/howto/" + localeName)) {
        if (!cachedHowtoDir->cd("share/bibletime/docs/howto/" + langCode)) {
            if (!cachedHowtoDir->cd("share/bibletime/docs/howto/en/")) {
                qWarning() << "Cannot find handbook directory relative to" << wDir.absolutePath();
                return false;
            }
        }
    }

    cachedDisplayTemplatesDir.reset(new QDir(wDir)); //display templates dir
    if (!cachedDisplayTemplatesDir->cd("share/bibletime/display-templates/")) {
        qWarning() << "Cannot find display template directory relative to" << wDir.absolutePath();
        return false;
    }

    cachedUserHomeDir.reset(new QDir(qgetenv("HOME")));

    cachedUserBaseDir.reset(new QDir(*cachedUserHomeDir));
    if (!cachedUserBaseDir->cd(BIBLETIME)) {
        if (!cachedUserBaseDir->mkpath(BIBLETIME) || !cachedUserBaseDir->cd(BIBLETIME)) {
            qWarning() << "Could not create user settings directory relative to" << cachedUserHomeDir->absolutePath();
            return false;
        }
    }

    cachedUserHomeSwordDir.reset(new QDir(*cachedUserHomeDir));
    if (!cachedUserHomeSwordDir->cd(SWORD_DIR)) {
        if (!cachedUserHomeSwordDir->mkpath(SWORD_DIR) || !cachedUserHomeSwordDir->cd(SWORD_DIR)) {
            qWarning() << "Could not create user home " << SWORD_DIR << " directory.";
            return false;
        }
    }

    cachedUserHomeSwordModsDir.reset(new QDir(*cachedUserHomeSwordDir));
    if (!cachedUserHomeSwordModsDir->cd("mods.d")) {
        if (!cachedUserHomeSwordModsDir->mkdir("mods.d") || !cachedUserHomeSwordModsDir->cd("mods.d")) {
            qWarning() << "Could not create user home " << SWORD_DIR << " mods.d directory.";
            return false;
        }
    }

    cachedUserSessionsDir.reset(new QDir(*cachedUserBaseDir));
    if (!cachedUserSessionsDir->cd("sessions")) {
        if (!cachedUserSessionsDir->mkdir("sessions") || !cachedUserSessionsDir->cd("sessions")) {
            qWarning() << "Could not create user sessions directory.";
            return false;
        }
    }

    cachedUserCacheDir.reset(new QDir(*cachedUserBaseDir));
    if (!cachedUserCacheDir->cd("cache")) {
        if (!cachedUserCacheDir->mkdir("cache") || !cachedUserCacheDir->cd("cache")) {
            qWarning() << "Could not create user cache directory.";
            return false;
        }
    }

    cachedUserIndexDir.reset(new QDir(*cachedUserBaseDir));
    if (!cachedUserIndexDir->cd("indices")) {
        if (!cachedUserIndexDir->mkdir("indices") || !cachedUserIndexDir->cd("indices")) {
            qWarning() << "Could not create user indices directory.";
            return false;
        }
    }

    cachedUserDisplayTemplatesDir.reset(new QDir(*cachedUserBaseDir));
    if (!cachedUserDisplayTemplatesDir->cd("display-templates")) {
        if (!cachedUserDisplayTemplatesDir->mkdir("display-templates") || !cachedUserDisplayTemplatesDir->cd("display-templates")) {
            qWarning() << "Could not create user display templates directory.";
            return false;
        }
    }

    return true;
} // bool initDirectoryCache();

void removeRecursive(const QString &dir) {
    //Check for validity of argument
    if (dir.isEmpty()) return;
    QDir d(dir);
    if (!d.exists()) return;

    //remove all files in this dir
    d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
    const QFileInfoList fileList = d.entryInfoList();
    for (QFileInfoList::const_iterator it_file = fileList.begin(); it_file != fileList.end(); ++it_file) {
        d.remove( it_file->fileName() );
    }

    //remove all subdirs recursively
    d.setFilter( QDir::Dirs | QDir::NoSymLinks );
    const QFileInfoList dirList = d.entryInfoList();
    for (QFileInfoList::const_iterator it_dir = dirList.begin(); it_dir != dirList.end(); ++it_dir) {
        if ( !it_dir->isDir() || it_dir->fileName() == "." || it_dir->fileName() == ".." ) {
            continue;
        }
        removeRecursive( it_dir->absoluteFilePath() );
    }
    d.rmdir(dir);
}

/** Returns the size of the directory including the size of all it's files and it's subdirs.
 */
unsigned long getDirSizeRecursive(const QString &dir) {
    //Check for validity of argument
    QDir d(dir);
    if (!d.exists()) return 0;

    unsigned long size = 0;

    //First get the size of all files int this folder
    d.setFilter(QDir::Files);
    const QFileInfoList infoList = d.entryInfoList();
    for (QFileInfoList::const_iterator it = infoList.begin(); it != infoList.end(); ++it) {
        size += it->size();
    }

    //Then add the sizes of all subdirectories
    d.setFilter(QDir::Dirs);
    const QFileInfoList dirInfoList = d.entryInfoList();
    for (QFileInfoList::const_iterator it_dir = dirInfoList.begin(); it_dir != dirInfoList.end(); ++it_dir) {
        if ( !it_dir->isDir() || it_dir->fileName() == "." || it_dir->fileName() == ".." ) {
            continue;
        }
        size += getDirSizeRecursive( it_dir->absoluteFilePath() );
    }
    return size;
}

/**Recursively copies a directory, overwriting existing files*/
void copyRecursive(const QString &src, const QString &dest) {
    QDir srcDir(src);
    QDir destDir(dest);
    //Copy files
    QStringList files = srcDir.entryList(QDir::Files);
    for (QStringList::iterator it = files.begin(); it != files.end(); ++it) {
        QFile currFile(src + "/" + *it);
        QString newFileLoc = dest + "/" + *it;
        QFile newFile(newFileLoc);
        newFile.remove();
        currFile.copy(newFileLoc);
    }
    QStringList dirs = srcDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    for (QStringList::iterator it = dirs.begin(); it != dirs.end(); ++it) {
        QString temp = *it;
        if (!destDir.cd(*it)) {
            destDir.mkdir(*it);
        }
        copyRecursive(src + "/" + *it, dest + "/" + *it);
    }
}

QString convertDirSeparators(const QString& path) {
    QString result = path;
#ifdef Q_OS_WIN
    result.replace("/", "\\");
#else
    result.replace("\\", "/");
#endif
    return result;
}

#ifdef Q_OS_WIN
const QDir &getApplicationSwordDir() {
    return *cachedApplicationSwordDir;
}

const QDir &getSharedSwordDir() {
    return *cachedSharedSwordDir;
}

#endif

#ifdef Q_OS_MAC
const QDir &getSwordLocalesDir() {
    return *cachedSwordLocalesDir;
}
#endif

const QDir &getSwordPathDir() {
    return *cachedSwordPathDir;
}

const QDir &getIconDir() {
    return *cachedIconDir;
}

const QDir &getJavascriptDir() {
    return *cachedJavascriptDir;
}

const QDir &getLicenseDir() {
    return *cachedLicenseDir;
}

const QDir &getPicsDir() {
    return *cachedPicsDir;
}

const QDir &getLocaleDir() {
    return *cachedLocaleDir;
}

const QDir &getHandbookDir() {
    return *cachedHandbookDir;
}

const QDir &getHowtoDir() {
    return *cachedHowtoDir;
}

const QDir &getDisplayTemplatesDir() {
    return *cachedDisplayTemplatesDir;
}

const QDir &getUserBaseDir() {
    return *cachedUserBaseDir;
}

const QDir &getUserHomeDir() {
    return *cachedUserHomeDir;
}

const QDir &getUserHomeSwordDir() {
    return *cachedUserHomeSwordDir;
}

const QDir &getUserHomeSwordModsDir() {
    return *cachedUserHomeSwordModsDir;
}

const QDir &getUserSessionsDir() {
    return *cachedUserSessionsDir;
}

const QDir &getUserCacheDir() {
    return *cachedUserCacheDir;
}

const QDir &getUserIndexDir() {
    return *cachedUserIndexDir;
}

const QDir &getUserDisplayTemplatesDir() {
    return *cachedUserDisplayTemplatesDir;
}

} // namespace directory
} // namespace util
