/**************************************************************************
*   Copyright (C) 2008-2010 by Ilya Kotov                                 *
*   forkotov02@hotmail.ru                                                 *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#include <QFile>
#include <QSettings>
#include <QTranslator>
#include <QApplication>
#include <QPluginLoader>
#include <QMetaObject>
#include <QLibrary>
#include <qmmp/qmmp.h>
#include "filedialog.h"
#include "qtfiledialog.h"


FileDialog* FileDialog::_instance = 0;

QMap<QString,FileDialogFactory*> FileDialog::factories = QMap<QString,FileDialogFactory*>();

FileDialog::FileDialog() : QObject(), m_initialized(false)
{
    m_lastDir = 0;
}

bool FileDialog::isModal()
{
    return instance()->modal();
}

void FileDialog::setEnabled(FileDialogFactory *factory)
{
    if (factories.isEmpty())
    {
        registerBuiltinFactories();
        registerExternalFactories();
    }
    QSettings settings (Qmmp::configFile(), QSettings::IniFormat);
    settings.setValue("FileDialog", factories.key(factory));
}

bool FileDialog::isEnabled(FileDialogFactory *factory)
{
    if (factories.isEmpty())
    {
        registerBuiltinFactories();
        registerExternalFactories();
    }
    QSettings settings (Qmmp::configFile(), QSettings::IniFormat);
    QString f_dialogName = settings.value("FileDialog", "qt_dialog").toString();
    return factories.value(f_dialogName) == factory;
}

void FileDialog::init(QObject* receiver, const char* member, QString *dir)
{
    m_lastDir = dir;
    if (m_initialized)
        disconnect();
    if (receiver &&  member)
    {
        connect(this,SIGNAL(filesAdded(const QStringList&)), receiver, member);
        connect(this,SIGNAL(filesAdded(const QStringList&)), SLOT(updateLastDir(const QStringList&)));
        m_initialized = true;
    }
}

QString FileDialog::getExistingDirectory(QWidget *parent,
        const QString &caption,
        const QString &dir)
{
    return instance()->existingDirectory(parent,caption,dir);
}

QString FileDialog::getOpenFileName(QWidget *parent,
                                    const QString &caption,
                                    const QString &dir,
                                    const QString &filter,
                                    QString *selectedFilter)
{
    return instance()->openFileName(parent,caption,dir,filter,selectedFilter);
}

QStringList FileDialog::getOpenFileNames(QWidget *parent, const QString &caption,
        const QString &dir,const QString &filter,
        QString *selectedFilter)
{
    return  instance()->openFileNames(parent,caption,dir,filter,selectedFilter);
}

QString FileDialog::getSaveFileName (QWidget *parent, const QString &caption,
                                     const QString& dir, const QString &filter,
                                     QString *selectedFilter)
{
    return instance()->saveFileName(parent,caption,dir,filter,selectedFilter);
}

//virtual
QString FileDialog::existingDirectory(QWidget *parent, const QString &caption, const QString &dir)
{
    FileDialog *instance = FileDialog::defaultInstance();
    QString dir_path = instance->existingDirectory(parent, caption, dir);
    delete instance;
    return dir_path;
}

QString FileDialog::openFileName(QWidget *parent, const QString &caption, const QString &dir,
                                 const QString &filter, QString *selectedFilter)
{
    FileDialog *instance = FileDialog::defaultInstance();
    QString file_path = instance->openFileName(parent, caption, dir, filter, selectedFilter);
    delete instance;
    return file_path;
}

QStringList FileDialog::openFileNames(QWidget *parent, const QString &caption, const QString &dir,
                                      const QString &filter, QString *selectedFilter)
{
    FileDialog *instance = FileDialog::defaultInstance();
    QStringList list = instance->openFileNames(parent, caption, dir, filter, selectedFilter);
    delete instance;
    return list;
}

QString FileDialog::saveFileName(QWidget *parent, const QString &caption, const QString &dir,
                                 const QString &filter, QString *selectedFilter)
{
    FileDialog *instance = FileDialog::defaultInstance();
    QString file_path = instance->saveFileName(parent, caption, dir, filter, selectedFilter);
    delete instance;
    return file_path;
}


void FileDialog::registerBuiltinFactories()
{
    FileDialogFactory *fct = new QtFileDialogFactory();
    qApp->installTranslator(fct->createTranslator(qApp));
    registerFactory(fct);
}

void FileDialog::registerExternalFactories()
{
    QDir pluginsDir (Qmmp::pluginsPath());
    pluginsDir.cd("FileDialogs");
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (loader.isLoaded())
            qDebug("FileDialog: plugin loaded - %s", qPrintable(fileName));
        else
            qDebug("FileDialog: %s",qPrintable(loader.errorString()));

        FileDialogFactory *fct = 0;
        if (plugin)
            fct = qobject_cast<FileDialogFactory *>(plugin);

        if (fct)
        {
            if (!registerFactory(fct))
                qDebug("Warning: Plugin with name %s is already registered...",
                       qPrintable(fileName));
            qApp->installTranslator(fct->createTranslator(qApp));
        }
    }
#ifndef Q_OS_WIN32
#if (QT_VERSION >= 0x040500 && QT_VERSION < 0x040600)
    //load native kde dialog
    QStringList paths;
    paths << "/usr/lib/kde4/kio_file.so";
    paths << "/usr/lib64/kde4/kio_file.so";
    paths << "/usr/local/kde4/lib/kde4/kio_file.so";
    foreach(QString path, paths)
    {
        if(QFile::exists(path))
        {
            QLibrary *l = new QLibrary(path, qApp);
            l->load();
            break;
        }
    }
#endif
#endif
}

bool FileDialog::registerFactory(FileDialogFactory *factory)
{
    if (!factories.contains(factory->properties().shortName))
    {
        factories.insert(factory->properties().shortName, factory);
        return true;
    }
    return false;
}

QString FileDialog::m_current_factory = QString();

FileDialog* FileDialog::instance()
{
    if (factories.isEmpty())
    {
        registerBuiltinFactories();
        registerExternalFactories();
    }

    QSettings settings (Qmmp::configFile(), QSettings::IniFormat);
    QString f_dialogName = settings.value("FileDialog", "qt_dialog").toString();

    QStringList names = factories.keys();

    if (!names.contains(f_dialogName))
        f_dialogName = "qt_dialog";

    if (m_current_factory != f_dialogName || !_instance)
    {
        if (_instance)
        {
            delete _instance;
            _instance = 0;
        }

        foreach(QString name,names)
        {
            if (name == f_dialogName)
            {
                _instance = factories[name]->create();
                m_current_factory = f_dialogName;
                break;
            }
        }

        if (!_instance)
            _instance = factories["qt_dialog"]->create();
    }
    return _instance;
}

FileDialog* FileDialog::defaultInstance()
{
    if (factories.isEmpty())
    {
        registerBuiltinFactories();
        registerExternalFactories();
    }
    return factories["qt_dialog"]->create();
}

QList <FileDialogFactory*> FileDialog::registeredFactories()
{
    if (factories.isEmpty())
    {
        registerBuiltinFactories();
        registerExternalFactories();
    }
    return factories.values();
}

void FileDialog::popup(QWidget *parent,
                       Mode m,
                       QString *dir,
                       QObject *receiver,
                       const char *member,
                       const QString &caption,
                       const QString &filters)

{
    if (!dir)
        qFatal("FileDialog: empty last dir pointer");
    FileDialog* inst = instance();
    inst->setParent(parent);
    inst->init(receiver, member, dir);
    if (!inst->modal())
        inst->raise(*dir, m, caption, filters.split(";;"));
    else
    {
        QStringList files;
        if (m == AddFiles || m == AddFile || m == AddDirsFiles)
        {
            QString selectedFilter;
            files = getOpenFileNames(parent, caption, *dir, filters, &selectedFilter);
        }
        else if (m == AddDirs || m == AddDir)
        {
            QString path = getExistingDirectory(parent, caption, *dir);
            if (!path.isEmpty())
                files << path;
        }
        QMetaObject::invokeMethod(inst, "filesAdded", Q_ARG(QStringList, files));
    }
}


void FileDialog::updateLastDir(const QStringList& list)
{
    if (!list.isEmpty() && m_lastDir)
    {
        QString path = list[0];
        if (path.endsWith('/'))
            path.remove(path.size()-1, 1);
        *m_lastDir = path.left(path.lastIndexOf ('/'));
    }
}
