/*
 * Copyright (C) 2001-2008 Justin Karneges, Martin Hostettler
 *
 * 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/>.
 *
 */

 // Generic tab completion support code.

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

TabCompletion::TabCompletion(QObject *parent)
    : QObject(parent)
    , atStart_(true)
    , typingStatus_(TypingStatus::Normal)
    , suggestedIndex_(0)
    , textEdit_(nullptr)
{
}


void TabCompletion::setTextEdit(QTextEdit* textEdit) {
    textEdit_ = textEdit;

    QColor mleBackground(textEdit_->palette().color(QPalette::Active, QPalette::Base));

    if (mleBackground.value() < 128) {
        highlight_ = mleBackground.lighter(125);
    } else {
        highlight_ = mleBackground.darker(125);
    }
}

QTextEdit* TabCompletion::getTextEdit() {
    return textEdit_;
}


void TabCompletion::highlight(bool set) {
    Q_UNUSED(set);
/*
    if (set) {
        QTextEdit::ExtraSelection es;
        es.cursor = replacementCursor_;
        es.format.setBackground(highlight_);
        textEdit_->setExtraSelections(QList<QTextEdit::ExtraSelection>() << es);
    } else {
        if (textEdit_) textEdit_->setExtraSelections(QList<QTextEdit::ExtraSelection>());
    }*/
}




void TabCompletion::moveCursorToOffset(QTextCursor &cur, int offset, QTextCursor::MoveMode mode) {
    cur.movePosition(QTextCursor::Start, mode);
    for (int i = 0; i < offset; i++) { // some sane limit on iterations
        if (cur.position() >= offset) break; // done our work
        if (!cur.movePosition(QTextCursor::NextCharacter, mode)) break; // failed?
    }
}



/** Find longest common (case insensitive) prefix of \a list.
    */
QString TabCompletion::longestCommonPrefix(QStringList list) {
    QString candidate = list.first().toLower();
    int len = candidate.length();
    while (len > 0) {
        bool found = true;
        foreach(QString str, list) {
            if (str.left(len).toLower() != candidate) {
                found = false;
                break;
            }
        }

        if (found) {
            break;
        }

        --len;
        candidate = candidate.left(len);
    }
    return candidate;
}

void TabCompletion::setup(QString text, int pos, int &start, int &end) {
    if (text.isEmpty() || pos==0) {
        atStart_ = true;
        toComplete_ = "";
        start = 0;
        end = 0;
        return;
    }
    end = pos;
    int i;
    for (i = pos - 1; i > 0; --i) {
        if (text[i].isSpace()) {
            break;
        }
    }
    if (!text[i].isSpace()) {
        atStart_ = true;
        start = 0;
    } else {
        atStart_ = false;
        start = i+1;
    }
    toComplete_ = text.mid(start, end-start);
}




QString TabCompletion::suggestCompletion(bool *replaced) {

    suggestedCompletion_ = possibleCompletions();
    suggestedIndex_ = -1;

    QString newText;
    if (suggestedCompletion_.count() == 1) {
        *replaced = true;
        newText = suggestedCompletion_.first();
    } else if (suggestedCompletion_.count() > 1) {
        newText = longestCommonPrefix(suggestedCompletion_);
        if (newText.isEmpty()) {
            return toComplete_; // FIXME is this right?
        }

        typingStatus_ = TypingStatus::MultipleSuggestions;
        // TODO: display a tooltip that will contain all suggestedCompletion
        *replaced = true;
    }


    return newText;
}



void TabCompletion::reset() {
    typingStatus_ = TypingStatus::Normal;
    highlight(false);
}

/** Handle tab completion.
    * User interface uses a dual model, first tab completes upto the
    * longest common (case insensitiv) match, further tabbing cycles through all
    * possible completions. When doing a tab completion without something to complete
    * possibly offers a special guess first.
    */
void TabCompletion::tryComplete() {
    switch (typingStatus_) {
        case TypingStatus::Normal:
            typingStatus_ = TypingStatus::TabPressed;
            break;
        case TypingStatus::TabPressed:
            typingStatus_ = TypingStatus::TabbingCompletions;
            break;
        default:
            break;
    }


    QString newText;
    bool replaced = false;

    if (typingStatus_ == TypingStatus::MultipleSuggestions) {
        if (!suggestedCompletion_.isEmpty()) {
            suggestedIndex_++;
            if (suggestedIndex_ >= (int)suggestedCompletion_.count()) {
                suggestedIndex_ = 0;
            }
            newText = suggestedCompletion_[suggestedIndex_];
            replaced = true;
        }
    } else {
        QTextCursor cursor = textEdit_->textCursor();
        QString wholeText = textEdit_->toPlainText();

        int begin, end;
        setup(wholeText, cursor.position(), begin, end);
        replacementCursor_ = QTextCursor(textEdit_->document());
        moveCursorToOffset(replacementCursor_, begin);
        moveCursorToOffset(replacementCursor_, end, QTextCursor::KeepAnchor);

        if (toComplete_.isEmpty() && typingStatus_ == TypingStatus::TabbingCompletions) {
            typingStatus_ = TypingStatus::MultipleSuggestions;

            QString guess;
            suggestedCompletion_ = allChoices(guess);

            if ( !guess.isEmpty() ) {
                suggestedIndex_ = -1;
                newText = guess;
                replaced = true;
            } else if (!suggestedCompletion_.isEmpty()) {
                suggestedIndex_ = 0;
                newText = suggestedCompletion_.first();
                replaced = true;
            }
        } else {
            newText = suggestCompletion(&replaced);
        }
    }

    if (replaced) {
        textEdit_->setUpdatesEnabled(false);

        int start = qMin(replacementCursor_.anchor(), replacementCursor_.position());

        replacementCursor_.beginEditBlock();
        replacementCursor_.insertText(newText);
        replacementCursor_.endEditBlock();

        QTextCursor newPos(replacementCursor_);

        moveCursorToOffset(replacementCursor_, start, QTextCursor::KeepAnchor);


        newPos.clearSelection();

        textEdit_->setTextCursor(newPos);

        textEdit_->setUpdatesEnabled(true);
        textEdit_->viewport()->update();
    }
    highlight(typingStatus_ == TypingStatus::MultipleSuggestions);
}

