/*  This file is part of the KDE libraries and the Kate part.
 *
 *  Copyright (C) 2008 - 2009 Erlend Hamberg <ehamberg@gmail.com>
 *  Copyright (C) 2009 Paul Gideon Dann <pdgiddie@gmail.com>
 *  Copyright (C) 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com>
 *  Copyright (C) 2012 - 2013 Simon St James <kdedevel@etotheipiplusone.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "kateviinputmodemanager.h"

#include <QKeyEvent>
#include <QString>
#include <QApplication>

#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>

#include "kateconfig.h"
#include "kateglobal.h"
#include "kateviglobal.h"
#include "kateviewinternal.h"
#include "katevinormalmode.h"
#include "kateviinsertmode.h"
#include "katevivisualmode.h"
#include "katevireplacemode.h"
#include "katevikeyparser.h"
#include "katevikeymapper.h"
#include "kateviemulatedcommandbar.h"
#include "katepartdebug.h"
#include "kateviinputmode.h"

using KTextEditor::Cursor;
using KTextEditor::Document;
using KTextEditor::Mark;
using KTextEditor::MarkInterface;
using KTextEditor::MovingCursor;

KateViInputModeManager::KateViInputModeManager(KateViInputMode *inputAdapter, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal)
    : m_inputAdapter(inputAdapter)
{
    m_viNormalMode = new KateViNormalMode(this, view, viewInternal);
    m_viInsertMode = new KateViInsertMode(this, view, viewInternal);
    m_viVisualMode = new KateViVisualMode(this, view, viewInternal);
    m_viReplaceMode = new KateViReplaceMode(this, view, viewInternal);

    m_currentViMode = NormalMode;
    m_previousViMode = NormalMode;

    m_view = view;
    m_viewInternal = viewInternal;

    m_insideHandlingKeyPressCount = 0;

    m_isReplayingLastChange = false;

    m_isRecordingMacro = false;
    m_macrosBeingReplayedCount = 0;
    m_lastPlayedMacroRegister = QChar::Null;

    m_keyMapperStack.push(QSharedPointer<KateViKeyMapper>(new KateViKeyMapper(this, m_view->doc(), m_view)));

    m_lastSearchBackwards = false;
    m_lastSearchCaseSensitive = false;
    m_lastSearchPlacedCursorAtEndOfMatch = false;

    jump_list = new QList<KateViJump>;
    current_jump = jump_list->begin();
    m_temporaryNormalMode = false;

    m_markSetInsideViInputModeManager = false;

    connect(m_view->doc(),
            SIGNAL(markChanged(KTextEditor::Document *, KTextEditor::Mark,
                               KTextEditor::MarkInterface::MarkChangeAction)),
            this,
            SLOT(markChanged(KTextEditor::Document *, KTextEditor::Mark,
                             KTextEditor::MarkInterface::MarkChangeAction)));
    // We have to do this outside of KateViNormalMode, as we don't want
    // KateViVisualMode (which inherits from KateViNormalMode) to respond
    // to changes in the document as well.
    m_viNormalMode->beginMonitoringDocumentChanges();
}

KateViInputModeManager::~KateViInputModeManager()
{
    delete m_viNormalMode;
    delete m_viInsertMode;
    delete m_viVisualMode;
    delete m_viReplaceMode;
    delete jump_list;
}

bool KateViInputModeManager::handleKeypress(const QKeyEvent *e)
{
    m_insideHandlingKeyPressCount++;
    bool res = false;
    bool keyIsPartOfMapping = false;
    const bool isSyntheticSearchCompletedKeyPress = m_inputAdapter->viModeEmulatedCommandBar()->isSendingSyntheticSearchCompletedKeypress();

    // With macros, we want to record the keypresses *before* they are mapped, but if they end up *not* being part
    // of a mapping, we don't want to record them when they are played back by m_keyMapper, hence
    // the "!isPlayingBackRejectedKeys()". And obviously, since we're recording keys before they are mapped, we don't
    // want to also record the executed mapping, as when we replayed the macro, we'd get duplication!
    if (isRecordingMacro() && !isReplayingMacro() && !isSyntheticSearchCompletedKeyPress && !keyMapper()->isExecutingMapping() && !keyMapper()->isPlayingBackRejectedKeys()) {
        QKeyEvent copy(e->type(), e->key(), e->modifiers(), e->text());
        m_currentMacroKeyEventsLog.append(copy);
    }

    if (!isReplayingLastChange() && !isSyntheticSearchCompletedKeyPress) {
        if (e->key() == Qt::Key_AltGr) {
            return true; // do nothing
        }

        // Hand off to the key mapper, and decide if this key is part of a mapping.
        if (e->key() != Qt::Key_Control && e->key() != Qt::Key_Shift && e->key() != Qt::Key_Alt && e->key() != Qt::Key_Meta) {
            const QChar key = KateViKeyParser::self()->KeyEventToQChar(*e);
            if (keyMapper()->handleKeypress(key)) {
                keyIsPartOfMapping = true;
                res = true;
            }
        }
    }

    if (!keyIsPartOfMapping) {
        if (!isReplayingLastChange() && !isSyntheticSearchCompletedKeyPress) {
            // record key press so that it can be repeated via "."
            QKeyEvent copy(e->type(), e->key(), e->modifiers(), e->text());
            appendKeyEventToLog(copy);
        }

        if (m_inputAdapter->viModeEmulatedCommandBar()->isActive()) {
            res = m_inputAdapter->viModeEmulatedCommandBar()->handleKeyPress(e);
        } else {
            res = getCurrentViModeHandler()->handleKeypress(e);
        }
    }

    m_insideHandlingKeyPressCount--;
    Q_ASSERT(m_insideHandlingKeyPressCount >= 0);

    return res;
}

void KateViInputModeManager::feedKeyPresses(const QString &keyPresses) const
{
    int key;
    Qt::KeyboardModifiers mods;
    QString text;

    foreach (const QChar &c, keyPresses) {
        QString decoded = KateViKeyParser::self()->decodeKeySequence(QString(c));
        key = -1;
        mods = Qt::NoModifier;
        text.clear();

        qCDebug(LOG_PART) << "\t" << decoded;

        if (decoded.length() > 1) {  // special key

            // remove the angle brackets
            decoded.remove(0, 1);
            decoded.remove(decoded.indexOf(QLatin1String(">")), 1);
            qCDebug(LOG_PART) << QLatin1String("\t Special key:") << decoded;

            // check if one or more modifier keys where used
            if (decoded.indexOf(QLatin1String("s-")) != -1 || decoded.indexOf(QLatin1String("c-")) != -1
                    || decoded.indexOf(QLatin1String("m-")) != -1 || decoded.indexOf(QLatin1String("a-")) != -1) {

                int s = decoded.indexOf(QLatin1String("s-"));
                if (s != -1) {
                    mods |= Qt::ShiftModifier;
                    decoded.remove(s, 2);
                }

                int c = decoded.indexOf(QLatin1String("c-"));
                if (c != -1) {
                    mods |= Qt::ControlModifier;
                    decoded.remove(c, 2);
                }

                int a = decoded.indexOf(QLatin1String("a-"));
                if (a != -1) {
                    mods |= Qt::AltModifier;
                    decoded.remove(a, 2);
                }

                int m = decoded.indexOf(QLatin1String("m-"));
                if (m != -1) {
                    mods |= Qt::MetaModifier;
                    decoded.remove(m, 2);
                }

                if (decoded.length() > 1) {
                    key = KateViKeyParser::self()->vi2qt(decoded);
                } else if (decoded.length() == 1) {
                    key = int(decoded.at(0).toUpper().toLatin1());
                    text = decoded.at(0);
                    qCDebug(LOG_PART) << "###########" << key;
                } else {
                    qCWarning(LOG_PART) << "decoded is empty. skipping key press.";
                }
            } else { // no modifiers
                key = KateViKeyParser::self()->vi2qt(decoded);
            }
        } else {
            key = decoded.at(0).unicode();
            text = decoded.at(0);
        }

        // We have to be clever about which widget we dispatch to, as we can trigger
        // shortcuts if we're not careful (even if Vim mode is configured to steal shortcuts).
        QKeyEvent k(QEvent::KeyPress, key, mods, text);
        QWidget *destWidget = NULL;
        if (QApplication::activePopupWidget()) {
            // According to the docs, the activePopupWidget, if present, takes all events.
            destWidget = QApplication::activePopupWidget();
        } else if (QApplication::focusWidget()) {
            if (QApplication::focusWidget()->focusProxy()) {
                destWidget = QApplication::focusWidget()->focusProxy();
            } else {
                destWidget = QApplication::focusWidget();
            }
        } else {
            destWidget = m_view->focusProxy();
        }
        QApplication::sendEvent(destWidget, &k);
    }
}

bool KateViInputModeManager::isHandlingKeypress() const
{
    return m_insideHandlingKeyPressCount > 0;
}

void KateViInputModeManager::appendKeyEventToLog(const QKeyEvent &e)
{
    if (e.key() != Qt::Key_Shift && e.key() != Qt::Key_Control
            && e.key() != Qt::Key_Meta && e.key() != Qt::Key_Alt) {
        m_currentChangeKeyEventsLog.append(e);
    }
}

void KateViInputModeManager::storeLastChangeCommand()
{
    m_lastChange.clear();

    QList<QKeyEvent> keyLog = m_currentChangeKeyEventsLog;

    for (int i = 0; i < keyLog.size(); i++) {
        int keyCode = keyLog.at(i).key();
        QString text = keyLog.at(i).text();
        int mods = keyLog.at(i).modifiers();
        QChar key;

        if (text.length() > 0) {
            key = text.at(0);
        }

        if (text.isEmpty() || (text.length() == 1 && text.at(0) < 0x20)
                || (mods != Qt::NoModifier && mods != Qt::ShiftModifier)) {
            QString keyPress;

            keyPress.append(QLatin1Char('<'));
            keyPress.append((mods & Qt::ShiftModifier)   ? QLatin1String("s-") : QString());
            keyPress.append((mods & Qt::ControlModifier) ? QLatin1String("c-") : QString());
            keyPress.append((mods & Qt::AltModifier)     ? QLatin1String("a-") : QString());
            keyPress.append((mods & Qt::MetaModifier)    ? QLatin1String("m-") : QString());
            keyPress.append(keyCode <= 0xFF ? QChar(keyCode) : KateViKeyParser::self()->qt2vi(keyCode));
            keyPress.append(QLatin1Char('>'));

            key = KateViKeyParser::self()->encodeKeySequence(keyPress).at(0);
        }

        m_lastChange.append(key);
    }
    m_lastChangeCompletionsLog = m_currentChangeCompletionsLog;
}

void KateViInputModeManager::repeatLastChange()
{
    m_isReplayingLastChange = true;
    m_nextLoggedLastChangeComplexIndex = 0;
    feedKeyPresses(m_lastChange);
    m_isReplayingLastChange = false;
}

void KateViInputModeManager::findNext()
{
    getCurrentViModeHandler()->findNext();
}

void KateViInputModeManager::findPrevious()
{
    getCurrentViModeHandler()->findPrev();
}

void KateViInputModeManager::startRecordingMacro(QChar macroRegister)
{
    Q_ASSERT(!m_isRecordingMacro);
    qCDebug(LOG_PART) << "Recording macro: " << macroRegister;
    m_isRecordingMacro = true;
    m_recordingMacroRegister = macroRegister;
    m_inputAdapter->viGlobal()->clearMacro(macroRegister);
    m_currentMacroKeyEventsLog.clear();
    m_currentMacroCompletionsLog.clear();
}

void KateViInputModeManager::finishRecordingMacro()
{
    Q_ASSERT(m_isRecordingMacro);
    m_isRecordingMacro = false;
    m_inputAdapter->viGlobal()->storeMacro(m_recordingMacroRegister, m_currentMacroKeyEventsLog, m_currentMacroCompletionsLog);
}

bool KateViInputModeManager::isRecordingMacro()
{
    return m_isRecordingMacro;
}

void KateViInputModeManager::replayMacro(QChar macroRegister)
{
    if (macroRegister == QLatin1Char('@')) {
        macroRegister = m_lastPlayedMacroRegister;
    }
    m_lastPlayedMacroRegister = macroRegister;
    qCDebug(LOG_PART) << "Replaying macro: " << macroRegister;
    const QString macroAsFeedableKeypresses = m_inputAdapter->viGlobal()->getMacro(macroRegister);
    qCDebug(LOG_PART) << "macroAsFeedableKeypresses:  " << macroAsFeedableKeypresses;

    m_macrosBeingReplayedCount++;
    m_nextLoggedMacroCompletionIndex.push(0);
    m_macroCompletionsToReplay.push(m_inputAdapter->viGlobal()->getMacroCompletions(macroRegister));
    m_keyMapperStack.push(QSharedPointer<KateViKeyMapper>(new KateViKeyMapper(this, m_view->doc(), m_view)));
    feedKeyPresses(macroAsFeedableKeypresses);
    m_keyMapperStack.pop();
    m_macroCompletionsToReplay.pop();
    m_nextLoggedMacroCompletionIndex.pop();
    m_macrosBeingReplayedCount--;
    qCDebug(LOG_PART) << "Finished replaying: " << macroRegister;
}

bool KateViInputModeManager::isReplayingMacro()
{
    return m_macrosBeingReplayedCount > 0;
}

void KateViInputModeManager::logCompletionEvent(const KateViInputModeManager::Completion &completion)
{
    // Ctrl-space is a special code that means: if you're replaying a macro, fetch and execute
    // the next logged completion.
    QKeyEvent ctrlSpace(QKeyEvent::KeyPress, Qt::Key_Space, Qt::ControlModifier, QLatin1String(" "));
    if (isRecordingMacro()) {
        m_currentMacroKeyEventsLog.append(ctrlSpace);
        m_currentMacroCompletionsLog.append(completion);
    }
    m_currentChangeKeyEventsLog.append(ctrlSpace);
    m_currentChangeCompletionsLog.append(completion);
}

KateViInputModeManager::Completion KateViInputModeManager::nextLoggedCompletion()
{
    Q_ASSERT(isReplayingLastChange() || isReplayingMacro());
    if (isReplayingLastChange()) {
        if (m_nextLoggedLastChangeComplexIndex >= m_lastChangeCompletionsLog.length()) {
            qCDebug(LOG_PART) << "Something wrong here: requesting more completions for last change than we actually have.  Returning dummy.";
            return Completion(QString(), false, Completion::PlainText);
        }
        return m_lastChangeCompletionsLog[m_nextLoggedLastChangeComplexIndex++];
    } else {
        if (m_nextLoggedMacroCompletionIndex.top() >= m_macroCompletionsToReplay.top().length()) {
            qCDebug(LOG_PART) << "Something wrong here: requesting more completions for macro than we actually have.  Returning dummy.";
            return Completion(QString(), false, Completion::PlainText);
        }
        return m_macroCompletionsToReplay.top()[m_nextLoggedMacroCompletionIndex.top()++];
    }
}

void KateViInputModeManager::doNotLogCurrentKeypress()
{
    if (m_isRecordingMacro) {
        Q_ASSERT(!m_currentMacroKeyEventsLog.isEmpty());
        m_currentMacroKeyEventsLog.pop_back();
    }
    Q_ASSERT(!m_currentChangeKeyEventsLog.isEmpty());
    m_currentChangeKeyEventsLog.pop_back();
}

const QString KateViInputModeManager::getLastSearchPattern() const
{
    return m_lastSearchPattern;
}

void KateViInputModeManager::setLastSearchPattern(const QString &p)
{
    m_lastSearchPattern = p;
}

void KateViInputModeManager::changeViMode(ViMode newMode)
{
    m_previousViMode = m_currentViMode;
    m_currentViMode = newMode;
}

ViMode KateViInputModeManager::getCurrentViMode() const
{
    return m_currentViMode;
}

KTextEditor::View::ViewMode KateViInputModeManager::getCurrentViewMode() const
{
    switch (m_currentViMode) {
        case InsertMode:
            return KTextEditor::View::ViModeInsert;
        case VisualMode:
            return KTextEditor::View::ViModeVisual;
        case VisualLineMode:
            return KTextEditor::View::ViModeVisualLine;
        case VisualBlockMode:
            return KTextEditor::View::ViModeVisualBlock;
        case ReplaceMode:
            return KTextEditor::View::ViModeReplace;
        case NormalMode:
        default:
            return KTextEditor::View::ViModeNormal;
    }
}

ViMode KateViInputModeManager::getPreviousViMode() const
{
    return m_previousViMode;
}

bool KateViInputModeManager::isAnyVisualMode() const
{
    return ((m_currentViMode == VisualMode) || (m_currentViMode == VisualLineMode) || (m_currentViMode == VisualBlockMode));
}

KateViModeBase *KateViInputModeManager::getCurrentViModeHandler() const
{
    switch (m_currentViMode) {
    case NormalMode:
        return m_viNormalMode;
    case InsertMode:
        return m_viInsertMode;
    case VisualMode:
    case VisualLineMode:
    case VisualBlockMode:
        return m_viVisualMode;
    case ReplaceMode:
        return m_viReplaceMode;
    }
    qCDebug(LOG_PART) << "WARNING: Unknown Vi mode.";
    return NULL;
}

void KateViInputModeManager::viEnterNormalMode()
{
    bool moveCursorLeft = (m_currentViMode == InsertMode || m_currentViMode == ReplaceMode)
                          && m_viewInternal->getCursor().column() > 0;

    if (!isReplayingLastChange() && m_currentViMode == InsertMode) {
        // '^ is the insert mark and "^ is the insert register,
        // which holds the last inserted text
        Range r(m_view->cursorPosition(), getMarkPosition(QLatin1Char('^')));

        if (r.isValid()) {
            QString insertedText = m_view->doc()->text(r);
            m_inputAdapter->viGlobal()->fillRegister(QLatin1Char('^'), insertedText);
        }

        addMark(m_view->doc(), QLatin1Char('^'), Cursor(m_view->cursorPosition()), false, false);
    }

    changeViMode(NormalMode);

    if (moveCursorLeft) {
        m_viewInternal->cursorPrevChar();
    }
    m_inputAdapter->setCaretStyle(KateRenderer::Block);
    m_viewInternal->update();
}

void KateViInputModeManager::viEnterInsertMode()
{
    changeViMode(InsertMode);
    addMark(m_view->doc(), QLatin1Char('^'), Cursor(m_view->cursorPosition()), false, false);
    if (getTemporaryNormalMode()) {
        // Ensure the key log contains a request to re-enter Insert mode, else the keystrokes made
        // after returning from temporary normal mode will be treated as commands!
        m_currentChangeKeyEventsLog.append(QKeyEvent(QEvent::KeyPress, Qt::Key_I, Qt::NoModifier, QLatin1String("i")));
    }
    m_inputAdapter->setCaretStyle(KateRenderer::Line);
    setTemporaryNormalMode(false);
    m_viewInternal->update();
}

void KateViInputModeManager::viEnterVisualMode(ViMode mode)
{
    changeViMode(mode);

    // If the selection is inclusive, the caret should be a block.
    // If the selection is exclusive, the caret should be a line.
    m_inputAdapter->setCaretStyle(KateRenderer::Block);
    m_viewInternal->update();
    getViVisualMode()->setVisualModeType(mode);
    getViVisualMode()->init();
}

void KateViInputModeManager::viEnterReplaceMode()
{
    changeViMode(ReplaceMode);
    m_inputAdapter->setCaretStyle(KateRenderer::Underline);
    m_viewInternal->update();
}

KateViNormalMode *KateViInputModeManager::getViNormalMode()
{
    return m_viNormalMode;
}

KateViInsertMode *KateViInputModeManager::getViInsertMode()
{
    return m_viInsertMode;
}

KateViVisualMode *KateViInputModeManager::getViVisualMode()
{
    return m_viVisualMode;
}

KateViReplaceMode *KateViInputModeManager::getViReplaceMode()
{
    return m_viReplaceMode;
}

const QString KateViInputModeManager::getVerbatimKeys() const
{
    QString cmd;

    switch (getCurrentViMode()) {
    case NormalMode:
        cmd = m_viNormalMode->getVerbatimKeys();
        break;
    case InsertMode:
    case ReplaceMode:
        // ...
        break;
    case VisualMode:
    case VisualLineMode:
    case VisualBlockMode:
        cmd = m_viVisualMode->getVerbatimKeys();
        break;
    }

    return cmd;
}

void KateViInputModeManager::readSessionConfig(const KConfigGroup &config)
{

    if (m_inputAdapter->viGlobal()->getRegisters()->size() > 0) {
        QStringList names = config.readEntry("ViRegisterNames", QStringList());
        QStringList contents = config.readEntry("ViRegisterContents", QStringList());
        QList<int> flags = config.readEntry("ViRegisterFlags", QList<int>());

        // sanity check
        if (names.size() == contents.size() && contents.size() == flags.size()) {
            for (int i = 0; i < names.size(); i++) {
                if (!names.at(i).isEmpty()) {
                    m_inputAdapter->viGlobal()->fillRegister(names.at(i).at(0), contents.at(i), (OperationMode)(flags.at(i)));
                }
            }
        }
    }

    // Reading jump list
    // Format: jump1.line, jump1.column, jump2.line, jump2.column, jump3.line, ...
    jump_list->clear();
    QStringList jumps = config.readEntry("JumpList", QStringList());
    for (int i = 0; i + 1 < jumps.size(); i += 2) {
        KateViJump jump = {jumps.at(i).toInt(), jumps.at(i + 1).toInt() };
        jump_list->push_back(jump);
    }
    current_jump = jump_list->end();
    PrintJumpList();

    // Reading marks
    QStringList marks = config.readEntry("ViMarks", QStringList());
    for (int i = 0; i + 2 < marks.size(); i += 3) {
        addMark(m_view->doc(), marks.at(i).at(0), Cursor(marks.at(i + 1).toInt(), marks.at(i + 2).toInt()));
    }
    syncViMarksAndBookmarks();
}

void KateViInputModeManager::writeSessionConfig(KConfigGroup &config)
{
    if (m_inputAdapter->viGlobal()->getRegisters()->size() > 0) {
        const QMap<QChar, KateViRegister> *regs = m_inputAdapter->viGlobal()->getRegisters();
        QStringList names, contents;
        QList<int> flags;
        QMap<QChar, KateViRegister>::const_iterator i;
        for (i = regs->constBegin(); i != regs->constEnd(); ++i) {
            if (i.value().first.length() <= 1000) {
                names << i.key();
                contents << i.value().first;
                flags << int(i.value().second);
            } else {
                qCDebug(LOG_PART) << "Did not save contents of register " << i.key() << ": contents too long ("
                                  << i.value().first.length() << " characters)";
            }
        }

        config.writeEntry("ViRegisterNames", names);
        config.writeEntry("ViRegisterContents", contents);
        config.writeEntry("ViRegisterFlags", flags);
    }

    // Writing Jump List
    // Format: jump1.line, jump1.column, jump2.line, jump2.column, jump3.line, ...
    QStringList l;
    for (int i = 0; i < jump_list->size(); i++) {
        l << QString::number(jump_list->at(i).line) << QString::number(jump_list->at(i).column);
    }
    config.writeEntry("JumpList", l);

    // Writing marks;
    l.clear();
    foreach (QChar key, m_marks.keys()) {
        l << key << QString::number(m_marks.value(key)->line())
          << QString::number(m_marks.value(key)->column());
    }
    config.writeEntry("ViMarks", l);
}

void KateViInputModeManager::reset()
{
    if (m_viVisualMode) {
        m_viVisualMode->reset();
    }
}

void KateViInputModeManager::addJump(Cursor cursor)
{
    for (QList<KateViJump>::iterator iterator = jump_list->begin();
            iterator != jump_list->end();
            iterator ++) {
        if ((*iterator).line == cursor.line()) {
            jump_list->erase(iterator);
            break;
        }
    }

    KateViJump jump = { cursor.line(), cursor.column()};
    jump_list->push_back(jump);
    current_jump = jump_list->end();

    // DEBUG
    PrintJumpList();

}

Cursor KateViInputModeManager::getNextJump(Cursor cursor)
{
    if (current_jump != jump_list->end()) {
        KateViJump jump;
        if (current_jump + 1 != jump_list->end()) {
            jump = *(++current_jump);
        } else {
            jump = *(current_jump);
        }

        cursor = Cursor(jump.line, jump.column);
    }

    // DEBUG
    PrintJumpList();

    return cursor;
}

Cursor KateViInputModeManager::getPrevJump(Cursor cursor)
{
    if (current_jump == jump_list->end()) {
        addJump(cursor);
        current_jump--;
    }

    if (current_jump != jump_list->begin()) {
        KateViJump jump;
        jump = *(--current_jump);
        cursor = Cursor(jump.line, jump.column);
    }

    // DEBUG
    PrintJumpList();

    return cursor;
}

void KateViInputModeManager::PrintJumpList()
{
    qCDebug(LOG_PART) << "Jump List";
    for (QList<KateViJump>::iterator iter = jump_list->begin();
            iter != jump_list->end();
            iter++) {
        if (iter == current_jump) {
            qCDebug(LOG_PART) << (*iter).line << (*iter).column << "<< Current Jump";
        } else {
            qCDebug(LOG_PART) << (*iter).line << (*iter).column;
        }
    }
    if (current_jump == jump_list->end()) {
        qCDebug(LOG_PART) << "    << Current Jump";
    }

}

void KateViInputModeManager::addMark(KTextEditor::DocumentPrivate *doc, const QChar &mark, const Cursor &pos,
                                     const bool moveoninsert, const bool showmark)
{
    m_markSetInsideViInputModeManager = true;
    uint marktype = m_view->doc()->mark(pos.line());

    // delete old cursor if any
    if (MovingCursor *oldCursor = m_marks.value(mark)) {

        int  number_of_marks = 0;

        foreach (QChar c, m_marks.keys()) {
            if (m_marks.value(c)->line() ==  oldCursor->line()) {
                number_of_marks++;
            }
        }

        if (number_of_marks == 1 && pos.line() != oldCursor->line()) {
            m_view->doc()->removeMark(oldCursor->line(), MarkInterface::markType01);
        }

        delete oldCursor;
    }

    MovingCursor::InsertBehavior behavior = moveoninsert ? MovingCursor::MoveOnInsert
                                            : MovingCursor::StayOnInsert;
    // create and remember new one
    m_marks.insert(mark, doc->newMovingCursor(pos, behavior));

    // Showing what mark we set:
    if (showmark && mark != QLatin1Char('>') && mark != QLatin1Char('<') && mark != QLatin1Char('[') && mark != QLatin1Char('.') && mark != QLatin1Char(']')) {
        if (!marktype & MarkInterface::markType01) {
            m_view->doc()->addMark(pos.line(),
                                   MarkInterface::markType01);
        }

        // only show message for active view
        if (m_view->doc()->activeView() == m_view) {
            m_viNormalMode->message(i18n("Mark set: %1", mark));
        }
    }

    m_markSetInsideViInputModeManager = false;
}

Cursor KateViInputModeManager::getMarkPosition(const QChar &mark) const
{
    if (m_marks.contains(mark)) {
        MovingCursor *c = m_marks.value(mark);
        return Cursor(c->line(), c->column());
    } else {
        return Cursor::invalid();
    }
}

void KateViInputModeManager::markChanged(Document *doc,
        Mark mark,
        MarkInterface::MarkChangeAction action)
{

    Q_UNUSED(doc)
    if (mark.type != MarkInterface::Bookmark || m_markSetInsideViInputModeManager) {
        return;
    }
    if (action == MarkInterface::MarkRemoved) {
        foreach (QChar markerChar, m_marks.keys()) {
            if (m_marks.value(markerChar)->line() == mark.line) {
                m_marks.remove(markerChar);
            }
        }
    } else if (action == MarkInterface::MarkAdded) {
        bool freeMarkerCharFound = false;
        for (char markerChar = 'a'; markerChar <= 'z'; markerChar++) {
            if (!m_marks.value(QChar::fromLatin1(markerChar))) {
                addMark(m_view->doc(), QChar::fromLatin1(markerChar), Cursor(mark.line, 0));
                freeMarkerCharFound = true;
                break;
            }
        }
        if (!freeMarkerCharFound) {
            m_viNormalMode->error(i18n("There are no more chars for the next bookmark."));
        }
    }
}

void KateViInputModeManager::syncViMarksAndBookmarks()
{
    const QHash<int, Mark *> &m = m_view->doc()->marks();

    //  Each bookmark should have a vi mark on the same line.
    for (QHash<int, Mark *>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) {
        if (it.value()->type & MarkInterface::markType01) {
            bool thereIsViMarkForThisLine = false;
            foreach (QChar markerChar, m_marks.keys()) {
                if (m_marks.value(markerChar)->line() == it.value()->line) {
                    thereIsViMarkForThisLine = true;
                    break;
                }
            }
            if (!thereIsViMarkForThisLine) {
                for (char markerChar = 'a'; markerChar <= 'z'; markerChar++) {
                    if (! m_marks.value(QChar::fromLatin1(markerChar))) {
                        addMark(m_view->doc(), QChar::fromLatin1(markerChar), Cursor(it.value()->line, 0));
                        break;
                    }
                }
            }
        }
    }

    // For each vi mark line should be bookmarked.
    foreach (QChar markerChar, m_marks.keys()) {
        bool thereIsKateMarkForThisLine = false;
        for (QHash<int, Mark *>::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) {
            if (it.value()->type & MarkInterface::markType01) {
                if (m_marks.value(markerChar)->line() == it.value()->line) {
                    thereIsKateMarkForThisLine = true;
                    break;
                }
                if (!thereIsKateMarkForThisLine) {
                    m_view->doc()->addMark(m_marks.value(markerChar)->line(), MarkInterface::markType01);
                }
            }
        }
    }
}

// Returns a string of marks and columns.
QString KateViInputModeManager::getMarksOnTheLine(int line)
{
    QString res;

    foreach (QChar markerChar, m_marks.keys()) {
        if (m_marks.value(markerChar)->line() == line) {
            res += markerChar + QLatin1String(":") + QString::number(m_marks.value(markerChar)->column()) + QLatin1String(" ");
        }
    }

    return res;
}

QString KateViInputModeManager::modeToString(ViMode mode)
{
    QString modeStr;
    switch (mode) {
    case InsertMode:
        modeStr = i18n("VI: INSERT MODE");
        break;
    case NormalMode:
        modeStr = i18n("VI: NORMAL MODE");
        break;
    case VisualMode:
        modeStr = i18n("VI: VISUAL");
        break;
    case VisualBlockMode:
        modeStr = i18n("VI: VISUAL BLOCK");
        break;
    case VisualLineMode:
        modeStr = i18n("VI: VISUAL LINE");
        break;
    case ReplaceMode:
        modeStr = i18n("VI: REPLACE");
        break;
    }

    return modeStr;
}

KateViKeyMapper *KateViInputModeManager::keyMapper()
{
    return m_keyMapperStack.top().data();
}

KateViInputModeManager::Completion::Completion(const QString &completedText, bool removeTail, CompletionType completionType)
    : m_completedText(completedText),
      m_removeTail(removeTail),
      m_completionType(completionType)
{
    if (m_completionType == FunctionWithArgs || m_completionType == FunctionWithoutArgs) {
        qCDebug(LOG_PART) << "Completing a function while not removing tail currently unsupported; will remove tail instead";
        m_removeTail = true;
    }
}
QString KateViInputModeManager::Completion::completedText() const
{
    return m_completedText;
}
bool KateViInputModeManager::Completion::removeTail() const
{
    return m_removeTail;
}
KateViInputModeManager::Completion::CompletionType KateViInputModeManager::Completion::completionType() const
{
    return m_completionType;
}

void KateViInputModeManager::updateCursor(const Cursor &c)
{
    m_inputAdapter->updateCursor(c);
}

KateViGlobal *KateViInputModeManager::viGlobal() const
{
    return m_inputAdapter->viGlobal();
}

KTextEditor::ViewPrivate *KateViInputModeManager::view() const
{
    return m_view;
}
