/************************************************************************
 *
 * Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com)
 *
 * This file is part of SuperCollider Qt GUI.
 *
 * 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 "QcTextEdit.h"
#include "../QcWidgetFactory.h"
#include "../hacks/hacks_qt.hpp"

#include <QFile>
#include <QUrl>
#include <QKeyEvent>
#include <QApplication>
#include <QTextBlock>

class QcTextEditFactory : public QcWidgetFactory<QcTextEdit> {
    void initialize(QWidgetProxy* p, QcTextEdit* w) { p->setMouseEventWidget(w->viewport()); }
};

QC_DECLARE_FACTORY(QcTextEdit, QcTextEditFactory);

QcTextEdit::QcTextEdit(): _interpretSelection(true) {
    setAttribute(Qt::WA_AcceptTouchEvents);

    connect(this, SIGNAL(interpret(QString)), qApp, SLOT(interpret(QString)), Qt::QueuedConnection);
}


QString QcTextEdit::documentFilename() const { return _document; }

void QcTextEdit::setDocument(const QString& filename) {
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    QByteArray data = file.readAll();
    setText(data);
    _document = filename;
}

int QcTextEdit::selectionStart() const { return textCursor().selectionStart(); }

int QcTextEdit::selectionSize() const {
    QTextCursor cursor = textCursor();
    return cursor.selectionEnd() - cursor.selectionStart();
}

void QcTextEdit::select(int start, int size) {
    if (start < 0)
        start = 0;

    QTextCursor cursor(document());

    cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, start);
    cursor.movePosition(size > 0 ? QTextCursor::Right : QTextCursor::Left, QTextCursor::KeepAnchor, qAbs(size));

    setTextCursor(cursor);
}

QString QcTextEdit::selectedString() const {
    QString selection(textCursor().selectedText());
    return prepareText(selection);
}

void QcTextEdit::replaceSelectedText(const QString& string) {
    QTextCursor cursor(textCursor());
    if (cursor.hasSelection()) {
        cursor.removeSelectedText();
    };
    cursor.insertText(string);
}

QString QcTextEdit::currentLine() const { return textCursor().block().text(); }

void QcTextEdit::setTextFont(const QFont& f) {
    QTextCharFormat format;
    format.setFont(f);

    QTextCursor cursor(document());
    cursor.select(QTextCursor::Document);
    cursor.mergeCharFormat(format);

    QTextEdit::setFont(f);
}

void QcTextEdit::setTextColor(const QColor& color) {
    QTextCharFormat format;
    format.setForeground(color);

    QTextCursor cursor(document());
    cursor.select(QTextCursor::Document);
    cursor.mergeCharFormat(format);
}

void QcTextEdit::setRangeColor(const QVariantList& list) {
    if (list.count() < 3)
        return;
    QColor c = list[0].value<QColor>();
    int start = list[1].toInt();
    int size = list[2].toInt();

    QTextCharFormat format;
    format.setForeground(c);

    QTextCursor cursor(document());
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, start);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, size);
    cursor.mergeCharFormat(format);
}

void QcTextEdit::setRangeFont(const QVariantList& list) {
    if (list.count() < 3)
        return;
    QFont f = list[0].value<QFont>();
    int start = list[1].toInt();
    int size = list[2].toInt();

    QTextCharFormat format;
    format.setFont(f);

    QTextCursor cursor(document());
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, start);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, size);
    cursor.mergeCharFormat(format);
}

void QcTextEdit::setRangeText(const QVariantList& list) {
    if (list.count() < 3)
        return;
    QString text = list[0].value<QString>();
    int start = list[1].toInt();
    int size = list[2].toInt();

    QTextCursor cursor(document());
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, start);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, size);
    cursor.insertText(text);
}

void QcTextEdit::keyPressEvent(QKeyEvent* e) {
    if (_interpretSelection && e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)
        && (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)) {
        QTextCursor c(textCursor());
        QString code;

        if (c.hasSelection())
            code = c.selectedText();
        else
            code = c.block().text();

        Q_EMIT(interpret(prepareText(code)));

        return;
    }

    QTextEdit::keyPressEvent(e);
}

void QcTextEdit::insertFromMimeData(const QMimeData* data) {
    if (data->hasUrls()) {
        QTextCursor c(textCursor());
        QList<QUrl> urls = data->urls();
        int n = urls.count();
        for (int i = 0; i < n; ++i) {
            QUrl& url = urls[i];
            QString text = QURL_IS_LOCAL_FILE(url) ? url.toLocalFile() : url.toString();
            c.insertText(text);
            if (n > 1)
                c.insertText("\n");
        }
    } else
        QTextEdit::insertFromMimeData(data);
}

QString& QcTextEdit::prepareText(QString& text) const {
    // NOTE: QTextDocument contains unicode paragraph separators U+2029
    // instead of newline \n characters
    return text.replace(QChar(0x2029), QChar('\n'));
}
