#include "texteditor.h"

#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QColorDialog>
#include <QComboBox>
#include <QFileDialog>
#include <QFontDialog>
#include <QMenuBar>
#include <QMessageBox>
#include <QPrintDialog>
#include <QPushButton>
#include <QPrinter>
#include <QStatusBar>
#include <QTextEdit>
#include <QToolBar>

#include "file.h"
#include "mainwindow.h"
#include "settings.h"
#include "shortcuts.h"

extern Main *mainWindow;
extern Settings settings;
extern QString iconTheme;

extern QAction *actionViewToggleNoteEditor;

extern QString vymName;

extern Switchboard switchboard;

extern QPrinter *printer;
extern bool debug;

///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////

TextEditor::TextEditor()    // FIXME-2 feature insert images with drag & drop
			    // https://stackoverflow.com/questions/3254652/several-ways-of-placing-an-image-in-a-qtextedit
{
    statusBar()->hide(); // Hide sizeGrip on default, which comes with statusBar

    editor = new QTextEdit(this);
    editor->setFocus();
    editor->setTabStopDistance(20); // unit is pixel, default would be 80
    editor->setAutoFillBackground(true);
    editor->installEventFilter(this);
    connect(editor, SIGNAL(textChanged()), this, SLOT(editorChanged()));
    setCentralWidget(editor);

    connect(editor, SIGNAL(currentCharFormatChanged(const QTextCharFormat &)), this,
            SLOT(formatChanged(const QTextCharFormat &)));

    // Don't show menubar per default
    menuBar()->hide();

    // Toolbars
    setupFileActions();
    setupEditActions();
    setupFormatActions();
    setupSettingsActions();

    // Various states
    blockChangedSignal = false;
    blockTextUpdate = false;
    setInactive();

    editorName = "Text editor";
    setEditorTitle("");
}

TextEditor::~TextEditor()
{
    // Save Settings
    QString n = QString("/satellite/%1/").arg(shortcutScope);
    settings.setValue(n + "geometry/size", size());
    settings.setValue(n + "geometry/pos", pos());
    settings.setValue(n + "state", saveState(0));

    QString s;
    if (actionSettingsFonthintDefault->isChecked())
        s = "fixed";
    else
        s = "variable";
    settings.setValue(n + "fonts/fonthintDefault", s);
    settings.setValue(n + "fonts/varFont", varFont.toString());
    settings.setValue(n + "fonts/fixedFont", fixedFont.toString());

    settings.setValue(n + "colors/richTextEditorBackground", colorRichTextEditorBackground.name());
    settings.setValue(n + "colors/richTextBackground", colorRichTextBackground.name());
    settings.setValue(n + "colors/richTextForeground", colorRichTextForeground.name());
}

void TextEditor::init(const QString &scope)
{
    shortcutScope = scope;
    QString n = QString("/satellite/%1/").arg(shortcutScope);
    restoreState(settings.value(n + "state", 0).toByteArray());
    filenameHint = "";
    fixedFont.fromString(
        settings.value(n + "fonts/fixedFont", "Courier,12,-1,5,48,0,0,0,1,0")// FIXME-2 Replace all occurences of Courier on Mac
            .toString());
    varFont.fromString(
        settings
            .value(n + "fonts/varFont", "DejaVu Sans Mono,12,-1,0,50,0,0,0,0,0")
            .toString());
    QString s =
        settings.value(n + "fonts/fonthintDefault", "variable").toString();
    if (s == "fixed") {
        actionSettingsFonthintDefault->setChecked(true);
        editor->setCurrentFont(fixedFont);
    }
    else {
        actionSettingsFonthintDefault->setChecked(false);
        editor->setCurrentFont(varFont);
    }

    // Default colors for RichText
    QPixmap pix(16, 16);
    colorRichTextEditorBackground.fromString(
        settings.value(n + "colors/richTextEditorBackground", "#ffffff").toString());
    pix.fill(colorRichTextEditorBackground);
    actionActiveEditorBGColor->setIcon(pix);


    colorRichTextForeground.fromString(
        settings.value(n + "colors/richTextForeground", "#000000").toString());
    pix.fill(colorRichTextForeground);
    actionRichTextFGColor->setIcon(pix);

    colorRichTextBackground.fromString(
        settings.value(n + "colors/richTextBackground", "#000000").toString());
    pix.fill(colorRichTextBackground);
    actionRichTextBGColor->setIcon(pix);

    // Default is PlainText
    actionFormatRichText->setChecked(false);
    clear();
}

bool TextEditor::isEmpty()
{
    if (editor->toPlainText().length() > 0)
        return false;
    else
        return true;
}

void TextEditor::setEditorTitle(const QString &s)
{
    editorTitle = (s.isEmpty()) ? editorName : editorName + ": " + s;

    // Set title of parent dockWidget
    if (parentWidget())
        parentWidget()->setWindowTitle(editorTitle);

    setWindowTitle(editorTitle);
}

QString TextEditor::getEditorTitle() { return editorTitle; }

void TextEditor::setEditorName(const QString &s) { editorName = s; }

void TextEditor::setFont(const QFont &font)
{
    blockChangedSignal = true;

    QTextCursor tc = editor->textCursor();
    QTextCharFormat format = tc.charFormat();

    tc.select(QTextCursor::Document);
    format.setFont(font);
    tc.setCharFormat(format);
    tc.clearSelection();
    fontChanged(fixedFont);

    blockChangedSignal = false;
}

void TextEditor::setFontHint(const QString &fh)
{
    if (fh == "fixed") {
        actionFormatUseFixedFont->setChecked(true);
        editor->setCurrentFont(fixedFont);
        setFont(fixedFont);
    }
    else {
        actionFormatUseFixedFont->setChecked(false);
        editor->setCurrentFont(varFont);
        setFont(varFont);
    }
}

QString TextEditor::getFontHint()
{
    if (actionFormatUseFixedFont->isChecked())
        return "fixed";
    else
        return "var";
}

QString TextEditor::getFontHintDefault()
{
    if (actionSettingsFonthintDefault->isChecked())
        return "fixed";
    else
        return "var";
}

void TextEditor::setFilename(const QString &fn)
{
    if (state == filledEditor) {
        if (fn.isEmpty()) {
            filename = "";
            mainWindow->statusMessage(
                tr("No filename available for this note.", "Statusbar message"));
        }
        else {
            filename = fn;
            mainWindow->statusMessage(
                tr(QString("Current filename is %1").arg(filename).toUtf8(),
                   "Statusbar message"));
        }
    }
}

QString TextEditor::getFilename() { return filename; }

void TextEditor::setFilenameHint(const QString &fnh) { filenameHint = fnh; }

QString TextEditor::getFilenameHint() { return filenameHint; }

QString TextEditor::getText()
{
    if (editor->toPlainText().isEmpty())
        return QString();

    if (actionFormatRichText->isChecked())
        return editor->toHtml();
    else
        return editor->toPlainText();
}

VymText TextEditor::getVymText()
{
    VymText vt;

    if (actionFormatRichText->isChecked()) {
        // Remove some QTextEdit specific tags and markers from RichText
        QString t = editor->toHtml();

        // Remove <!DOCTYPE settings in the beginning
        QRegularExpression re("(<!DOCTYPE.*)<html>");
        re.setPatternOptions(
                QRegularExpression::InvertedGreedinessOption |
                QRegularExpression::DotMatchesEverythingOption |
                QRegularExpression::MultilineOption);
        t.replace(re, "<html>");

        // Remove heading with characters that might cause problems in undo scripts
        re.setPattern("<head>.*head>");
        t.replace(re, "");
        vt.setRichText(t);
    } else
        vt.setPlainText(editor->toPlainText());

    if (actionFormatUseFixedFont->isChecked())
        vt.setFontHint(getFontHint());

    return vt;
}

bool TextEditor::findText(const QString &t,
                          const QTextDocument::FindFlags &flags)
{
    if (editor->find(t, flags))
        return true;
    else
        return false;
}

bool TextEditor::findText(const QString &t,
                          const QTextDocument::FindFlags &flags, int i)
{
    // Position at beginning
    QTextCursor c = editor->textCursor();
    c.setPosition(0, QTextCursor::MoveAnchor);
    editor->setTextCursor(c);

    // Search for t
    int j = 0;
    while (j <= i) {
        if (!editor->find(t, flags))
            return false;
        j++;
    }
    return true;
}

void TextEditor::setTextCursor(const QTextCursor &cursor)
{
    editor->setTextCursor(cursor);
}

QTextCursor TextEditor::getTextCursor() { return editor->textCursor(); }

void TextEditor::setFocus() { editor->setFocus(); }

void TextEditor::setupFileActions()
{
    QToolBar *tb = addToolBar(tr("Note Actions"));
    tb->setObjectName("noteEditorFileActions");
    QMenu *fileMenu = menuBar()->addMenu(tr("&Note", "Menubar"));

    QString tag = tr("Texteditor", "Shortcuts");
    QAction *a;
    a = new QAction(QPixmap(QString(":/document-open-%1").arg(iconTheme)), tr("&Import..."), this);
    a->setShortcut(Qt::CTRL | Qt::Key_O);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textLoad", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textLoad()));
    tb->addAction(a);
    fileMenu->addAction(a);
    actionFileLoad = a;

    fileMenu->addSeparator();
    a = new QAction(QPixmap(QString(":/document-export-%1").arg(iconTheme)), tr("&Export..."), this);
    a->setShortcut(Qt::CTRL | Qt::Key_S);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textSave", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textExport()));
    tb->addAction(a);
    fileMenu->addAction(a);
    addAction(a);
    filledEditorActions << a;
    actionFileExport = a;

    /*
    a = new QAction(tr("Export &As... (HTML)"), this);
    connect(a, SIGNAL(triggered()), this, SLOT(textExportHtml()));  //FIXME-2 slot not available??? Difference to textEport???
    fileMenu->addAction(a);
    filledEditorActions << a;
    actionFileExportHtml = a;
    */

    a = new QAction(tr("Export &As...(ASCII)"), this);
    switchboard.addSwitch("textExportAsASCII", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textExportText()));
    fileMenu->addAction(a);
    addAction(a);
    filledEditorActions << a;
    actionFileExportText = a;

    fileMenu->addSeparator();
    a = new QAction(QPixmap(QString(":/document-print-%1.svg").arg(iconTheme)), tr("&Print..."), this);
    a->setShortcut(Qt::CTRL | Qt::Key_P);
    switchboard.addSwitch("textPrint", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textPrint()));
    tb->addAction(a);
    fileMenu->addAction(a);
    filledEditorActions << a;
    actionFilePrint = a;

    a = new QAction(QPixmap(QString(":/edit-delete-%1.svg").arg(iconTheme)), tr("&Delete All"), this);
    connect(a, SIGNAL(triggered()), this, SLOT(deleteAll()));
    fileMenu->addAction(a);
    tb->addAction(a);
    filledEditorActions << a;
    actionFileDeleteAll = a;
}

void TextEditor::setupEditActions()
{
    QString tag = tr("Texteditor", "Shortcuts");
    QToolBar *editToolBar = addToolBar(tr("Edit Actions"));
    editToolBar->setObjectName("noteEditorEditActions");
    editToolBar->hide();
    QMenu *editMenu = menuBar()->addMenu(tr("Edi&t"));

    QAction *a;
    a = new QAction(QPixmap(":/undo.png"), tr("&Undo"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_Z);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textUndo", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), editor, SLOT(undo()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    // filledEditorActions << a;  // FIXME-2  Use undoAvailable instead?
    actionEditUndo = a;

    a = new QAction(QPixmap(":/redo.png"), tr("&Redo"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_Y);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textRedo", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), editor, SLOT(redo()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    filledEditorActions << a;
    actionEditRedo = a;

    editMenu->addSeparator();
    a = new QAction(QPixmap(), tr("Select and copy &all"), this);
    a->setShortcutContext(Qt::WidgetShortcut);
    a->setShortcut(Qt::CTRL | Qt::Key_A);
    switchboard.addSwitch("textCopyAll", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(editCopyAll()));
    editMenu->addAction(a);
    filledEditorActions << a;
    actionSelectAll = a;

    editMenu->addSeparator();
    a = new QAction(QPixmap(":/editcopy.svg"), tr("&Copy"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_C);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textCopy", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), editor, SLOT(copy()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    filledEditorActions << a;
    actionEditCopy = a;

    a = new QAction(QPixmap(":/editcut.png"), tr("Cu&t"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_X);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textCut", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), editor, SLOT(cut()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    filledEditorActions << a;
    actionEditCut = a;

    a = new QAction(QPixmap(":/editpaste.png"), tr("&Paste"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_V);
    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textPaste", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), editor, SLOT(paste()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    filledEditorActions << a;
    actionEditPaste = a;

    a = new QAction(QPixmap(QString(":/insert-image-%1.svg").arg(iconTheme)), tr("Insert image", "TextEditor") + "...", this);
    editMenu->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(insertImage()));
    editMenu->addAction(a);
    editToolBar->addAction(a);
    filledEditorActions << a;
    actionInsertImage = a;
}

void TextEditor::setupFormatActions()
{
    QString tag = tr("Texteditor", "Shortcuts");
    fontHintsToolBar =
        addToolBar(tr("Font hints", "toolbar in texteditor"));
    fontHintsToolBar->setObjectName("noteEditorFontToolBar");
    QMenu *formatMenu = menuBar()->addMenu(tr("F&ormat"));

    QAction *a;

    a = new QAction(QPixmap(":/formatfixedfont.png"), tr("&Font hint"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_H);
    a->setCheckable(true);
    a->setChecked(
        settings.value("/noteeditor/fonts/useFixedByDefault", false).toBool());
    switchboard.addSwitch("textToggleFonthint", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(toggleFonthint()));
    formatMenu->addAction(a);
    fontHintsToolBar->addAction(a);
    filledEditorActions << a;
    actionFormatUseFixedFont = a;

    // Original icon: ./share/icons/oxygen/22x22/actions/format-text-color.png
    a = new QAction(QPixmap(":/formatrichtext.svg"), tr("&Richtext"), this);
    //  a->setShortcut(Qt::CTRL | Qt::Key_R);
    //  a->setShortcutContext (Qt::WidgetShortcut);
    a->setCheckable(true);
    switchboard.addSwitch("textToggleRichText", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(toggleRichText()));
    formatMenu->addAction(a);
    fontHintsToolBar->addAction(a);
    filledEditorActions << a;
    actionFormatRichText = a;

    fontToolBar = addToolBar(tr("Fonts", "toolbar in texteditor"));
    fontToolBar->setObjectName("noteEditorFontToolBar");

    comboFont = new QComboBox;
    fontToolBar->addWidget(comboFont);
    QFontDatabase fontDB;
    comboFont->insertItems(0, fontDB.families());
    connect(comboFont, SIGNAL(currentTextChanged(const QString &)), this,
            SLOT(textFamily(const QString &)));

    comboSize = new QComboBox;
    fontToolBar->addWidget(comboSize);
    QList<int> sizes = fontDB.standardSizes();
    QList<int>::iterator it = sizes.begin();
    int i = 0;
    while (it != sizes.end()) {
        i++;
        ++it; // increment i before using it
        comboSize->insertItem(i, QString::number(*it));
    }
    connect(comboSize, SIGNAL(currentTextChanged(const QString &)), this,
            SLOT(textSize(const QString &)));

    formatMenu->addSeparator();

    formatToolBar = addToolBar(tr("Format", "toolbar in texteditor"));
    formatToolBar->setObjectName("noteEditorFormatToolBar");

    QPixmap pix(16, 16);
    pix.fill(editor->textColor());
    a = new QAction(pix, tr("&Text Color..."), this);
    formatMenu->addAction(a);
    formatToolBar->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(selectTextFGColor()));
    filledEditorRichTextActions << a;
    actionTextFGColor = a;

    pix.fill(editor->textBackgroundColor());
    a = new QAction(pix, tr("&Text highlight color..."), this);
    formatMenu->addAction(a);
    formatToolBar->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(selectTextBGColor()));
    filledEditorRichTextActions << a;
    actionTextBGColor = a;

    a = new QAction(QPixmap(QString(":/format-text-bold-%1.svg").arg(iconTheme)), tr("&Bold"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_B);
//    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textToggleBold", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textBold()));
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    a->setCheckable(true);
    filledEditorRichTextActions << a;
    actionTextBold = a;

    a = new QAction(QPixmap(QString(":/format-text-italic-%1.svg").arg(iconTheme)), tr("&Italic"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_I);
//    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textToggleItalic", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textItalic()));
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    a->setCheckable(true);
    filledEditorRichTextActions << a;
    actionTextItalic = a;

    a = new QAction(QPixmap(QString(":/text-format-underline-%1.svg").arg(iconTheme)), tr("&Underline"), this);
    a->setShortcut(Qt::CTRL | Qt::Key_U);
//    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    switchboard.addSwitch("textToggleUnderline", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textUnderline()));
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    filledEditorRichTextActions << a;
    a->setCheckable(true);
    // richTextWidgets.append((QWidget*)a);
    actionTextUnderline = a;
    formatMenu->addSeparator();

    QActionGroup *actGrp2 = new QActionGroup(this);
    actGrp2->setExclusive(true);
    a = new QAction(QPixmap(QString(":/text-format-subscript-%1.svg").arg(iconTheme)), tr("Subs&cript"), actGrp2);
    a->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_B);
//    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    switchboard.addSwitch("textToggleSub", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
    filledEditorRichTextActions << a;
    actionAlignSubScript = a;

    a = new QAction(QPixmap(QString(":/text-format-superscript-%1.svg").arg(iconTheme)), tr("Su&perscript"), actGrp2);
    a->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_P);
//    a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    switchboard.addSwitch("textToggleSuper", shortcutScope, a, tag);
    connect(a, SIGNAL(triggered()), this, SLOT(textVAlign()));
    filledEditorRichTextActions << a;
    actionAlignSuperScript = a;
    QActionGroup *grp = new QActionGroup(this);
    connect(grp, SIGNAL(triggered(QAction *)), this,
            SLOT(textAlign(QAction *)));

    formatMenu->addSeparator();

    a = new QAction(QPixmap(QString(":/format-justify-left-%1.svg").arg(iconTheme)), tr("&Left"), grp);
    // a->setShortcut( Qt::CTRL+Qt::Key_L );
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    filledEditorRichTextActions << a;
    actionAlignLeft = a;
    a = new QAction(QPixmap(QString(":/format-justify-center-%1.svg").arg(iconTheme)), tr("C&enter"), grp);
    // a->setShortcut(  Qt::CTRL | Qt::Key_E);
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    filledEditorRichTextActions << a;
    actionAlignCenter = a;
    a = new QAction(QPixmap(QString(":/format-justify-right-%1.svg").arg(iconTheme)), tr("&Right"), grp);
    // a->setShortcut(Qt::CTRL | Qt::Key_R );
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    filledEditorRichTextActions << a;
    actionAlignRight = a;
    a = new QAction(QPixmap(QString(":/format-justify-fill-%1.svg").arg(iconTheme)), tr("&Justify"), grp);
    // a->setShortcut(Qt::CTRL | Qt::Key_J );
    a->setCheckable(true);
    formatToolBar->addAction(a);
    formatMenu->addAction(a);
    filledEditorRichTextActions << a;
    actionAlignJustify = a;
}

void TextEditor::setupSettingsActions()
{
    QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));

    QAction *a;
    a = new QAction(tr("Set &fixed font"), this);
    connect(a, SIGNAL(triggered()), this, SLOT(setFixedFont()));
    settingsMenu->addAction(a);
    actionSettingsFixedFont = a;

    a = new QAction(tr("Set &variable font"), this);
    connect(a, SIGNAL(triggered()), this, SLOT(setVarFont()));
    settingsMenu->addAction(a);
    actionSettingsVarFont = a;

    a = new QAction(tr("&fixed font is default"), this);
    a->setCheckable(true);
    // set state later in constructor...
    settingsMenu->addAction(a);
    actionSettingsFonthintDefault = a;

    settingsMenu->addSeparator();

    a = new QAction(
        tr("Set RichText mode editor background color", "TextEditor") + "...", this);
    settingsMenu->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(selectRichTextEditorBackgroundColor()));
    actionActiveEditorBGColor = a;

    a = new QAction(tr("Set RichText mode default text color", "TextEditor") + "...", this);
    settingsMenu->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(selectRichTextForegroundColor()));
    actionRichTextFGColor = a;

    a = new QAction(tr("Set RichText mode default text background color", "TextEditor") + "...", this);
    settingsMenu->addAction(a);
    connect(a, SIGNAL(triggered()), this, SLOT(selectRichTextBackgroundColor()));
    actionRichTextBGColor = a;
}

void TextEditor::textLoad()
{
    if (state != inactiveEditor) {
        if (!isEmpty()) {
            QMessageBox mb(
                   QMessageBox::Warning,
                   vymName + " - " + tr("Note Editor"),
                   "Loading will overwrite the existing note");
            QPushButton *overwriteButton = mb.addButton(tr("Overwrite"), QMessageBox::AcceptRole);
            mb.addButton(tr("Cancel"), QMessageBox::RejectRole);
            mb.exec();
            if (mb.clickedButton() != overwriteButton) return;
        }
        // Load note
        QFileDialog *fd = new QFileDialog(this);
        QStringList types;
        types << "Text (*.txt *.html)"
              << "VYM notes and HTML (*.html)"
              << "ASCII texts (*.txt)"
              << "All files (*)";
        fd->setNameFilters(types);
        fd->setDirectory(QDir().current());
        fd->show();
        QString fn;
        if (fd->exec() == QDialog::Accepted && !fd->selectedFiles().isEmpty())
            fn = fd->selectedFiles().first();

        if (!fn.isEmpty()) {
            QFile f(fn);
            if (!f.open(QIODevice::ReadOnly))
                return;

            QTextStream ts(&f);
            setTextAuto(ts.readAll());
            editorChanged();
        }
    }
}

void TextEditor::closeEvent(QCloseEvent *ce)
{
    ce->accept(); // TextEditor can be reopened with show()
    hide();
    emit windowClosed();
    return;
}

bool TextEditor::eventFilter(QObject *obj, QEvent *ev)
{
    if (obj == editor) {
        if (ev->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
            if (keyEvent == QKeySequence::Paste) {
                // switch editor mode to match clipboard content before pasting
                const QClipboard *clipboard = QApplication::clipboard();
                const QMimeData *mimeData = clipboard->mimeData();

                if (mimeData->hasHtml() && !actionFormatRichText->isChecked())
                    setRichTextMode(true);
            }
        }
    }
    // pass the event on to the parent class
    return QMainWindow::eventFilter(obj, ev);
}

void TextEditor::editorChanged()
{
    //qDebug() << "TE::editorChanged" << editorName << "blockChanged: " << blockChangedSignal;
    EditorState oldState = state;
    if (isEmpty())
        state = emptyEditor;
    else
        state = filledEditor;

    if (!blockChangedSignal) {
        blockTextUpdate = true;
        emit textHasChanged(getVymText());
        blockTextUpdate = false;
    }

    if (state == oldState)
        return;

    updateState();
}

void TextEditor::setRichText(const QString &t)
{
    blockChangedSignal = true;
    editor->setReadOnly(false);
    editor->setHtml(t);
    actionFormatRichText->setChecked(true);
    actionInsertImage->setEnabled(true);

    // Update state including colors
    updateState();

    updateActions();
    blockChangedSignal = false;
}

void TextEditor::setPlainText(const QString &t)
{
    blockChangedSignal = true;
    editor->setReadOnly(false);

    editor->setPlainText(t);
    actionFormatRichText->setChecked(false);
    actionInsertImage->setEnabled(false);

    // Reset also text format
    QTextCharFormat textformat;
    textformat.setForeground(qApp->palette().color(QPalette::WindowText));
    textformat.setFont(varFont);
    editor->setCurrentCharFormat(textformat);

    // Update state including colors
    updateState();

    updateActions();
    blockChangedSignal = false;
}

void TextEditor::setTextAuto(const QString &t)
{
    if (Qt::mightBeRichText(t))
        setRichText(t);
    else
        setPlainText(t);
}

void TextEditor::setVymText(const VymText &vt)
{
    // While a note is being edited, we are sending textHasChanged
    // Then we don't want to update the text additionally from outside,
    // as this would position cursor at beginning of text
    if (blockTextUpdate) return;

    if (vt.getText() == getText()) return;

    if (vt.isRichText())
        setRichText(vt.getText());
    else
        setPlainText(vt.getText());
}

void TextEditor::setInactive()
{
    setState(inactiveEditor);
}

void TextEditor::editCopyAll()
{
    editor->selectAll();
    editor->copy();
}

void TextEditor::clear()
{
    //qDebug() << "TE::clear" << editorName;
    bool blockChangedOrg = blockChangedSignal;

    blockChangedSignal = true;
    editor->clear();
    setState(emptyEditor);

    blockChangedSignal = blockChangedOrg;
}

void TextEditor::deleteAll()
{
    editor->clear();
}

void TextEditor::textExportAs()
{
    QTextCharFormat f = editor->currentCharFormat();

    QString caption = tr("Export Note to single file");
    QString fn = QFileDialog::getSaveFileName(
        this, caption, QString(), "VYM Note (HTML) (*.html);;All files (*)",
        0, QFileDialog::DontConfirmOverwrite);

    if (!fn.isEmpty()) {
        QFile file(fn);
        if (file.exists()) {
            QMessageBox mb(
                QMessageBox::Warning,
                vymName,
                tr("The file %1\nexists already.\nDo you want to overwrite it?",
                   "dialog 'save note as'").arg(fn));
            QPushButton *overwriteButton = mb.addButton(tr("Overwrite"), QMessageBox::AcceptRole);
            mb.addButton(tr("Cancel"), QMessageBox::RejectRole);
            mb.exec();
            if (mb.clickedButton() != overwriteButton) return;

            // save
            filename = fn;
            textExport();
            return;
        }
        else {
            filename = fn;
            textExport();
            return;
        }
    }
    mainWindow->statusMessage(
        tr("Couldn't export note ", "dialog 'save note as'") + fn);
}

void TextEditor::textExport()
{
    if (filename.isEmpty()) {
        textExportAs();
        return;
    }

    QString text;
    if (actionFormatRichText->isChecked())
        text = editor->toHtml();
    else
        text = editor->toPlainText();

    QFile f(filename);
    if (!f.open(QIODevice::WriteOnly)) {
        mainWindow->statusMessage(QString("Could not write to %1").arg(filename));
        return;
    }

    QTextStream t(&f);
    t << text;
    f.close();

    editor->document()->setModified(false);

    mainWindow->statusMessage(QString("Note exported as %1").arg(filename));
}

void TextEditor::textExportText()
{
    QString fn, s;
    if (!filenameHint.isEmpty()) {
        if (!filenameHint.contains(".txt"))
            s = filenameHint + ".txt";
        else
            s = filenameHint;
    }
    else
        s = QString();
    QString caption = tr("Export Note to single file (ASCII)");
    fn = QFileDialog::getSaveFileName(
        this, caption, s, "VYM Note (ASCII) (*.txt);;All files (*)");

    if (!fn.isEmpty()) {
        QFile file(fn);

        // Already tested in QFileDialog, if we may overwrite in case file exists already

        if (!file.open(QIODevice::WriteOnly))
            mainWindow->statusMessage(
                QString("Could not write to %1").arg(filename));
        else {
            QTextStream t(&file);
            t << getVymText().getTextASCII();
            file.close();

            mainWindow->statusMessage(QString("Note exported as %1").arg(fn));
        }
    }
}

void TextEditor::textPrint()
{
    QTextDocument *document = editor->document();

    if (!printer)
        mainWindow->setupPrinter();

    QPrintDialog dialog(printer, this);
    dialog.setWindowTitle(tr("Print", "TextEditor"));
    if (dialog.exec() != QDialog::Accepted)
        return;

    document->print(printer);
}

void TextEditor::textEditUndo() {}

void TextEditor::toggleFonthint()
{
    if (!actionFormatUseFixedFont->isChecked()) {
        editor->setCurrentFont(varFont);
        setFont(varFont);
    }
    else {
        editor->setCurrentFont(fixedFont);
        setFont(fixedFont);
    }
    emit textHasChanged(getVymText());
}

void TextEditor::setRichTextMode(bool b)
{
    //qDebug() << "TE::setRichTextMode b=" << b;
    actionFormatUseFixedFont->setEnabled(false);
    if (b) {
        setRichText(editor->toHtml());

        // Use default foreground color for all text when switching to RichText
        QTextCursor cursor = editor->textCursor();
        editor->selectAll();
        editor->setTextColor(colorRichTextForeground);
        editor->setTextBackgroundColor(colorRichTextBackground);
        editor->setTextCursor(cursor);
        
    } else {
        setPlainText(editor->toPlainText());
    }
    emit textHasChanged(getVymText());
}

void TextEditor::toggleRichText()
{
    if (actionFormatRichText->isChecked())
        setRichTextMode(true);
    else
        setRichTextMode(false);
}

void TextEditor::setFixedFont()
{
    bool ok;
    QFont font = QFontDialog::getFont(&ok, fixedFont, this);
    if (ok)
        fixedFont = font;
}

void TextEditor::setVarFont()
{
    bool ok;
    QFont font = QFontDialog::getFont(&ok, varFont, this);
    if (ok)
        varFont = font;
}

void TextEditor::textBold()
{
    if (actionTextBold->isChecked())
        editor->setFontWeight(QFont::Bold);
    else
        editor->setFontWeight(QFont::Normal);
}

void TextEditor::textUnderline()
{
    editor->setFontUnderline(actionTextUnderline->isChecked());
}

void TextEditor::textItalic()
{
    editor->setFontItalic(actionTextItalic->isChecked());
}

void TextEditor::textFamily(const QString &f) { editor->setFontFamily(f); }

void TextEditor::textSize(const QString &p) { editor->setFontPointSize(p.toInt()); }

void TextEditor::selectTextFGColor()
{
    QColor col = QColorDialog::getColor(editor->textColor(), this);
    if (!col.isValid())
        return;
    editor->setTextColor(col);
    /*
    QPixmap pix( 16, 16 );
    pix.fill( col );
    actionTextColor->setIcon( pix );
    */
}

void TextEditor::selectTextBGColor()
{
    QColor col = QColorDialog::getColor(editor->textBackgroundColor(), this);
    if (!col.isValid())
        return;
    editor->setTextBackgroundColor(col);
    /*
    QPixmap pix( 16, 16 );
    pix.fill( col );
    actionTextColor->setIcon( pix );
    */
}

void TextEditor::textAlign(QAction *a)
{
    QTextCursor c = editor->textCursor();

    if (a == actionAlignLeft)
        editor->setAlignment(Qt::AlignLeft);
    else if (a == actionAlignCenter)
        editor->setAlignment(Qt::AlignHCenter);
    else if (a == actionAlignRight)
        editor->setAlignment(Qt::AlignRight);
    else if (a == actionAlignJustify)
        editor->setAlignment(Qt::AlignJustify);
}

void TextEditor::textVAlign()
{
    QTextCharFormat format;

    if (sender() == actionAlignSuperScript &&
        actionAlignSuperScript->isChecked()) {
        format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
    }
    else if (sender() == actionAlignSubScript &&
             actionAlignSubScript->isChecked()) {
        format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
    }
    else {
        format.setVerticalAlignment(QTextCharFormat::AlignNormal);
    }
    editor->mergeCurrentCharFormat(format);
}

void TextEditor::fontChanged(const QFont &f)
{
    int i = comboFont->findText(f.family());
    if (i >= 0)
        comboFont->setCurrentIndex(i);
    i = comboSize->findText(QString::number(f.pointSize()));
    if (i >= 0)
        comboSize->setCurrentIndex(i);
    actionTextBold->setChecked(f.bold());
    actionTextItalic->setChecked(f.italic());
    actionTextUnderline->setChecked(f.underline());
}

void TextEditor::colorFGChanged(const QColor &c)
{
    QImage image("icons/color-text.svg");
    QPainter painter;
    painter.begin(&image);
    painter.setBrush(c);
    painter.drawRect(0,110,128,128);
    painter.end();

    actionTextFGColor->setIcon(QPixmap::fromImage(image));
}

void TextEditor::colorBGChanged(const QColor &c)
{
    QImage image("icons/draw-brush.svg");
    QPainter painter;
    painter.begin(&image);
    painter.setBrush(c);
    painter.drawRect(0,110,128,128);
    painter.end();

    actionTextBGColor->setIcon(QPixmap::fromImage(image));
}

void TextEditor::formatChanged(const QTextCharFormat &f)
{
    //qDebug() << "TE::formatChanged  fg=" << f.foreground().color() << " bg=" << f.background().color() << " valid=" << f.isValid();
    if (!actionFormatRichText->isChecked())
        return;
    fontChanged(f.font());
    colorFGChanged(f.foreground().color());
    colorBGChanged(f.background().color());
    alignmentChanged(editor->alignment());
    verticalAlignmentChanged(f.verticalAlignment());
}

void TextEditor::alignmentChanged(int a)
{
    if ((a == Qt::AlignLeft) || (a & Qt::AlignLeft))
        actionAlignLeft->setChecked(true);
    else if ((a & Qt::AlignHCenter))
        actionAlignCenter->setChecked(true);
    else if ((a & Qt::AlignRight))
        actionAlignRight->setChecked(true);
    else if ((a & Qt::AlignJustify))
        actionAlignJustify->setChecked(true);
}

void TextEditor::verticalAlignmentChanged(QTextCharFormat::VerticalAlignment a)
{
    actionAlignSubScript->setChecked(false);
    actionAlignSuperScript->setChecked(false);
    switch (a) {
    case QTextCharFormat::AlignSuperScript:
        actionAlignSuperScript->setChecked(true);
        break;
    case QTextCharFormat::AlignSubScript:
        actionAlignSubScript->setChecked(true);
        break;
    default:;
    }
}

void TextEditor::updateActions()
{
    if (state == inactiveEditor) {
        actionFileLoad->setEnabled(false);
        foreach (QAction* a, filledEditorActions)
            a->setEnabled(false);
        foreach (QAction* a, filledEditorActions)
            a->setEnabled(false);
        foreach (QAction* a, filledEditorRichTextActions)
            a->setEnabled(false);
        return;
    }

    actionFileLoad->setEnabled(true);

    // editorState is filledEditor or emptyEditor
    foreach (QAction* a, emptyEditorActions)
        a->setEnabled(true);

    bool b = (state == filledEditor) ? true : false;
    foreach (QAction* a, filledEditorActions)
        a->setEnabled(b);

    b = (state == filledEditor && actionFormatRichText->isChecked()) ? true : false;
    foreach (QAction* a, filledEditorRichTextActions)
        a->setEnabled(b);
}

void TextEditor::setState(EditorState s)
{
    // qDebug() << "TE::setState" << s << editorName;
    QPalette p = qApp->palette();
    QColor baseColor;
    state = s;
    switch (state) {
        case emptyEditor:
            if (actionFormatRichText->isChecked()) {
                editor->setTextColor(colorRichTextForeground);
                editor->setTextBackgroundColor(colorRichTextBackground);
	    } else
                editor->setTextColor(p.color(QPalette::Text));

        case filledEditor:
            if (actionFormatRichText->isChecked()) {
                if (useColorMapBackground)
                    baseColor = colorMapBackground;
                else
                    baseColor = colorRichTextEditorBackground;
            } else {
                baseColor = p.color(QPalette::Base);
            }
            editor->setReadOnly(false);
            break;
        case inactiveEditor:
            baseColor = Qt::black;
            editor->setReadOnly(true);
    }
    p.setColor(QPalette::Base, baseColor);
    editor->setPalette(p);

    updateActions();
}

void TextEditor::updateState()
{
    //qDebug() << "TE::updateState" << editorName;
    if (isEmpty())
        setState(emptyEditor);
    else
        setState(filledEditor);
}

void TextEditor::selectRichTextEditorBackgroundColor()
{
    QColor col = QColorDialog::getColor(colorRichTextEditorBackground, nullptr);
    if (!col.isValid())
        return;
    colorRichTextEditorBackground = col;
    QPixmap pix(16, 16);
    pix.fill(colorRichTextEditorBackground);
    actionActiveEditorBGColor->setIcon(pix);
}

void TextEditor::selectRichTextForegroundColor()
{
    QColor col = QColorDialog::getColor(colorRichTextForeground, nullptr);
    setRichTextForegroundColor(col);
}

void TextEditor::selectRichTextBackgroundColor()
{
    QColor col = QColorDialog::getColor(colorRichTextBackground, nullptr);
    setRichTextBackgroundColor(col);
}

void TextEditor::insertImage()
{
    QStringList imagePaths = openImageDialog(tr("Load image", "TextEditor"));

    foreach (QString path, imagePaths) {
	QUrl Uri ( QString ( "file://%1" ).arg (path));
	QImage image = QImageReader (path).read();

	QBuffer buffer;
	buffer.open(QIODevice::WriteOnly);
	image.save(&buffer, "PNG");
	QString encodedImage = buffer.data().toBase64();

	QTextDocument * textDocument = editor->document();
	textDocument->addResource( QTextDocument::ImageResource, Uri, QVariant (image));
	QTextCursor cursor = editor->textCursor();
	QTextImageFormat imageFormat;
	imageFormat.setWidth(image.width());
	imageFormat.setHeight(image.height());
	imageFormat.setName(Uri.toString());
	//cursor.insertImage(imageFormat);
	cursor.insertHtml("<img src=\"data:image;base64," + encodedImage + "\"/>");
    }
}

void TextEditor::setRichTextForegroundColor(const QColor &col)
{
    if (!col.isValid()) return;

    colorRichTextForeground = col;
    QPixmap pix(16, 16);
    pix.fill(colorRichTextForeground);
    actionRichTextFGColor->setIcon(pix);
}

void TextEditor::setRichTextBackgroundColor(const QColor &col)
{
    if (!col.isValid()) return;

    colorRichTextBackground = col;
    QPixmap pix(16, 16);
    pix.fill(colorRichTextBackground);
    actionRichTextBGColor->setIcon(pix);
}

void TextEditor::setMapBackgroundColor(const QColor &col)
{
    colorMapBackground = col;
}

void TextEditor::setUseMapBackgroundColor(bool b)
{
    useColorMapBackground = b;
}
