/*****************************************************************************
 * Copyright (c) 2007 Andreas Pakulat <apaku@gmx.de>                         *
 * Copyright (c) 2007 Piyush verma <piyush.verma@gmail.com>                  *
 * Copyright (c) 2012 Sven Brauch <svenbrauch@gmail.com>                     *
 *                                                                           *
 * 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, see <http://www.gnu.org/licenses/>.     *
 *****************************************************************************
 */

#include "pythonlanguagesupport.h"

#include <QMutexLocker>
#include <QReadWriteLock>

#include <KPluginFactory>
#include <KPluginLoader>

#include <interfaces/icore.h>
#include <interfaces/ilanguagecontroller.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/idocument.h>
#include <interfaces/isourceformatter.h>
#include <interfaces/idocumentcontroller.h>
#include <interfaces/context.h>
#include <interfaces/contextmenuextension.h>
#include <interfaces/iprojectcontroller.h>
#include <interfaces/iproject.h>
#include <interfaces/isession.h>
#include <language/assistant/renameassistant.h>
#include <language/assistant/staticassistantsmanager.h>
#include <language/interfaces/editorcontext.h>
#include <language/duchain/duchain.h>
#include <language/duchain/duchainlock.h>
#include <language/codecompletion/codecompletion.h>
#include <language/codecompletion/codecompletionmodel.h>

#include "pythonparsejob.h"
#include "pythonhighlighting.h"
#include "duchain/pythoneditorintegrator.h"
#include "codecompletion/model.h"
#include "codegen/refactoring.h"
#include "codegen/correctionfilegenerator.h"
#include "kdevpythonversion.h"
#include "pep8kcm/kcm_pep8.h"
#include "docfilekcm/kcm_docfiles.h"

#include <QDebug>
#include "pythondebug.h"

using namespace KDevelop;

K_PLUGIN_FACTORY_WITH_JSON( KDevPythonSupportFactory, "kdevpythonsupport.json", registerPlugin<Python::LanguageSupport>(); )

namespace Python
{
LanguageSupport* LanguageSupport::m_self = 0;

KDevelop::ContextMenuExtension LanguageSupport::contextMenuExtension(KDevelop::Context* context)
{
    ContextMenuExtension cm;
    EditorContext *ec = dynamic_cast<KDevelop::EditorContext *>(context);

    if (ec && ICore::self()->languageController()->languagesForUrl(ec->url()).contains(this)) {
        // It's a Python file, let's add our context menu.
        m_refactoring->fillContextMenu(cm, context);
        TypeCorrection::self().doContextMenu(cm, context);
    }
    return cm;
}

LanguageSupport::LanguageSupport( QObject* parent, const QVariantList& /*args*/ )
    : KDevelop::IPlugin("pythonlanguagesupport", parent )
    , KDevelop::ILanguageSupport()
    , m_highlighting( new Highlighting( this ) )
    , m_refactoring( new Refactoring( this ) )
{
    KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageSupport )
    KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageCheckProvider )

    m_self = this;

    PythonCodeCompletionModel* codeCompletion = new PythonCodeCompletionModel(this);
    new KDevelop::CodeCompletion(this, codeCompletion, "Python");

    auto assistantsManager = core()->languageController()->staticAssistantsManager();
    assistantsManager->registerAssistant(StaticAssistant::Ptr(new RenameAssistant(this)));

    QObject::connect(ICore::self()->documentController(), &IDocumentController::documentOpened,
                     this, &LanguageSupport::documentOpened);
}

void LanguageSupport::documentOpened(IDocument* doc)
{
    if ( ! ICore::self()->languageController()->languagesForUrl(doc->url()).contains(this) ) {
        // not a python file
        return;
    }

    DUChainReadLocker lock;
    TopDUContextPointer topContext = TopDUContextPointer(DUChain::self()->chainForDocument(doc->url()));
    lock.unlock();
    ParseJob::eventuallyDoPEP8Checking(IndexedString(doc->url()), topContext.data());
}

LanguageSupport::~LanguageSupport()
{
    parseLock()->lockForWrite();
    // By locking the parse-mutexes, we make sure that parse jobs get a chance to finish in a good state
    parseLock()->unlock();

    delete m_highlighting;
    m_highlighting = 0;
}

KDevelop::ParseJob *LanguageSupport::createParseJob( const IndexedString& url )
{
    return new ParseJob(url, this);
}

QString LanguageSupport::name() const
{
    return "Python";
}

LanguageSupport* LanguageSupport::self()
{
    return m_self;
}

SourceFormatterItemList LanguageSupport::sourceFormatterItems() const
{
    SourceFormatterStyle autopep8("autopep8");
    autopep8.setCaption("autopep8");
    autopep8.setDescription(i18n("Format source with the autopep8 formatter."));
    autopep8.setOverrideSample("class klass:\n def method(arg1,arg2):\n  a=3+5\n"
                               "def function(arg,*vararg,**kwargs): return arg+kwarg[0]\nfunction(3, 5, 7)");
    using P = SourceFormatterStyle::MimeHighlightPair;
    autopep8.setMimeTypes(SourceFormatterStyle::MimeList{ P{"text/x-python", "Python"} });
    QString autopep8path = QStandardPaths::findExecutable("autopep8");
    if (autopep8path.isEmpty()) {
        // TODO: proper error handling/user notification
        qDebug() << "Could not find the autopep8 executable";
        autopep8path = "/usr/bin/autopep8";
    }
    autopep8.setContent(autopep8path + " -i $TMPFILE");

    return SourceFormatterItemList{SourceFormatterStyleItem{"customscript", autopep8}};
}

KDevelop::ICodeHighlighting* LanguageSupport::codeHighlighting() const
{
    return m_highlighting;
}

BasicRefactoring* LanguageSupport::refactoring() const
{
    return m_refactoring;
}

int LanguageSupport::suggestedReparseDelayForChange(KTextEditor::Document* doc, const KTextEditor::Range& changedRange,
                                                    const QString& changedText, bool /*removal*/) const
{
    if ( changedRange.start().line() != changedRange.end().line() ) {
        // instant update
        return 0;
    }
    if ( std::all_of(changedText.begin(), changedText.end(), [](const QChar& c) { return c.isSpace(); }) ) {
        qDebug() << changedText << changedRange.end().column() << doc->lineLength(changedRange.end().line());
        if ( changedRange.end().column()-1 == doc->lineLength(changedRange.end().line()) ) {
            return ILanguageSupport::NoUpdateRequired;
        }
    }
    return ILanguageSupport::DefaultDelay;
}


QList<ILanguageCheck*> LanguageSupport::providedChecks()
{
    return {};
}

int LanguageSupport::configPages() const
{
    return 2;
}

KDevelop::ConfigPage* LanguageSupport::configPage(int number, QWidget* parent)
{
    if (number == 0) {
        return new PEP8KCModule(this, parent);
    } else if (number == 1) {
        return new DocfilesKCModule(this, parent);
    }
    return nullptr;
}


}

#include "pythonlanguagesupport.moc"
